mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-08 13:55:06 -05:00
Compare commits
4 Commits
fix/execut
...
lluisagust
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
881e7cacef | ||
|
|
8bab923477 | ||
|
|
2d0c2166c8 | ||
|
|
9be32e15b1 |
@@ -21,6 +21,8 @@
|
||||
"varsIgnorePattern": "^_",
|
||||
"caughtErrorsIgnorePattern": "^_"
|
||||
}
|
||||
]
|
||||
],
|
||||
// Prevent console statements in production code
|
||||
"no-console": "error"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ const nextConfig = {
|
||||
"ddz4ak4pa3d19.cloudfront.net",
|
||||
"upload.wikimedia.org",
|
||||
"storage.googleapis.com",
|
||||
|
||||
"ideogram.ai", // for generated images
|
||||
"picsum.photos", // for placeholder images
|
||||
],
|
||||
|
||||
@@ -99,8 +99,7 @@ export default function Page() {
|
||||
agentRuns: (state?.agentRuns || 0) + 1,
|
||||
});
|
||||
router.push("/onboarding/6-congrats");
|
||||
} catch (error) {
|
||||
console.error("Error running agent:", error);
|
||||
} catch {
|
||||
toast({
|
||||
title: "Error running agent",
|
||||
description:
|
||||
|
||||
@@ -14,12 +14,7 @@ export async function addDollars(formData: FormData) {
|
||||
comments: formData.get("comments") as string,
|
||||
};
|
||||
const api = new BackendApi();
|
||||
const resp = await api.addUserCredits(
|
||||
data.user_id,
|
||||
data.amount,
|
||||
data.comments,
|
||||
);
|
||||
console.log(resp);
|
||||
await api.addUserCredits(data.user_id, data.amount, data.comments);
|
||||
revalidatePath("/admin/spending");
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ export async function GET(request: Request) {
|
||||
revalidatePath("/", "layout");
|
||||
}
|
||||
} catch (createUserError) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error creating user:", createUserError);
|
||||
// Continue with redirect even if createUser fails
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ export async function GET(request: Request) {
|
||||
const code = searchParams.get("code");
|
||||
const state = searchParams.get("state");
|
||||
|
||||
console.debug("OAuth callback received:", { code, state });
|
||||
|
||||
const message: OAuthPopupResultMessage =
|
||||
code && state
|
||||
? { message_type: "oauth_popup_result", success: true, code, state }
|
||||
@@ -20,8 +18,6 @@ export async function GET(request: Request) {
|
||||
message: `Incomplete query: ${searchParams.toString()}`,
|
||||
};
|
||||
|
||||
console.debug("Sending message to opener:", message);
|
||||
|
||||
// Return a response with the message as JSON and a script to close the window
|
||||
return new NextResponse(
|
||||
`
|
||||
|
||||
@@ -25,7 +25,6 @@ export async function askOtto(
|
||||
const response = await api.askOtto(ottoQuery);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error in askOtto server action:", error);
|
||||
return {
|
||||
answer: error instanceof Error ? error.message : "Unknown error occurred",
|
||||
documents: [],
|
||||
|
||||
@@ -410,7 +410,6 @@ export default function AgentRunsPage(): React.ReactElement {
|
||||
router.push(`/library/agents/${newAgent.id}`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error copying agent:", error);
|
||||
toast({
|
||||
title: "Error copying agent",
|
||||
description: `An error occurred while copying the agent: ${error.message}`,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { useState } from "react";
|
||||
import { Graph } from "@/app/api/__generated__/models/graph";
|
||||
import { sanitizeImportedGraph } from "@/lib/autogpt-server-api";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export const useLibraryUploadAgentDialog = () => {
|
||||
const [isDroped, setisDroped] = useState(false);
|
||||
@@ -95,7 +96,7 @@ export const useLibraryUploadAgentDialog = () => {
|
||||
form.setValue("agentDescription", agent.description);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading agent file:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
|
||||
@@ -80,7 +80,6 @@ export async function providerLogin(provider: LoginProvider) {
|
||||
return "not_allowed";
|
||||
}
|
||||
|
||||
console.error("Error logging in", error);
|
||||
return error.message;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,7 @@ export default async function MarketplaceAgentPage({
|
||||
const libraryAgent = user
|
||||
? await api
|
||||
.getLibraryAgentByStoreListingVersionID(agent.active_version_id || "")
|
||||
.catch((error) => {
|
||||
console.error("Failed to fetch library agent:", error);
|
||||
.catch(() => {
|
||||
return null;
|
||||
})
|
||||
: null;
|
||||
|
||||
@@ -8,7 +8,6 @@ export const useSearchbar = () => {
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
console.log(searchQuery);
|
||||
|
||||
if (searchQuery.trim()) {
|
||||
const encodedTerm = encodeURIComponent(searchQuery);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { SearchFilterChips } from "@/components/agptui/SearchFilterChips";
|
||||
import { SortDropdown } from "@/components/agptui/SortDropdown";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
import { Creator, StoreAgent } from "@/lib/autogpt-server-api";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
type MarketplaceSearchPageSearchParams = { searchTerm?: string; sort?: string };
|
||||
|
||||
@@ -57,7 +58,7 @@ function SearchResults({
|
||||
setAgents(agentsRes.agents || []);
|
||||
setCreators(creatorsRes.creators || []);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
Sentry.captureException(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Metadata } from "next/types";
|
||||
import { redirect } from "next/navigation";
|
||||
import BackendAPI from "@/lib/autogpt-server-api";
|
||||
import { ProfileInfoForm } from "@/components/agptui/ProfileInfoForm";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
// Force dynamic rendering to avoid static generation issues with cookies
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -12,7 +13,7 @@ export const metadata: Metadata = { title: "Profile - AutoGPT Platform" };
|
||||
export default async function UserProfilePage(): Promise<React.ReactElement> {
|
||||
const api = new BackendAPI();
|
||||
const profile = await api.getStoreProfile().catch((error) => {
|
||||
console.error("Error fetching profile:", error);
|
||||
Sentry.captureException(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function sendResetEmail(email: string, turnstileToken: string) {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error("Error sending reset email", error);
|
||||
Sentry.captureException(error);
|
||||
return error.message;
|
||||
}
|
||||
},
|
||||
@@ -61,7 +61,7 @@ export async function changePassword(password: string, turnstileToken: string) {
|
||||
const { error } = await supabase.auth.updateUser({ password });
|
||||
|
||||
if (error) {
|
||||
console.error("Error changing password", error);
|
||||
Sentry.captureException(error);
|
||||
return error.message;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function signup(
|
||||
const { data, error } = await supabase.auth.signUp(values);
|
||||
|
||||
if (error) {
|
||||
console.error("Error signing up", error);
|
||||
Sentry.captureException(error);
|
||||
// FIXME: supabase doesn't return the correct error message for this case
|
||||
if (error.message.includes("P0001")) {
|
||||
return "not_allowed";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { exchangePasswordResetCode } from "@/lib/supabase/helpers";
|
||||
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
@@ -33,7 +34,7 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
return NextResponse.redirect(`${origin}/reset-password`);
|
||||
} catch (error) {
|
||||
console.error("Password reset callback error:", error);
|
||||
Sentry.captureException(error);
|
||||
return NextResponse.redirect(
|
||||
`${origin}/reset-password?error=Password reset failed`,
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import {
|
||||
createRequestHeaders,
|
||||
getServerAuthToken,
|
||||
@@ -72,7 +73,7 @@ export const customMutator = async <T = any>(
|
||||
const authHeaders = createRequestHeaders(token, !!data, contentType);
|
||||
headers = { ...headers, ...authHeaders };
|
||||
} catch (error) {
|
||||
console.warn("Failed to get server auth token:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +93,6 @@ export const customMutator = async <T = any>(
|
||||
// 4. If the request succeeds on the server side, the data will be cached, and the client will use it instead of sending a request to the proxy.
|
||||
|
||||
if (!response.ok && isServerSide()) {
|
||||
console.error("Request failed on server side", response, fullUrl);
|
||||
throw new Error(`Request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "@/lib/autogpt-server-api/helpers";
|
||||
import { getAgptServerBaseUrl } from "@/lib/env-config";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
function buildBackendUrl(path: string[], queryString: string): string {
|
||||
const backendPath = path.join("/");
|
||||
@@ -22,7 +23,7 @@ async function handleJsonRequest(
|
||||
payload = await req.json();
|
||||
} catch (error) {
|
||||
// Handle cases where request body is empty, invalid JSON, or already consumed
|
||||
console.warn("Failed to parse JSON from request body:", error);
|
||||
Sentry.captureException(error);
|
||||
payload = null;
|
||||
}
|
||||
|
||||
@@ -99,7 +100,7 @@ function createResponse(
|
||||
}
|
||||
|
||||
function createErrorResponse(error: unknown): NextResponse {
|
||||
console.error("API proxy error:", error);
|
||||
Sentry.captureException(error);
|
||||
|
||||
// If it's our custom ApiError, preserve the original status and response
|
||||
if (error instanceof ApiError) {
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { IconCircleAlert } from "@/components/ui/icons";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-background">
|
||||
<div className="w-full max-w-md px-4 text-center sm:px-6">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
@@ -467,7 +468,6 @@ export const CustomNode = React.memo(
|
||||
|
||||
const handleInputClick = useCallback(
|
||||
(key: string) => {
|
||||
console.debug(`Opening modal for key: ${key}`);
|
||||
setActiveKey(key);
|
||||
const value = getValue(key, data.hardcodedValues);
|
||||
setInputModalValue(
|
||||
@@ -506,8 +506,6 @@ export const CustomNode = React.memo(
|
||||
};
|
||||
|
||||
const deleteNode = useCallback(() => {
|
||||
console.debug("Deleting node:", id);
|
||||
|
||||
// Remove the node
|
||||
deleteElements({ nodes: [{ id }] });
|
||||
}, [id, deleteElements]);
|
||||
@@ -517,7 +515,9 @@ export const CustomNode = React.memo(
|
||||
const currentNode = getNode(id);
|
||||
|
||||
if (!currentNode) {
|
||||
console.error("Cannot copy node: current node not found");
|
||||
Sentry.captureException(
|
||||
new Error("Cannot copy node: current node not found"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -564,19 +564,19 @@ export const CustomNode = React.memo(
|
||||
hasNonNullNonObjectValue(value),
|
||||
),
|
||||
);
|
||||
console.error(
|
||||
"Block configuration errors for",
|
||||
data.title,
|
||||
":",
|
||||
filteredErrors,
|
||||
Sentry.captureException(
|
||||
new Error(
|
||||
`Block configuration errors for ${data.title}: ${JSON.stringify(
|
||||
filteredErrors,
|
||||
)}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (hasOutputError) {
|
||||
console.error(
|
||||
"Block output contains error for",
|
||||
data.title,
|
||||
":",
|
||||
outputData.error,
|
||||
Sentry.captureException(
|
||||
new Error(
|
||||
`Block output contains error for ${data.title}: ${outputData.error}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}, [hasConfigErrors, hasOutputError, data.errors, outputData, data.title]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import React, {
|
||||
createContext,
|
||||
useState,
|
||||
@@ -135,10 +136,7 @@ const FlowEditor: React.FC<{
|
||||
.getLibraryAgentByGraphID(flowID, flowVersion)
|
||||
.then((libraryAgent) => setLibraryAgent(libraryAgent))
|
||||
.catch((error) => {
|
||||
console.warn(
|
||||
`Failed to fetch LibraryAgent for graph #${flowID} v${flowVersion}`,
|
||||
error,
|
||||
);
|
||||
Sentry.captureException(error);
|
||||
});
|
||||
}, [api, flowID, flowVersion]);
|
||||
|
||||
@@ -318,7 +316,6 @@ const FlowEditor: React.FC<{
|
||||
);
|
||||
|
||||
if (existingConnection) {
|
||||
console.warn("This exact connection already exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -419,6 +416,7 @@ const FlowEditor: React.FC<{
|
||||
|
||||
if (replaceEdges.length > 0) {
|
||||
// Reset node connections for all edges
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
"useReactFlow().setRootEdges was used to overwrite all edges. " +
|
||||
"Use addEdges, deleteElements, or reconnectEdge for incremental changes.",
|
||||
@@ -485,7 +483,6 @@ const FlowEditor: React.FC<{
|
||||
(blockId: string, nodeType: string, hardcodedValues: any = {}) => {
|
||||
const nodeSchema = availableBlocks.find((node) => node.id === blockId);
|
||||
if (!nodeSchema) {
|
||||
console.error(`Schema not found for block ID: ${blockId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,8 +112,7 @@ export default function OttoChatWidget({
|
||||
{ type: "assistant", content: data.answer },
|
||||
]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Unexpected error in chat widget:", error);
|
||||
} catch {
|
||||
setMessages((prev) => [
|
||||
...prev.slice(0, -1),
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { Button } from "./ui/button";
|
||||
import { QuestionMarkCircledIcon } from "@radix-ui/react-icons";
|
||||
import { useRouter, usePathname } from "next/navigation";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
const TallyPopupSimple = () => {
|
||||
const [isFormVisible, setIsFormVisible] = useState(false);
|
||||
@@ -34,7 +35,7 @@ const TallyPopupSimple = () => {
|
||||
setIsFormVisible(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing Tally message:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
approveAgent,
|
||||
rejectAgent,
|
||||
} from "@/app/(platform)/admin/marketplace/actions";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export function ApproveRejectButtons({
|
||||
version,
|
||||
@@ -35,7 +36,7 @@ export function ApproveRejectButtons({
|
||||
await approveAgent(formData);
|
||||
router.refresh(); // Refresh the current route
|
||||
} catch (error) {
|
||||
console.error("Error approving agent:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,7 +46,7 @@ export function ApproveRejectButtons({
|
||||
await rejectAgent(formData);
|
||||
router.refresh(); // Refresh the current route
|
||||
} catch (error) {
|
||||
console.error("Error rejecting agent:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { downloadAsAdmin } from "@/app/(platform)/admin/marketplace/actions";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export function DownloadAgentAdminButton({
|
||||
storeListingVersionId,
|
||||
@@ -38,7 +39,7 @@ export function DownloadAgentAdminButton({
|
||||
// Revoke the temporary URL
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error("Download failed:", error);
|
||||
Sentry.captureException(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { addDollars } from "@/app/(platform)/admin/spending/actions";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export function AdminAddMoneyButton({
|
||||
userId,
|
||||
@@ -41,7 +42,7 @@ export function AdminAddMoneyButton({
|
||||
await addDollars(formData);
|
||||
router.refresh(); // Refresh the current route
|
||||
} catch (error) {
|
||||
console.error("Error adding dollars:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { z } from "zod";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -120,7 +121,7 @@ export const AgentImportForm: React.FC<
|
||||
form.setValue("agentName", graph.name);
|
||||
form.setValue("agentDescription", graph.description);
|
||||
} catch (error) {
|
||||
console.error("Error loading agent file:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
};
|
||||
// Load file
|
||||
|
||||
@@ -70,8 +70,7 @@ export const AgentInfo: FC<AgentInfoProps> = ({
|
||||
description: "Redirecting to your library...",
|
||||
duration: 2000,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to add agent to library:", error);
|
||||
} catch {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to add agent to library. Please try again.",
|
||||
@@ -111,8 +110,7 @@ export const AgentInfo: FC<AgentInfoProps> = ({
|
||||
title: "Download Complete",
|
||||
description: "Your agent has been successfully downloaded.",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error downloading agent:`, error);
|
||||
} catch {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to download agent. Please try again.",
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
|
||||
import { Button } from "./Button";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export function ProfileInfoForm({ profile }: { profile: ProfileDetails }) {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
@@ -32,7 +33,7 @@ export function ProfileInfoForm({ profile }: { profile: ProfileDetails }) {
|
||||
setProfileData(returnedProfile);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating profile:", error);
|
||||
Sentry.captureException(error);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -50,7 +51,7 @@ export function ProfileInfoForm({ profile }: { profile: ProfileDetails }) {
|
||||
const returnedProfile = await api.updateStoreProfile(updatedProfile);
|
||||
setProfileData(returnedProfile);
|
||||
} catch (error) {
|
||||
console.error("Error uploading image:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ export const RatingCard: React.FC<RatingCardProps> = ({
|
||||
|
||||
const handleSubmit = async (rating: number) => {
|
||||
if (rating > 0) {
|
||||
console.log(`Rating submitted for ${agentName}:`, rating);
|
||||
await api.reviewAgent("--", agentName, {
|
||||
store_listing_version_id: storeListingVersionId,
|
||||
score: rating,
|
||||
|
||||
@@ -31,8 +31,6 @@ export const SearchBar: React.FC<SearchBarProps> = ({
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
console.log(searchQuery);
|
||||
|
||||
if (searchQuery.trim()) {
|
||||
// Encode the search term and navigate to the desired path
|
||||
const encodedTerm = encodeURIComponent(searchQuery);
|
||||
|
||||
@@ -32,7 +32,6 @@ export const SearchFilterChips: React.FC<SearchFilterChipsProps> = ({
|
||||
const handleFilterClick = (value: string) => {
|
||||
setSelected(value);
|
||||
onFilterChange?.(value);
|
||||
console.log(`Filter selected: ${value}`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -28,7 +28,6 @@ export const SortDropdown: React.FC<{
|
||||
const handleSelect = (option: SortOption) => {
|
||||
setSelected(option);
|
||||
onSort(option.value);
|
||||
console.log(`Sorting by: ${option.label} (${option.value})`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -56,8 +56,6 @@ export default function WalletRefill() {
|
||||
resolver: zodResolver(autoRefillSchema),
|
||||
});
|
||||
|
||||
console.log("autoRefillForm");
|
||||
|
||||
// Pre-fill the auto-refill form with existing values
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
||||
@@ -55,7 +55,6 @@ export function GoogleAnalytics(props: GAParams) {
|
||||
|
||||
export function sendGAEvent(...args: any[]) {
|
||||
if (currDataLayerName === undefined) {
|
||||
console.warn(`Custom GA: GA has not been initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,6 +62,6 @@ export function sendGAEvent(...args: any[]) {
|
||||
if (dataLayer) {
|
||||
dataLayer.push(...args);
|
||||
} else {
|
||||
console.warn(`Custom GA: dataLayer ${currDataLayerName} does not exist`);
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { isServerSide } from "@/lib/utils/is-server-side";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export interface TurnstileProps {
|
||||
siteKey: string;
|
||||
@@ -74,7 +75,7 @@ export function Turnstile({
|
||||
try {
|
||||
window.turnstile.reset(widgetIdRef.current);
|
||||
} catch (err) {
|
||||
console.warn("Failed to reset existing Turnstile widget:", err);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +104,7 @@ export function Turnstile({
|
||||
try {
|
||||
window.turnstile.remove(widgetIdRef.current);
|
||||
} catch (err) {
|
||||
console.warn("Failed to remove Turnstile widget:", err);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
setWidgetId?.(null);
|
||||
widgetIdRef.current = null;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Button } from "../../../../atoms/Button/Button";
|
||||
import { StepHeader } from "../StepHeader";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useAgentSelectStep } from "./useAgentSelectStep";
|
||||
import Image from "next/image";
|
||||
|
||||
interface Props {
|
||||
onSelect: (agentId: string, agentVersion: number) => void;
|
||||
@@ -142,7 +143,7 @@ export function AgentSelectStep({
|
||||
aria-pressed={selectedAgentId === agent.id}
|
||||
>
|
||||
<div className="relative h-32 bg-zinc-400 sm:h-40">
|
||||
<img
|
||||
<Image
|
||||
src={agent.imageSrc}
|
||||
alt={agent.name}
|
||||
className="h-full w-full object-cover"
|
||||
|
||||
@@ -203,40 +203,33 @@ export const CredentialsInput: FC<{
|
||||
const controller = new AbortController();
|
||||
setOAuthPopupController(controller);
|
||||
controller.signal.onabort = () => {
|
||||
console.debug("OAuth flow aborted");
|
||||
setOAuth2FlowInProgress(false);
|
||||
popup.close();
|
||||
};
|
||||
|
||||
const handleMessage = async (e: MessageEvent<OAuthPopupResultMessage>) => {
|
||||
console.debug("Message received:", e.data);
|
||||
if (
|
||||
typeof e.data != "object" ||
|
||||
!("message_type" in e.data) ||
|
||||
e.data.message_type !== "oauth_popup_result"
|
||||
) {
|
||||
console.debug("Ignoring irrelevant message");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e.data.success) {
|
||||
console.error("OAuth flow failed:", e.data.message);
|
||||
setOAuthError(`OAuth flow failed: ${e.data.message}`);
|
||||
setOAuth2FlowInProgress(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.data.state !== state_token) {
|
||||
console.error("Invalid state token received");
|
||||
setOAuthError("Invalid state token received");
|
||||
setOAuth2FlowInProgress(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.debug("Processing OAuth callback");
|
||||
const credentials = await oAuthCallback(e.data.code, e.data.state);
|
||||
console.debug("OAuth callback processed successfully");
|
||||
onSelectCredentials({
|
||||
id: credentials.id,
|
||||
type: "oauth2",
|
||||
@@ -244,7 +237,6 @@ export const CredentialsInput: FC<{
|
||||
provider,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error in OAuth callback:", error);
|
||||
setOAuthError(
|
||||
// type of error is unkown so we need to use String(error)
|
||||
`Error in OAuth callback: ${
|
||||
@@ -252,20 +244,17 @@ export const CredentialsInput: FC<{
|
||||
}`,
|
||||
);
|
||||
} finally {
|
||||
console.debug("Finalizing OAuth flow");
|
||||
setOAuth2FlowInProgress(false);
|
||||
controller.abort("success");
|
||||
}
|
||||
};
|
||||
|
||||
console.debug("Adding message event listener");
|
||||
window.addEventListener("message", handleMessage, {
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
console.debug("OAuth flow timed out");
|
||||
controller.abort("timeout");
|
||||
setOAuth2FlowInProgress(false);
|
||||
setOAuthError("OAuth flow timed out");
|
||||
|
||||
@@ -15,8 +15,8 @@ export async function getNavbarAccountData() {
|
||||
}
|
||||
try {
|
||||
await prefetchGetV2GetUserProfileQuery(queryClient);
|
||||
} catch (error) {
|
||||
console.error("Error fetching profile:", error);
|
||||
} catch {
|
||||
// ignore for unauthenticated users
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,8 +4,6 @@ import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
export function useNavbar() {
|
||||
const { isLoggedIn, isUserLoading } = useSupabase();
|
||||
|
||||
console.log("isLoggedIn", isLoggedIn);
|
||||
|
||||
const {
|
||||
data: profileResponse,
|
||||
isLoading: isProfileLoading,
|
||||
|
||||
@@ -156,7 +156,7 @@ function renderControlledDialog() {
|
||||
isOpen,
|
||||
set: setIsOpen,
|
||||
}}
|
||||
onClose={() => console.log("Dialog closed")}
|
||||
onClose={() => window.alert("Dialog closed")}
|
||||
>
|
||||
<Dialog.Content>
|
||||
<div className="flex flex-col gap-4">
|
||||
|
||||
@@ -116,14 +116,11 @@ export const SchedulesTable = ({
|
||||
}
|
||||
setIsLoading(true);
|
||||
const agent = agents.find((a) => a.id == selectedAgent)!;
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
router.push(
|
||||
`/build?flowID=${agent.graph_id}&flowVersion=${agent.graph_version}&open_scheduling=true`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Navigation error:", error);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
router.push(
|
||||
`/build?flowID=${agent.graph_id}&flowVersion=${agent.graph_version}&open_scheduling=true`,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -13,6 +13,7 @@ import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
@@ -104,7 +105,7 @@ export default function OnboardingProvider({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch onboarding data:", error);
|
||||
Sentry.captureException(error);
|
||||
// Don't update state on error to prevent null access issues
|
||||
}
|
||||
};
|
||||
@@ -140,7 +141,7 @@ export default function OnboardingProvider({
|
||||
// Make the API call asynchronously to not block render
|
||||
setTimeout(() => {
|
||||
api.updateUserOnboarding(newState).catch((error) => {
|
||||
console.error("Failed to update user onboarding:", error);
|
||||
Sentry.captureException(error);
|
||||
});
|
||||
}, 0);
|
||||
},
|
||||
|
||||
@@ -553,8 +553,6 @@ export const startTutorial = (
|
||||
for (const step of tour.steps) {
|
||||
step.on("show", () => {
|
||||
"use client";
|
||||
console.debug("sendTutorialStep");
|
||||
|
||||
sendGAEvent("event", "tutorial_step_shown", { value: step.id });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -439,7 +439,6 @@ const FileInput: FC<FileInputProps> = ({ value, onChange, className }) => {
|
||||
// Set the file URI as the value
|
||||
onChange(result.file_uri);
|
||||
} catch (error) {
|
||||
console.error("Upload failed:", error);
|
||||
setUploadError(error instanceof Error ? error.message : "Upload failed");
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
|
||||
@@ -105,11 +105,13 @@ export default function useAgentGraph(
|
||||
api
|
||||
.subscribeToGraphExecution(flowExecutionID)
|
||||
.then(() =>
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(
|
||||
`Subscribed to updates for execution #${flowExecutionID}`,
|
||||
),
|
||||
)
|
||||
.catch((error) =>
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`Failed to subscribe to updates for execution #${flowExecutionID}:`,
|
||||
error,
|
||||
@@ -335,9 +337,6 @@ export default function useAgentGraph(
|
||||
const addExecutionDataToNode = useCallback(
|
||||
(node: CustomNode, executionData: NodeExecutionResult) => {
|
||||
if (!executionData.output_data) {
|
||||
console.warn(
|
||||
`Execution data for node ${executionData.node_id} is empty, skipping update`,
|
||||
);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -392,11 +391,6 @@ export default function useAgentGraph(
|
||||
(node) => node.data.backend_id === executionData.node_id,
|
||||
)?.id;
|
||||
if (!nodeId) {
|
||||
console.error(
|
||||
"Node not found for execution data:",
|
||||
executionData,
|
||||
"This shouldn't happen and means that the frontend and backend are out of sync.",
|
||||
);
|
||||
return nodes;
|
||||
}
|
||||
return nodes.map((node) =>
|
||||
@@ -415,10 +409,7 @@ export default function useAgentGraph(
|
||||
if (savedAgent?.id === flowID && savedAgent.version === flowVersion) return;
|
||||
|
||||
api.getGraph(flowID, flowVersion).then((graph) => {
|
||||
console.debug("Fetching graph", flowID, "version", flowVersion);
|
||||
if (graph.version === savedAgent?.version) return; // in case flowVersion is not set
|
||||
|
||||
console.debug("Loading graph", graph.id, "version", graph.version);
|
||||
_loadGraph(graph);
|
||||
});
|
||||
}, [flowID, flowVersion, availableBlocks, api]);
|
||||
@@ -556,7 +547,6 @@ export default function useAgentGraph(
|
||||
)?.inputSchema;
|
||||
|
||||
if (!blockSchema) {
|
||||
console.error(`Schema not found for block ID: ${node.data.block_id}`);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -623,23 +613,14 @@ export default function useAgentGraph(
|
||||
// Differences in IDs are ignored.
|
||||
let newSavedAgent: Graph;
|
||||
if (savedAgent && graphsEquivalent(savedAgent, payload)) {
|
||||
console.warn("No need to save: Graph is the same as version on server");
|
||||
newSavedAgent = savedAgent;
|
||||
} else {
|
||||
console.debug(
|
||||
"Saving new Graph version; old vs new:",
|
||||
savedAgent,
|
||||
payload,
|
||||
);
|
||||
|
||||
newSavedAgent = savedAgent
|
||||
? await api.updateGraph(savedAgent.id, {
|
||||
...payload,
|
||||
id: savedAgent.id,
|
||||
})
|
||||
: await api.createGraph(payload);
|
||||
|
||||
console.debug("Response from the API:", newSavedAgent);
|
||||
}
|
||||
|
||||
// Update the URL
|
||||
@@ -715,7 +696,6 @@ export default function useAgentGraph(
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
console.error("Error saving agent", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error saving agent",
|
||||
@@ -890,8 +870,7 @@ export default function useAgentGraph(
|
||||
if (searchParams.get("open_scheduling") === "true") {
|
||||
router.push("/monitoring");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error scheduling agent",
|
||||
|
||||
@@ -57,10 +57,6 @@ export default function useCredentials(
|
||||
);
|
||||
}
|
||||
if (!discriminatedProvider) {
|
||||
console.warn(
|
||||
`Missing discriminator value from '${credsInputSchema.discriminator}': ` +
|
||||
"hiding credentials input until it is set.",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
providerName = discriminatedProvider;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useCallback, useEffect } from "react";
|
||||
import { verifyTurnstileToken } from "@/lib/turnstile";
|
||||
import { BehaveAs, getBehaveAs } from "@/lib/utils";
|
||||
import { isServerSide } from "@/lib/utils/is-server-side";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
interface UseTurnstileOptions {
|
||||
action?: string;
|
||||
@@ -84,7 +85,7 @@ export function useTurnstile({
|
||||
try {
|
||||
window.turnstile.reset(widgetId);
|
||||
} catch (err) {
|
||||
console.warn("Failed to reset Turnstile widget:", err);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
}
|
||||
}, [shouldRender, widgetId]);
|
||||
|
||||
@@ -448,8 +448,7 @@ export default class BackendAPI {
|
||||
try {
|
||||
const result = this._get("/store/profile");
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error fetching store profile:", error);
|
||||
} catch {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
@@ -975,10 +974,6 @@ export default class BackendAPI {
|
||||
path: string,
|
||||
payload?: Record<string, any>,
|
||||
) {
|
||||
if (method !== "GET") {
|
||||
console.debug(`${method} ${path} payload:`, payload);
|
||||
}
|
||||
|
||||
if (isClient) {
|
||||
return this._makeClientRequest(method, path, payload);
|
||||
} else {
|
||||
@@ -1124,10 +1119,8 @@ export default class BackendAPI {
|
||||
if (serverToken && !error) {
|
||||
token = serverToken;
|
||||
} else if (error) {
|
||||
console.warn("Failed to get WebSocket token from server:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Failed to get token for WebSocket connection:", error);
|
||||
} catch {
|
||||
// Continue with empty token, connection might still work
|
||||
}
|
||||
|
||||
@@ -1137,7 +1130,6 @@ export default class BackendAPI {
|
||||
|
||||
this.webSocket.onopen = () => {
|
||||
this.webSocket!.state = "connected";
|
||||
console.info("[BackendAPI] WebSocket connected to", this.wsUrl);
|
||||
this._startWSHeartbeat(); // Start heartbeat when connection opens
|
||||
this._clearDisconnectIntent(); // Clear disconnect intent when connected
|
||||
this.wsOnConnectHandlers.forEach((handler) => handler());
|
||||
@@ -1146,11 +1138,13 @@ export default class BackendAPI {
|
||||
|
||||
this.webSocket.onclose = (event) => {
|
||||
if (this.webSocket?.state == "connecting") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`[BackendAPI] WebSocket failed to connect: ${event.reason}`,
|
||||
event,
|
||||
);
|
||||
} else if (this.webSocket?.state == "connected") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`[BackendAPI] WebSocket connection closed: ${event.reason}`,
|
||||
event,
|
||||
@@ -1172,11 +1166,13 @@ export default class BackendAPI {
|
||||
|
||||
this.webSocket.onerror = (error) => {
|
||||
if (this.webSocket?.state == "connected") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("[BackendAPI] WebSocket error:", error);
|
||||
}
|
||||
};
|
||||
this.webSocket.onmessage = (event) => this._handleWSMessage(event);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("[BackendAPI] Error connecting to WebSocket:", error);
|
||||
reject(error);
|
||||
}
|
||||
@@ -1245,6 +1241,7 @@ export default class BackendAPI {
|
||||
);
|
||||
|
||||
this.heartbeatTimeoutID = window.setTimeout(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn("Heartbeat timeout - reconnecting");
|
||||
this.webSocket?.close();
|
||||
this.connectWebSocket();
|
||||
|
||||
@@ -97,8 +97,7 @@ export async function getServerAuthToken(): Promise<string> {
|
||||
}
|
||||
|
||||
return session.access_token;
|
||||
} catch (error) {
|
||||
console.error("Failed to get auth token:", error);
|
||||
} catch {
|
||||
return "no-token-found";
|
||||
}
|
||||
}
|
||||
@@ -251,16 +250,11 @@ export async function makeAuthenticatedRequest(
|
||||
if (isAuthenticationError(response, errorDetail)) {
|
||||
if (isLogoutInProgress()) {
|
||||
// Silently return null during logout to prevent error noise
|
||||
console.debug(
|
||||
"Authentication request failed during logout, ignoring:",
|
||||
errorDetail,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// For authentication errors outside logout, log but don't throw
|
||||
// This prevents crashes when session expires naturally
|
||||
console.warn("Authentication failed:", errorDetail);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -305,13 +299,6 @@ export async function makeAuthenticatedFileUpload(
|
||||
}
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
if (isLogoutInProgress()) {
|
||||
console.debug(
|
||||
"File upload authentication failed during logout, ignoring",
|
||||
);
|
||||
return "";
|
||||
}
|
||||
console.warn("File upload authentication failed:", errorMessage);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
@@ -1053,9 +1053,6 @@ export function determineDataType(schema: BlockIOSubSchema): DataType {
|
||||
// $ref has sibling schema properties (which isn't technically allowed),
|
||||
// so there will only be one item in allOf[].
|
||||
// this should NEVER happen though, as $refs are resolved server-side.
|
||||
console.warn(
|
||||
`Detected 'allOf' wrapper: ${schema}. Normalizing use ${schema.allOf[0]} instead.`,
|
||||
);
|
||||
schema = schema.allOf[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -42,14 +42,12 @@ function MyComponent() {
|
||||
// Regular API calls use secure server-side authentication
|
||||
const handleGetGraphs = async () => {
|
||||
const graphs = await api.listGraphs();
|
||||
console.log(graphs);
|
||||
};
|
||||
|
||||
// File uploads also work with secure authentication
|
||||
const handleFileUpload = async (file: File) => {
|
||||
try {
|
||||
const mediaUrl = await api.uploadStoreSubmissionMedia(file);
|
||||
console.log("Uploaded:", mediaUrl);
|
||||
} catch (error) {
|
||||
console.error("Upload failed:", error);
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ export async function validateSession(
|
||||
user,
|
||||
isValid: true,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Session validation error:", error);
|
||||
} catch {
|
||||
const redirectPath = getRedirectPath(currentPath);
|
||||
return {
|
||||
user: null,
|
||||
@@ -93,7 +92,6 @@ export async function getCurrentUser(): Promise<{
|
||||
|
||||
return { user };
|
||||
} catch (error) {
|
||||
console.error("Get current user error:", error);
|
||||
return {
|
||||
user: null,
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
@@ -135,7 +133,6 @@ export async function getWebSocketToken(): Promise<{
|
||||
|
||||
return { token: session?.access_token || null };
|
||||
} catch (error) {
|
||||
console.error("Get WebSocket token error:", error);
|
||||
return {
|
||||
token: null,
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
@@ -158,7 +155,6 @@ export async function serverLogout(options: ServerLogoutOptions = {}) {
|
||||
|
||||
if (!supabase) {
|
||||
redirect("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -169,10 +165,10 @@ export async function serverLogout(options: ServerLogoutOptions = {}) {
|
||||
revalidatePath("/");
|
||||
|
||||
if (error) {
|
||||
console.error("Error logging out:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Logout error:", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
|
||||
// Clear all cached data and redirect
|
||||
@@ -214,7 +210,6 @@ export async function refreshSession() {
|
||||
|
||||
return { user };
|
||||
} catch (error) {
|
||||
console.error("Refresh session error:", error);
|
||||
return {
|
||||
user: null,
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
import { getSupabaseUrl, getSupabaseAnonKey } from "@/lib/env-config";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import {
|
||||
getCurrentUser,
|
||||
refreshSession,
|
||||
@@ -40,7 +41,7 @@ export function useSupabase() {
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error creating Supabase client", error);
|
||||
Sentry.captureException(error);
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
@@ -53,7 +54,7 @@ export function useSupabase() {
|
||||
try {
|
||||
await serverLogout(options);
|
||||
} catch (error) {
|
||||
console.error("Error logging out:", error);
|
||||
Sentry.captureException(error);
|
||||
} finally {
|
||||
setUser(null);
|
||||
router.refresh();
|
||||
@@ -97,8 +98,7 @@ export function useSupabase() {
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Session validation error:", error);
|
||||
} catch {
|
||||
setUser(null);
|
||||
const redirectPath = getRedirectPath(pathname);
|
||||
if (redirectPath) {
|
||||
@@ -122,8 +122,7 @@ export function useSupabase() {
|
||||
setUser(serverUser);
|
||||
clearWebSocketDisconnectIntent();
|
||||
return serverUser;
|
||||
} catch (error) {
|
||||
console.error("Get user error:", error);
|
||||
} catch {
|
||||
setUser(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createServerClient } from "@supabase/ssr";
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
import { getCookieSettings, isAdminPage, isProtectedPage } from "./helpers";
|
||||
import { getSupabaseUrl, getSupabaseAnonKey } from "../env-config";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
export async function updateSession(request: NextRequest) {
|
||||
let supabaseResponse = NextResponse.next({
|
||||
@@ -81,7 +82,7 @@ export async function updateSession(request: NextRequest) {
|
||||
// If this is not done, you may be causing the browser and server to go out
|
||||
// of sync and terminate the user's session prematurely!
|
||||
} catch (error) {
|
||||
console.error("Failed to run Supabase middleware", error);
|
||||
Sentry.captureException(error);
|
||||
}
|
||||
|
||||
return supabaseResponse;
|
||||
|
||||
@@ -22,7 +22,6 @@ export async function getServerUser() {
|
||||
const role = user.role || null;
|
||||
return { user, role, error: null };
|
||||
} catch (error) {
|
||||
console.error("Unexpected error in getServerUser:", error);
|
||||
return {
|
||||
user: null,
|
||||
role: null,
|
||||
|
||||
@@ -32,14 +32,12 @@ export async function verifyTurnstileToken(
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error("Turnstile verification failed:", await response.text());
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.success === true;
|
||||
} catch (error) {
|
||||
console.error("Error verifying Turnstile token:", error);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ test.describe("Agent Dashboard", () => {
|
||||
const beforeCount = await rows.count();
|
||||
|
||||
if (beforeCount === 0) {
|
||||
console.log("No agents available; skipping delete flow.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,6 @@ test.describe("Build", () => { //(1)!
|
||||
const blocksToAdd = allBlocks.filter((_, index) =>
|
||||
index % totalParts === (part - 1)
|
||||
);
|
||||
|
||||
console.log(`Adding ${blocksToAdd.length} blocks starting with "${letter}" (part ${part}/${totalParts})`);
|
||||
|
||||
for (const block of blocksToAdd) {
|
||||
await buildPage.addBlock(block);
|
||||
|
||||
@@ -2,23 +2,15 @@ import { FullConfig } from "@playwright/test";
|
||||
import { createTestUsers, saveUserPool, loadUserPool } from "./utils/auth";
|
||||
|
||||
async function globalSetup(config: FullConfig) {
|
||||
console.log("🚀 Starting global test setup...");
|
||||
|
||||
try {
|
||||
const existingUserPool = await loadUserPool();
|
||||
|
||||
if (existingUserPool && existingUserPool.users.length > 0) {
|
||||
console.log(
|
||||
`♻️ Found existing user pool with ${existingUserPool.users.length} users`,
|
||||
);
|
||||
console.log("✅ Using existing user pool");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create test users using signup page
|
||||
const numberOfUsers = (config.workers || 1) + 8; // workers + buffer
|
||||
console.log(`👥 Creating ${numberOfUsers} test users via signup...`);
|
||||
console.log("⏳ Note: This may take a few minutes in CI environments");
|
||||
|
||||
const users = await createTestUsers(numberOfUsers);
|
||||
|
||||
@@ -36,15 +28,7 @@ async function globalSetup(config: FullConfig) {
|
||||
|
||||
// Save user pool
|
||||
await saveUserPool(users);
|
||||
|
||||
console.log("✅ Global setup completed successfully!");
|
||||
console.log(`📊 Created ${users.length} test users via signup page`);
|
||||
} catch (error) {
|
||||
console.error("❌ Global setup failed:", error);
|
||||
console.error("💡 This is likely due to:");
|
||||
console.error(" 1. Backend services not fully ready");
|
||||
console.error(" 2. Network timeouts in CI environment");
|
||||
console.error(" 3. Database or authentication issues");
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
|
||||
const marketplaceTitle = await marketplacePage.getMarketplaceTitle(page);
|
||||
await isVisible(marketplaceTitle);
|
||||
|
||||
console.log(
|
||||
"User can access marketplace page when logged out test passed ✅",
|
||||
);
|
||||
});
|
||||
|
||||
test("User can access marketplace page when logged in", async ({ page }) => {
|
||||
@@ -32,10 +28,6 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
|
||||
const marketplaceTitle = await marketplacePage.getMarketplaceTitle(page);
|
||||
await isVisible(marketplaceTitle);
|
||||
|
||||
console.log(
|
||||
"User can access marketplace page when logged in test passed ✅",
|
||||
);
|
||||
});
|
||||
|
||||
test("Featured agents, top agents, and featured creators are visible", async ({
|
||||
@@ -61,10 +53,6 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
await isVisible(featuredCreatorsSection);
|
||||
const creatorProfiles = await marketplacePage.getCreatorProfiles(page);
|
||||
await hasMinCount(creatorProfiles, 1);
|
||||
|
||||
console.log(
|
||||
"Featured agents, top agents, and featured creators are visible test passed ✅",
|
||||
);
|
||||
});
|
||||
|
||||
test("Can navigate and interact with marketplace elements", async ({
|
||||
@@ -92,10 +80,6 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
await firstCreatorProfile.click();
|
||||
await page.waitForURL("**/marketplace/creator/**");
|
||||
await matchesUrl(page, /\/marketplace\/creator\/.+/);
|
||||
|
||||
console.log(
|
||||
"Can navigate and interact with marketplace elements test passed ✅",
|
||||
);
|
||||
});
|
||||
|
||||
test("Complete search flow works correctly", async ({ page }) => {
|
||||
@@ -118,8 +102,6 @@ test.describe("Marketplace – Basic Functionality", () => {
|
||||
|
||||
const results = await marketplacePage.getSearchResultsCount(page);
|
||||
expect(results).toBeGreaterThan(0);
|
||||
|
||||
console.log("Complete search flow works correctly test passed ✅");
|
||||
});
|
||||
|
||||
// We need to add a test search with filters, but the current business logic for filters doesn't work as expected. We'll add it once we modify that.
|
||||
@@ -144,7 +126,5 @@ test.describe("Marketplace – Edge Cases", () => {
|
||||
|
||||
const results = await marketplacePage.getSearchResultsCount(page);
|
||||
expect(results).toBe(0);
|
||||
|
||||
console.log("Search for non-existent item shows no results test passed ✅");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,7 +49,6 @@ test.beforeEach(async ({ page }, testInfo: TestInfo) => {
|
||||
test.afterAll(async () => {
|
||||
// clear out the downloads folder
|
||||
const downloadsFolder = process.cwd() + "/downloads";
|
||||
console.log(`clearing out the downloads folder ${downloadsFolder}/monitor`);
|
||||
|
||||
await fs.rm(`${downloadsFolder}/monitor`, {
|
||||
recursive: true,
|
||||
@@ -88,8 +87,6 @@ test.skip("user can export and import agents", async ({
|
||||
`${monitorPage.downloadsFolder}/monitor/${download.suggestedFilename()}`,
|
||||
);
|
||||
|
||||
console.log(`downloaded file to ${download.suggestedFilename()}`);
|
||||
|
||||
expect(download.suggestedFilename()).toBeDefined();
|
||||
expect(download.suggestedFilename()).toContain("test-agent-");
|
||||
expect(download.suggestedFilename()).toContain("v1.json");
|
||||
@@ -122,8 +119,6 @@ test.skip("user can export and import agents", async ({
|
||||
|
||||
expect(postImportAgents.length).toBeGreaterThan(preImportAgents.length);
|
||||
|
||||
console.log(`postImportAgents: ${JSON.stringify(postImportAgents)}`);
|
||||
|
||||
const importedAgent = postImportAgents.find(
|
||||
(a: any) => a.name === `${baseName}-imported`,
|
||||
);
|
||||
|
||||
@@ -23,14 +23,9 @@ export class BuildPage extends BasePage {
|
||||
}
|
||||
|
||||
async closeTutorial(): Promise<void> {
|
||||
console.log(`closing tutorial`);
|
||||
try {
|
||||
await this.page
|
||||
.getByRole("button", { name: "Skip Tutorial", exact: true })
|
||||
.click();
|
||||
} catch (error) {
|
||||
console.info("Error closing tutorial:", error);
|
||||
}
|
||||
await this.page
|
||||
.getByRole("button", { name: "Skip Tutorial", exact: true })
|
||||
.click();
|
||||
}
|
||||
|
||||
async openBlocksPanel(): Promise<void> {
|
||||
@@ -51,7 +46,6 @@ export class BuildPage extends BasePage {
|
||||
name: string = "Test Agent",
|
||||
description: string = "",
|
||||
): Promise<void> {
|
||||
console.log(`💾 Saving agent '${name}' with description '${description}'`);
|
||||
await this.page.getByTestId("blocks-control-save-button").click();
|
||||
await this.page.getByTestId("save-control-name-input").fill(name);
|
||||
await this.page
|
||||
@@ -65,16 +59,12 @@ export class BuildPage extends BasePage {
|
||||
return Object.values(this.cachedBlocks);
|
||||
}
|
||||
|
||||
console.log(`Getting blocks from API request`);
|
||||
|
||||
// Make direct API request using the page's request context
|
||||
const response = await this.page.request.get(
|
||||
"http://localhost:3000/api/proxy/api/blocks",
|
||||
);
|
||||
const apiBlocks: APIBlock[] = await response.json();
|
||||
|
||||
console.log(`Found ${apiBlocks.length} blocks from API`);
|
||||
|
||||
// Convert API blocks to test Block format
|
||||
const blocks = apiBlocks.map((block) => ({
|
||||
id: block.id,
|
||||
@@ -96,14 +86,11 @@ export class BuildPage extends BasePage {
|
||||
async getFilteredBlocksFromAPI(
|
||||
filterFn: (block: Block) => boolean,
|
||||
): Promise<Block[]> {
|
||||
console.log(`Getting filtered blocks from API`);
|
||||
const blocks = await this.getBlocksFromAPI();
|
||||
return blocks.filter(filterFn);
|
||||
}
|
||||
|
||||
async addBlock(block: Block): Promise<void> {
|
||||
console.log(`Adding block ${block.name} (${block.id}) to agent`);
|
||||
|
||||
await this.openBlocksPanel();
|
||||
|
||||
const searchInput = this.page.locator(
|
||||
@@ -121,9 +108,6 @@ export class BuildPage extends BasePage {
|
||||
const blockInEditor = this.page.getByTestId(block.id).first();
|
||||
expect(blockInEditor).toBeAttached();
|
||||
} else {
|
||||
console.log(
|
||||
`❌ ❌ Block ${block.name} (display: ${displayName}) returned from the API but not found in block list`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,27 +117,22 @@ export class BuildPage extends BasePage {
|
||||
}
|
||||
|
||||
async getBlockInputs(blockId: string): Promise<string[]> {
|
||||
console.log(`Getting block ${blockId} inputs`);
|
||||
try {
|
||||
const node = this.page.locator(`[data-blockid="${blockId}"]`).first();
|
||||
const inputsData = await node.getAttribute("data-inputs");
|
||||
return inputsData ? JSON.parse(inputsData) : [];
|
||||
} catch (error) {
|
||||
console.error("Error getting block inputs:", error);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async selectBlockCategory(category: string): Promise<void> {
|
||||
console.log(`Selecting block category: ${category}`);
|
||||
await this.page.getByText(category, { exact: true }).click();
|
||||
// Wait for the blocks to load after category selection
|
||||
await this.page.waitForTimeout(3000);
|
||||
}
|
||||
|
||||
async getBlocksForCategory(category: string): Promise<Block[]> {
|
||||
console.log(`Getting blocks for category: ${category}`);
|
||||
|
||||
// Clear any existing search to ensure we see all blocks in the category
|
||||
const searchInput = this.page.locator(
|
||||
'[data-id="blocks-control-search-input"]',
|
||||
@@ -171,8 +150,6 @@ export class BuildPage extends BasePage {
|
||||
await blockFinder.first().waitFor();
|
||||
const blocks = await blockFinder.all();
|
||||
|
||||
console.log(`found ${blocks.length} blocks in category ${category}`);
|
||||
|
||||
const results = await Promise.all(
|
||||
blocks.map(async (block) => {
|
||||
try {
|
||||
@@ -194,6 +171,7 @@ export class BuildPage extends BasePage {
|
||||
description: description.trim(),
|
||||
};
|
||||
} catch (elementError) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error processing block:", elementError);
|
||||
return null;
|
||||
}
|
||||
@@ -203,6 +181,7 @@ export class BuildPage extends BasePage {
|
||||
// Filter out any null results from errors
|
||||
return results.filter((block): block is Block => block !== null);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Error getting blocks for category ${category}:`, error);
|
||||
return [];
|
||||
}
|
||||
@@ -216,7 +195,6 @@ export class BuildPage extends BasePage {
|
||||
}
|
||||
|
||||
async getBlockById(blockId: string, dataId?: string): Promise<Locator> {
|
||||
console.log(`getting block ${blockId} with dataId ${dataId}`);
|
||||
return this.page.locator(await this._buildBlockSelector(blockId, dataId));
|
||||
}
|
||||
|
||||
@@ -229,9 +207,6 @@ export class BuildPage extends BasePage {
|
||||
value: string,
|
||||
dataId?: string,
|
||||
): Promise<void> {
|
||||
console.log(
|
||||
`filling block input ${placeholder} with value ${value} of block ${blockId}`,
|
||||
);
|
||||
const block = await this.getBlockById(blockId, dataId);
|
||||
const input = block.getByPlaceholder(placeholder);
|
||||
await input.fill(value);
|
||||
@@ -243,9 +218,6 @@ export class BuildPage extends BasePage {
|
||||
value: string,
|
||||
dataId?: string,
|
||||
): Promise<void> {
|
||||
console.log(
|
||||
`selecting value ${value} for input ${inputName} of block ${blockId}`,
|
||||
);
|
||||
// First get the button that opens the dropdown
|
||||
const baseSelector = await this._buildBlockSelector(blockId, dataId);
|
||||
|
||||
@@ -263,6 +235,7 @@ export class BuildPage extends BasePage {
|
||||
// The actual selector for the option might need adjustment based on the dropdown structure
|
||||
await this.page.getByRole("option", { name: value }).click();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`Error selecting value "${value}" for input "${inputName}":`,
|
||||
error,
|
||||
@@ -276,7 +249,6 @@ export class BuildPage extends BasePage {
|
||||
label: string,
|
||||
value: string,
|
||||
): Promise<void> {
|
||||
console.log(`filling block input ${label} with value ${value}`);
|
||||
const block = await this.getBlockById(blockId);
|
||||
const input = block.getByLabel(label);
|
||||
await input.fill(value);
|
||||
@@ -286,9 +258,6 @@ export class BuildPage extends BasePage {
|
||||
blockOutputId: string,
|
||||
blockInputId: string,
|
||||
): Promise<void> {
|
||||
console.log(
|
||||
`connecting block output ${blockOutputId} to block input ${blockInputId}`,
|
||||
);
|
||||
try {
|
||||
// Locate the output element
|
||||
const outputElement = this.page.locator(`[data-id="${blockOutputId}"]`);
|
||||
@@ -297,6 +266,7 @@ export class BuildPage extends BasePage {
|
||||
|
||||
await outputElement.dragTo(inputElement);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error connecting block output to input:", error);
|
||||
}
|
||||
}
|
||||
@@ -309,10 +279,6 @@ export class BuildPage extends BasePage {
|
||||
startDataId?: string,
|
||||
endDataId?: string,
|
||||
): Promise<void> {
|
||||
console.log(
|
||||
`connecting block output ${startBlockOutputName} of block ${startBlockId} to block input ${endBlockInputName} of block ${endBlockId}`,
|
||||
);
|
||||
|
||||
const startBlockBase = await this._buildBlockSelector(
|
||||
startBlockId,
|
||||
startDataId,
|
||||
@@ -322,16 +288,12 @@ export class BuildPage extends BasePage {
|
||||
const startBlockOutputSelector = `${startBlockBase} [data-testid="output-handle-${startBlockOutputName.toLowerCase()}"]`;
|
||||
const endBlockInputSelector = `${endBlockBase} [data-testid="input-handle-${endBlockInputName.toLowerCase()}"]`;
|
||||
|
||||
console.log("Start block selector:", startBlockOutputSelector);
|
||||
console.log("End block selector:", endBlockInputSelector);
|
||||
|
||||
await this.page
|
||||
.locator(startBlockOutputSelector)
|
||||
.dragTo(this.page.locator(endBlockInputSelector));
|
||||
}
|
||||
|
||||
async isLoaded(): Promise<boolean> {
|
||||
console.log(`checking if build page is loaded`);
|
||||
try {
|
||||
await this.page.waitForLoadState("domcontentloaded", { timeout: 10_000 });
|
||||
return true;
|
||||
@@ -341,44 +303,37 @@ export class BuildPage extends BasePage {
|
||||
}
|
||||
|
||||
async isRunButtonEnabled(): Promise<boolean> {
|
||||
console.log(`checking if run button is enabled`);
|
||||
const runButton = this.page.getByTestId("primary-action-run-agent");
|
||||
return await runButton.isEnabled();
|
||||
}
|
||||
|
||||
async runAgent(): Promise<void> {
|
||||
console.log(`clicking run button`);
|
||||
const runButton = this.page.getByTestId("primary-action-run-agent");
|
||||
await runButton.click();
|
||||
}
|
||||
|
||||
async fillRunDialog(inputs: Record<string, string>): Promise<void> {
|
||||
console.log(`filling run dialog`);
|
||||
for (const [key, value] of Object.entries(inputs)) {
|
||||
await this.page.getByTestId(`agent-input-${key}`).fill(value);
|
||||
}
|
||||
}
|
||||
async clickRunDialogRunButton(): Promise<void> {
|
||||
console.log(`clicking run button`);
|
||||
await this.page.getByTestId("agent-run-button").click();
|
||||
}
|
||||
|
||||
async waitForCompletionBadge(): Promise<void> {
|
||||
console.log(`waiting for completion badge`);
|
||||
await this.page.waitForSelector(
|
||||
'[data-id^="badge-"][data-id$="-COMPLETED"]',
|
||||
);
|
||||
}
|
||||
|
||||
async waitForSaveButton(): Promise<void> {
|
||||
console.log(`waiting for save button`);
|
||||
await this.page.waitForSelector(
|
||||
'[data-testid="blocks-control-save-button"]:not([disabled])',
|
||||
);
|
||||
}
|
||||
|
||||
async isCompletionBadgeVisible(): Promise<boolean> {
|
||||
console.log(`checking for completion badge`);
|
||||
const completionBadge = this.page
|
||||
.locator('[data-id^="badge-"][data-id$="-COMPLETED"]')
|
||||
.first();
|
||||
@@ -386,8 +341,6 @@ export class BuildPage extends BasePage {
|
||||
}
|
||||
|
||||
async waitForVersionField(): Promise<void> {
|
||||
console.log(`waiting for version field`);
|
||||
|
||||
// wait for the url to have the flowID
|
||||
await this.page.waitForSelector(
|
||||
'[data-testid="save-control-version-output"]',
|
||||
@@ -413,8 +366,6 @@ export class BuildPage extends BasePage {
|
||||
}
|
||||
|
||||
async waitForSaveDialogClose(): Promise<void> {
|
||||
console.log(`waiting for save dialog to close`);
|
||||
|
||||
await this.page.waitForSelector(
|
||||
'[data-id="save-control-popover-content"]',
|
||||
{ state: "hidden" },
|
||||
@@ -432,7 +383,6 @@ export class BuildPage extends BasePage {
|
||||
}
|
||||
|
||||
async nextTutorialStep(): Promise<void> {
|
||||
console.log(`clicking next tutorial step`);
|
||||
await this.page.getByRole("button", { name: "Next" }).click();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,21 +17,14 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async isLoaded(): Promise<boolean> {
|
||||
console.log(`checking if library page is loaded`);
|
||||
try {
|
||||
await this.page.waitForLoadState("domcontentloaded", { timeout: 10_000 });
|
||||
await this.page.waitForLoadState("domcontentloaded", { timeout: 10_000 });
|
||||
|
||||
await this.page.waitForSelector('[data-testid="library-textbox"]', {
|
||||
state: "visible",
|
||||
timeout: 10_000,
|
||||
});
|
||||
await this.page.waitForSelector('[data-testid="library-textbox"]', {
|
||||
state: "visible",
|
||||
timeout: 10_000,
|
||||
});
|
||||
|
||||
console.log("Library page is loaded successfully");
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log("Library page failed to load:", error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async navigateToLibrary(): Promise<void> {
|
||||
@@ -53,7 +46,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async searchAgents(searchTerm: string): Promise<void> {
|
||||
console.log(`searching for agents with term: ${searchTerm}`);
|
||||
const { getRole } = getSelectors(this.page);
|
||||
const searchInput = getRole("textbox", "Search agents");
|
||||
await searchInput.fill(searchTerm);
|
||||
@@ -62,7 +54,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async clearSearch(): Promise<void> {
|
||||
console.log(`clearing search`);
|
||||
try {
|
||||
// Look for the clear button (X icon)
|
||||
const clearButton = this.page.locator(".lucide.lucide-x");
|
||||
@@ -79,6 +70,7 @@ export class LibraryPage extends BasePage {
|
||||
// Wait for results to update
|
||||
await this.page.waitForTimeout(500);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error clearing search:", error);
|
||||
}
|
||||
}
|
||||
@@ -96,19 +88,18 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async getCurrentSortOption(): Promise<string> {
|
||||
console.log(`getting current sort option`);
|
||||
try {
|
||||
const sortCombobox = this.page.getByRole("combobox");
|
||||
const currentOption = await sortCombobox.textContent();
|
||||
return currentOption?.trim() || "";
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error getting current sort option:", error);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
async openUploadDialog(): Promise<void> {
|
||||
console.log(`opening upload dialog`);
|
||||
await this.page.getByRole("button", { name: "Upload an agent" }).click();
|
||||
|
||||
// Wait for dialog to appear
|
||||
@@ -128,7 +119,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async isUploadDialogVisible(): Promise<boolean> {
|
||||
console.log(`checking if upload dialog is visible`);
|
||||
try {
|
||||
const dialog = this.page.getByRole("dialog", { name: "Upload Agent" });
|
||||
return await dialog.isVisible();
|
||||
@@ -138,10 +128,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async fillUploadForm(agentName: string, description: string): Promise<void> {
|
||||
console.log(
|
||||
`filling upload form with name: ${agentName}, description: ${description}`,
|
||||
);
|
||||
|
||||
// Fill agent name
|
||||
await this.page
|
||||
.getByRole("textbox", { name: "Agent name" })
|
||||
@@ -154,7 +140,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async isUploadButtonEnabled(): Promise<boolean> {
|
||||
console.log(`checking if upload button is enabled`);
|
||||
try {
|
||||
const uploadButton = this.page.getByRole("button", {
|
||||
name: "Upload Agent",
|
||||
@@ -199,7 +184,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`found ${agents.length} agents`);
|
||||
return agents;
|
||||
}
|
||||
|
||||
@@ -210,16 +194,12 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async clickSeeRuns(agent: Agent): Promise<void> {
|
||||
console.log(`clicking see runs for agent: ${agent.name}`);
|
||||
|
||||
// Find the "See runs" link for this specific agent
|
||||
const agentCard = this.page.locator(`[href="${agent.seeRunsUrl}"]`).first();
|
||||
await agentCard.click();
|
||||
}
|
||||
|
||||
async clickOpenInBuilder(agent: Agent): Promise<void> {
|
||||
console.log(`clicking open in builder for agent: ${agent.name}`);
|
||||
|
||||
// Find the "Open in builder" link for this specific agent
|
||||
const builderLink = this.page
|
||||
.locator(`[href="${agent.openInBuilderUrl}"]`)
|
||||
@@ -238,12 +218,10 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async clickMonitoringLink(): Promise<void> {
|
||||
console.log(`clicking monitoring link in alert`);
|
||||
await this.page.getByRole("link", { name: "here" }).click();
|
||||
}
|
||||
|
||||
async isMonitoringAlertVisible(): Promise<boolean> {
|
||||
console.log(`checking if monitoring alert is visible`);
|
||||
try {
|
||||
const alertText = this.page.locator("text=/Prefer the old experience/");
|
||||
return await alertText.isVisible();
|
||||
@@ -253,7 +231,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async getSearchValue(): Promise<string> {
|
||||
console.log(`getting search input value`);
|
||||
try {
|
||||
const searchInput = this.page.getByRole("textbox", {
|
||||
name: "Search agents",
|
||||
@@ -271,34 +248,22 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async scrollToBottom(): Promise<void> {
|
||||
console.log(`scrolling to bottom to trigger pagination`);
|
||||
await this.page.keyboard.press("End");
|
||||
await this.page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
async scrollDown(): Promise<void> {
|
||||
console.log(`scrolling down to trigger pagination`);
|
||||
await this.page.keyboard.press("PageDown");
|
||||
await this.page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
async scrollToLoadMore(): Promise<void> {
|
||||
console.log(`scrolling to load more agents`);
|
||||
|
||||
// Get initial agent count
|
||||
const initialCount = await this.getAgentCount();
|
||||
console.log(`Initial agent count: ${initialCount}`);
|
||||
|
||||
// Scroll down to trigger pagination
|
||||
await this.scrollToBottom();
|
||||
|
||||
// Wait for potential new agents to load
|
||||
await this.page.waitForTimeout(2000);
|
||||
|
||||
// Check if more agents loaded
|
||||
const newCount = await this.getAgentCount();
|
||||
console.log(`New agent count after scroll: ${newCount}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -320,8 +285,6 @@ export class LibraryPage extends BasePage {
|
||||
}
|
||||
|
||||
async getAgentsWithPagination(): Promise<Agent[]> {
|
||||
console.log(`getting all agents with pagination`);
|
||||
|
||||
let allAgents: Agent[] = [];
|
||||
let previousCount = 0;
|
||||
let currentCount = 0;
|
||||
@@ -336,21 +299,16 @@ export class LibraryPage extends BasePage {
|
||||
allAgents = currentAgents;
|
||||
currentCount = currentAgents.length;
|
||||
|
||||
console.log(`Attempt ${attempts + 1}: Found ${currentCount} agents`);
|
||||
|
||||
// Try to load more by scrolling
|
||||
await this.scrollToLoadMore();
|
||||
|
||||
attempts++;
|
||||
} while (currentCount > previousCount && attempts < maxAttempts);
|
||||
|
||||
console.log(`Total agents found with pagination: ${allAgents.length}`);
|
||||
return allAgents;
|
||||
}
|
||||
|
||||
async waitForPaginationLoad(): Promise<void> {
|
||||
console.log(`waiting for pagination to load`);
|
||||
|
||||
// Wait for any loading states to complete
|
||||
await this.page.waitForTimeout(1000);
|
||||
|
||||
@@ -372,8 +330,6 @@ export class LibraryPage extends BasePage {
|
||||
previousCount = currentCount;
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
console.log(`Pagination load stabilized with ${currentCount} agents`);
|
||||
}
|
||||
|
||||
async scrollAndWaitForNewAgents(): Promise<number> {
|
||||
@@ -386,10 +342,6 @@ export class LibraryPage extends BasePage {
|
||||
const finalCount = await this.getAgentCountByListLength();
|
||||
const newAgentsLoaded = finalCount - initialCount;
|
||||
|
||||
console.log(
|
||||
`Loaded ${newAgentsLoaded} new agents (${initialCount} -> ${finalCount})`,
|
||||
);
|
||||
|
||||
return newAgentsLoaded;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,6 @@ export class LoginPage {
|
||||
}
|
||||
|
||||
async login(email: string, password: string) {
|
||||
console.log(`ℹ️ Attempting login on ${this.page.url()} with`, {
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
// Wait for the form to be ready
|
||||
await this.page.waitForSelector("form", { state: "visible" });
|
||||
|
||||
@@ -34,7 +29,10 @@ export class LoginPage {
|
||||
await loginButton.waitFor({ state: "visible" });
|
||||
|
||||
// Attach navigation logger for debug purposes
|
||||
this.page.on("load", (page) => console.log(`ℹ️ Now at URL: ${page.url()}`));
|
||||
this.page.on("load", (page) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`ℹ️ Now at URL: ${page.url()}`);
|
||||
});
|
||||
|
||||
// Start waiting for navigation before clicking
|
||||
const leaveLoginPage = this.page
|
||||
@@ -43,6 +41,7 @@ export class LoginPage {
|
||||
{ timeout: 10_000 },
|
||||
)
|
||||
.catch((reason) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`🚨 Navigation away from /login timed out (current URL: ${this.page.url()}):`,
|
||||
reason,
|
||||
@@ -50,18 +49,13 @@ export class LoginPage {
|
||||
throw reason;
|
||||
});
|
||||
|
||||
console.log(`🖱️ Clicking login button...`);
|
||||
await loginButton.click();
|
||||
|
||||
console.log("⏳ Waiting for navigation away from /login ...");
|
||||
await leaveLoginPage;
|
||||
console.log(`⌛ Post-login redirected to ${this.page.url()}`);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200)); // allow time for client-side redirect
|
||||
await this.page.waitForLoadState("load", { timeout: 10_000 });
|
||||
|
||||
console.log("➡️ Navigating to /marketplace ...");
|
||||
await this.page.goto("/marketplace", { timeout: 10_000 });
|
||||
console.log("✅ Login process complete");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,44 +37,38 @@ export class MonitorPage extends BasePage {
|
||||
}
|
||||
|
||||
async isLoaded(): Promise<boolean> {
|
||||
console.log(`checking if monitor page is loaded`);
|
||||
try {
|
||||
// Wait for the monitor page
|
||||
await this.page.getByTestId("monitor-page").waitFor({
|
||||
state: "visible",
|
||||
timeout: 10_000,
|
||||
});
|
||||
// Wait for the monitor page
|
||||
await this.page.getByTestId("monitor-page").waitFor({
|
||||
state: "visible",
|
||||
timeout: 10_000,
|
||||
});
|
||||
|
||||
// Wait for table headers to be visible (indicates table structure is ready)
|
||||
await this.page.locator("thead th").first().waitFor({
|
||||
// Wait for table headers to be visible (indicates table structure is ready)
|
||||
await this.page.locator("thead th").first().waitFor({
|
||||
state: "visible",
|
||||
timeout: 15_000,
|
||||
});
|
||||
|
||||
// Wait for either a table row or an empty tbody to be present
|
||||
await Promise.race([
|
||||
// Wait for at least one row
|
||||
this.page.locator("tbody tr[data-testid]").first().waitFor({
|
||||
state: "visible",
|
||||
timeout: 15_000,
|
||||
});
|
||||
|
||||
// Wait for either a table row or an empty tbody to be present
|
||||
await Promise.race([
|
||||
// Wait for at least one row
|
||||
this.page.locator("tbody tr[data-testid]").first().waitFor({
|
||||
}),
|
||||
// OR wait for an empty tbody (indicating no agents but table is loaded)
|
||||
this.page
|
||||
.locator("tbody[data-testid='agent-flow-list-body']:empty")
|
||||
.waitFor({
|
||||
state: "visible",
|
||||
timeout: 15_000,
|
||||
}),
|
||||
// OR wait for an empty tbody (indicating no agents but table is loaded)
|
||||
this.page
|
||||
.locator("tbody[data-testid='agent-flow-list-body']:empty")
|
||||
.waitFor({
|
||||
state: "visible",
|
||||
timeout: 15_000,
|
||||
}),
|
||||
]);
|
||||
]);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async listAgents(): Promise<Agent[]> {
|
||||
console.log(`listing agents`);
|
||||
// Wait for table rows to be available
|
||||
const rows = await this.page.locator("tbody tr[data-testid]").all();
|
||||
|
||||
@@ -118,7 +112,6 @@ export class MonitorPage extends BasePage {
|
||||
}
|
||||
|
||||
async listRuns(filter?: Agent): Promise<Run[]> {
|
||||
console.log(`listing runs`);
|
||||
// Wait for the runs table to be loaded - look for table header "Agent"
|
||||
await this.page.locator("[data-testid='flow-runs-list-body']").waitFor({
|
||||
timeout: 10000,
|
||||
@@ -160,17 +153,14 @@ export class MonitorPage extends BasePage {
|
||||
return runs;
|
||||
}
|
||||
async listSchedules(): Promise<Schedule[]> {
|
||||
console.log(`listing schedules`);
|
||||
return [];
|
||||
}
|
||||
|
||||
async clickAgent(id: string) {
|
||||
console.log(`selecting agent ${id}`);
|
||||
await this.page.getByTestId(id).click();
|
||||
}
|
||||
|
||||
async clickCreateAgent(): Promise<void> {
|
||||
console.log(`clicking create agent`);
|
||||
await this.page.getByRole("link", { name: "Create" }).click();
|
||||
}
|
||||
|
||||
@@ -181,9 +171,6 @@ export class MonitorPage extends BasePage {
|
||||
description?: string,
|
||||
importType: ImportType = ImportType.AGENT,
|
||||
) {
|
||||
console.log(
|
||||
`importing from directory: ${directory} file: ${file} name: ${name} description: ${description} importType: ${importType}`,
|
||||
);
|
||||
await this.page.getByTestId("create-agent-dropdown").click();
|
||||
await this.page.getByTestId("import-agent-from-file").click();
|
||||
|
||||
@@ -191,47 +178,23 @@ export class MonitorPage extends BasePage {
|
||||
.getByTestId("import-agent-file-input")
|
||||
.setInputFiles(path.join(directory, file));
|
||||
if (name) {
|
||||
console.log(`filling agent name: ${name}`);
|
||||
await this.page.getByTestId("agent-name-input").fill(name);
|
||||
}
|
||||
if (description) {
|
||||
console.log(`filling agent description: ${description}`);
|
||||
await this.page.getByTestId("agent-description-input").fill(description);
|
||||
}
|
||||
if (importType === ImportType.TEMPLATE) {
|
||||
console.log(`clicking import as template switch`);
|
||||
await this.page.getByTestId("import-as-template-switch").click();
|
||||
}
|
||||
console.log(`clicking import agent submit`);
|
||||
await this.page.getByTestId("import-agent-submit").click();
|
||||
}
|
||||
|
||||
async deleteAgent(agent: Agent) {
|
||||
console.log(`deleting agent ${agent.id} ${agent.name}`);
|
||||
}
|
||||
|
||||
async clickAllVersions(agent: Agent) {
|
||||
console.log(`clicking all versions for agent ${agent.id} ${agent.name}`);
|
||||
}
|
||||
|
||||
async openInBuilder(agent: Agent) {
|
||||
console.log(`opening agent ${agent.id} ${agent.name} in builder`);
|
||||
async deleteAgent() {
|
||||
await this.page.getByTestId("delete-agent-button").click();
|
||||
}
|
||||
|
||||
async exportToFile(agent: Agent) {
|
||||
await this.clickAgent(agent.id);
|
||||
|
||||
console.log(`exporting agent id: ${agent.id} name: ${agent.name} to file`);
|
||||
await this.page.getByTestId("export-button").click();
|
||||
}
|
||||
|
||||
async selectRun(agent: Agent, run: Run) {
|
||||
console.log(`selecting run ${run.id} for agent ${agent.id} ${agent.name}`);
|
||||
}
|
||||
|
||||
async openOutputs(agent: Agent, run: Run) {
|
||||
console.log(
|
||||
`opening outputs for run ${run.id} of agent ${agent.id} ${agent.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@ export class ProfilePage extends BasePage {
|
||||
try {
|
||||
await this.waitForPageToLoad();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error loading profile page", error);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,12 +161,14 @@ test("multi-tab logout with WebSocket cleanup", async ({ context }) => {
|
||||
// Verify the profile menu is no longer visible (user is logged out)
|
||||
await isHidden(getId2("profile-popout-menu-trigger"));
|
||||
|
||||
// Verify no WebSocket connection errors occurred during logout
|
||||
test.expect(consoleErrors).toHaveLength(0);
|
||||
if (consoleErrors.length > 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("WebSocket errors during logout:", consoleErrors);
|
||||
}
|
||||
|
||||
// Verify no WebSocket connection errors occurred during logout
|
||||
test.expect(consoleErrors).toHaveLength(0);
|
||||
|
||||
// Clean up
|
||||
await page1.close();
|
||||
await page2.close();
|
||||
|
||||
@@ -9,26 +9,22 @@ import { getSelectors } from "./utils/selectors";
|
||||
import { hasUrl, isVisible } from "./utils/assertion";
|
||||
|
||||
test("user can signup successfully", async ({ page }) => {
|
||||
try {
|
||||
const testUser = await signupTestUser(page);
|
||||
const { getText, getId } = getSelectors(page);
|
||||
const testUser = await signupTestUser(page);
|
||||
const { getText, getId } = getSelectors(page);
|
||||
|
||||
// Verify user was created
|
||||
expect(testUser.email).toBeTruthy();
|
||||
expect(testUser.password).toBeTruthy();
|
||||
expect(testUser.createdAt).toBeTruthy();
|
||||
// Verify user was created
|
||||
expect(testUser.email).toBeTruthy();
|
||||
expect(testUser.password).toBeTruthy();
|
||||
expect(testUser.createdAt).toBeTruthy();
|
||||
|
||||
const marketplaceText = getText(
|
||||
"Bringing you AI agents designed by thinkers from around the world",
|
||||
);
|
||||
const marketplaceText = getText(
|
||||
"Bringing you AI agents designed by thinkers from around the world",
|
||||
);
|
||||
|
||||
// Verify we're on marketplace and authenticated
|
||||
await hasUrl(page, "/marketplace");
|
||||
await isVisible(marketplaceText);
|
||||
await isVisible(getId("profile-popout-menu-trigger"));
|
||||
} catch (error) {
|
||||
console.error("❌ Signup test failed:", error);
|
||||
}
|
||||
// Verify we're on marketplace and authenticated
|
||||
await hasUrl(page, "/marketplace");
|
||||
await isVisible(marketplaceText);
|
||||
await isVisible(getId("profile-popout-menu-trigger"));
|
||||
});
|
||||
|
||||
test("signup form validation works", async ({ page }) => {
|
||||
@@ -58,69 +54,57 @@ test("signup form validation works", async ({ page }) => {
|
||||
test("user can signup with custom credentials", async ({ page }) => {
|
||||
const { getId } = getSelectors(page);
|
||||
|
||||
try {
|
||||
const customEmail = generateTestEmail();
|
||||
const customPassword = generateTestPassword();
|
||||
const customEmail = generateTestEmail();
|
||||
const customPassword = generateTestPassword();
|
||||
|
||||
const testUser = await signupTestUser(page, customEmail, customPassword);
|
||||
const testUser = await signupTestUser(page, customEmail, customPassword);
|
||||
|
||||
// Verify correct credentials were used
|
||||
expect(testUser.email).toBe(customEmail);
|
||||
expect(testUser.password).toBe(customPassword);
|
||||
// Verify correct credentials were used
|
||||
expect(testUser.email).toBe(customEmail);
|
||||
expect(testUser.password).toBe(customPassword);
|
||||
|
||||
// Verify successful signup
|
||||
await hasUrl(page, "/marketplace");
|
||||
await isVisible(getId("profile-popout-menu-trigger"));
|
||||
} catch (error) {
|
||||
console.error("❌ Custom credentials signup test failed:", error);
|
||||
}
|
||||
// Verify successful signup
|
||||
await hasUrl(page, "/marketplace");
|
||||
await isVisible(getId("profile-popout-menu-trigger"));
|
||||
});
|
||||
|
||||
test("user can signup with existing email handling", async ({
|
||||
page,
|
||||
browser,
|
||||
}) => {
|
||||
try {
|
||||
const testEmail = generateTestEmail();
|
||||
const testPassword = generateTestPassword();
|
||||
const testEmail = generateTestEmail();
|
||||
const testPassword = generateTestPassword();
|
||||
|
||||
// First signup
|
||||
const firstUser = await signupTestUser(page, testEmail, testPassword);
|
||||
expect(firstUser.email).toBe(testEmail);
|
||||
// First signup
|
||||
const firstUser = await signupTestUser(page, testEmail, testPassword);
|
||||
expect(firstUser.email).toBe(testEmail);
|
||||
|
||||
// Create new browser context for second signup (simulates new browser window)
|
||||
const newContext = await browser.newContext();
|
||||
const newPage = await newContext.newPage();
|
||||
// Create new browser context for second signup (simulates new browser window)
|
||||
const newContext = await browser.newContext();
|
||||
const newPage = await newContext.newPage();
|
||||
|
||||
try {
|
||||
const { getText, getField, getRole, getButton } = getSelectors(newPage);
|
||||
const { getText, getField, getRole, getButton } = getSelectors(newPage);
|
||||
|
||||
// Second signup attempt with same email in new browser context
|
||||
// Navigate to signup page
|
||||
await newPage.goto("http://localhost:3000/signup");
|
||||
// Second signup attempt with same email in new browser context
|
||||
// Navigate to signup page
|
||||
await newPage.goto("http://localhost:3000/signup");
|
||||
|
||||
// Wait for page to load
|
||||
getText("Create a new account");
|
||||
// Wait for page to load
|
||||
getText("Create a new account");
|
||||
|
||||
// Fill form
|
||||
const emailInput = getField("Email");
|
||||
await emailInput.fill(testEmail);
|
||||
const passwordInput = newPage.locator("#password");
|
||||
await passwordInput.fill(testPassword);
|
||||
const confirmPasswordInput = newPage.locator("#confirmPassword");
|
||||
await confirmPasswordInput.fill(testPassword);
|
||||
// Fill form
|
||||
const emailInput = getField("Email");
|
||||
await emailInput.fill(testEmail);
|
||||
const passwordInput = newPage.locator("#password");
|
||||
await passwordInput.fill(testPassword);
|
||||
const confirmPasswordInput = newPage.locator("#confirmPassword");
|
||||
await confirmPasswordInput.fill(testPassword);
|
||||
|
||||
// Agree to terms and submit
|
||||
await getRole("checkbox").click();
|
||||
const signupButton = getButton("Sign up");
|
||||
await signupButton.click();
|
||||
await isVisible(getText("User with this email already exists"));
|
||||
} catch (_error) {
|
||||
} finally {
|
||||
// Clean up new browser context
|
||||
await newContext.close();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Duplicate email handling test failed:", error);
|
||||
}
|
||||
// Agree to terms and submit
|
||||
await getRole("checkbox").click();
|
||||
const signupButton = getButton("Sign up");
|
||||
await signupButton.click();
|
||||
await isVisible(getText("User with this email already exists"));
|
||||
// Clean up new browser context
|
||||
await newContext.close();
|
||||
});
|
||||
|
||||
@@ -45,14 +45,11 @@ export async function createTestUser(
|
||||
await browser.close();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ Error creating test user ${userEmail}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTestUsers(count: number): Promise<TestUser[]> {
|
||||
console.log(`👥 Creating ${count} test users...`);
|
||||
|
||||
const users: TestUser[] = [];
|
||||
let consecutiveFailures = 0;
|
||||
|
||||
@@ -61,21 +58,16 @@ export async function createTestUsers(count: number): Promise<TestUser[]> {
|
||||
const user = await createTestUser();
|
||||
users.push(user);
|
||||
consecutiveFailures = 0; // Reset failure counter on success
|
||||
console.log(`✅ Created user ${i + 1}/${count}: ${user.email}`);
|
||||
|
||||
// Small delay to prevent overwhelming the system
|
||||
if (i < count - 1) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
consecutiveFailures++;
|
||||
console.error(`❌ Failed to create user ${i + 1}/${count}:`, error);
|
||||
|
||||
// If we have too many consecutive failures, stop trying
|
||||
if (consecutiveFailures >= 3) {
|
||||
console.error(
|
||||
`⚠️ Stopping after ${consecutiveFailures} consecutive failures`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -84,7 +76,6 @@ export async function createTestUsers(count: number): Promise<TestUser[]> {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`🎉 Successfully created ${users.length}/${count} test users`);
|
||||
return users;
|
||||
}
|
||||
|
||||
@@ -109,9 +100,7 @@ export async function saveUserPool(
|
||||
|
||||
try {
|
||||
fs.writeFileSync(finalPath, JSON.stringify(userPool, null, 2));
|
||||
console.log(`✅ Successfully saved user pool to: ${finalPath}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to save user pool to ${finalPath}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -122,26 +111,16 @@ export async function loadUserPool(
|
||||
const defaultPath = path.resolve(process.cwd(), ".auth", "user-pool.json");
|
||||
const finalPath = filePath || defaultPath;
|
||||
|
||||
console.log(`📖 Loading user pool from: ${finalPath}`);
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(finalPath)) {
|
||||
console.log(`⚠️ User pool file not found: ${finalPath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const fileContent = fs.readFileSync(finalPath, "utf-8");
|
||||
const userPool: UserPool = JSON.parse(fileContent);
|
||||
|
||||
console.log(
|
||||
`✅ Successfully loaded ${userPool.users.length} users from: ${finalPath}`,
|
||||
);
|
||||
console.log(`📅 User pool created at: ${userPool.createdAt}`);
|
||||
console.log(`🔖 User pool version: ${userPool.version}`);
|
||||
|
||||
return userPool;
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to load user pool from ${finalPath}:`, error);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ export class SigninUtils {
|
||||
* Perform login and verify success
|
||||
*/
|
||||
async loginAndVerify(testUser: TestUser): Promise<void> {
|
||||
console.log(`🔐 Logging in as: ${testUser.email}`);
|
||||
|
||||
await this.page.goto("/login");
|
||||
await this.loginPage.login(testUser.email, testUser.password);
|
||||
|
||||
@@ -33,16 +31,12 @@ export class SigninUtils {
|
||||
state: "visible",
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
console.log("✅ Login successful");
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform logout and verify success
|
||||
*/
|
||||
async logoutAndVerify(): Promise<void> {
|
||||
console.log("🚪 Logging out...");
|
||||
|
||||
// Open profile menu
|
||||
await this.page.getByTestId("profile-popout-menu-trigger").click();
|
||||
|
||||
@@ -57,16 +51,12 @@ export class SigninUtils {
|
||||
|
||||
// Verify we're back on login page
|
||||
await this.page.waitForURL("/login");
|
||||
|
||||
console.log("✅ Logout successful");
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete authentication cycle: login -> logout -> login
|
||||
*/
|
||||
async fullAuthenticationCycle(testUser: TestUser): Promise<void> {
|
||||
console.log("🔄 Starting full authentication cycle...");
|
||||
|
||||
// First login
|
||||
await this.loginAndVerify(testUser);
|
||||
|
||||
@@ -75,8 +65,6 @@ export class SigninUtils {
|
||||
|
||||
// Login again
|
||||
await this.loginAndVerify(testUser);
|
||||
|
||||
console.log("✅ Full authentication cycle completed");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,10 +45,8 @@ export async function signupTestUser(
|
||||
page.waitForURL(/\/marketplace/, { timeout: 15000 }),
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"❌ Timeout waiting for redirect, current URL:",
|
||||
page.url(),
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Timeout waiting for redirect, current URL:", page.url());
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -95,7 +93,6 @@ export async function signupTestUser(
|
||||
|
||||
return testUser;
|
||||
} catch (error) {
|
||||
console.error(`❌ Error creating test user ${userEmail}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -105,47 +102,34 @@ export async function signupAndNavigateToMarketplace(
|
||||
email?: string,
|
||||
password?: string,
|
||||
): Promise<TestUser> {
|
||||
console.log("🧪 Creating user and navigating to marketplace...");
|
||||
|
||||
// Create the user via signup and automatically navigate to marketplace
|
||||
const testUser = await signupTestUser(page, email, password, true);
|
||||
|
||||
console.log("✅ User successfully created and authenticated in marketplace");
|
||||
return testUser;
|
||||
}
|
||||
|
||||
export async function validateSignupForm(page: any): Promise<void> {
|
||||
console.log("🧪 Validating signup form...");
|
||||
|
||||
await page.goto("http://localhost:3000/signup");
|
||||
|
||||
// Test empty form submission
|
||||
console.log("❌ Testing empty form submission...");
|
||||
const signupButton = page.getByRole("button", { name: "Sign up" });
|
||||
await signupButton.click();
|
||||
|
||||
// Should still be on signup page
|
||||
const currentUrl = page.url();
|
||||
if (currentUrl.includes("/signup")) {
|
||||
console.log("✅ Empty form correctly blocked");
|
||||
} else {
|
||||
console.log("⚠️ Empty form was not blocked as expected");
|
||||
}
|
||||
|
||||
// Test invalid email
|
||||
console.log("❌ Testing invalid email...");
|
||||
await page.getByLabel("Email").fill("invalid-email");
|
||||
await signupButton.click();
|
||||
|
||||
// Should still be on signup page
|
||||
const currentUrl2 = page.url();
|
||||
if (currentUrl2.includes("/signup")) {
|
||||
console.log("✅ Invalid email correctly blocked");
|
||||
} else {
|
||||
console.log("⚠️ Invalid email was not blocked as expected");
|
||||
}
|
||||
|
||||
console.log("✅ Signup form validation completed");
|
||||
}
|
||||
|
||||
export function generateTestEmail(): string {
|
||||
|
||||
Reference in New Issue
Block a user