Explore the world of ventures
- -- Unlock opportunities and connect with a community of passionate -
+ +Explore the world of ventures
+ +Unlock opportunities and connect with a community of passionate
investors and innovators.
Together, we turn ideas into impact.
- @@ -42,36 +34,36 @@ export default function Home() {Hottest Deals
-- The deals attracting the most interest right now -
+Hottest Deals
+The deals attracting the most interest right now
-
+
+
{props.name}
-
+
{/* Default content (visible when not hovered) */}
- Joined {props.joinDate}
+ Joined {props.joinDate}
- {props.location}
+ {props.location}
- {["Technology", "Gaming"].map((tag) => (
+ {props.tags.map((tag) => (
+ className="text-[10px] md:text-xs rounded-md bg-slate-200 dark:bg-slate-700 p-1 mx-1 mb-1">
{tag}
))}
@@ -58,24 +58,21 @@ export function ExtendableCard(props: ExtendableCardProps) {
{/* Hover content (appears when hovered) */}
- {props.description}
+ {props.description}
-
- ${props.totalRaised.toLocaleString()}{" "}
- committed and reserved
+
+ ${props.totalRaised.toLocaleString()} committed and reserved
-
- {props.totalInvestor.toLocaleString()}{" "}
- investors
+
+ {props.totalInvestor.toLocaleString()} investors
-
- ${props.minInvestment.toLocaleString()} min.
- investment
+
+ ${props.minInvestment.toLocaleString()} min. investment
diff --git a/src/components/navigationBar/nav.tsx b/src/components/navigationBar/nav.tsx
index ec17864..9b6bed0 100644
--- a/src/components/navigationBar/nav.tsx
+++ b/src/components/navigationBar/nav.tsx
@@ -8,6 +8,7 @@ import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
import { ThemeToggle } from "@/components/theme-toggle";
import { Button } from "@/components/ui/button";
+import { useRouter } from 'next/navigation';
import {
NavigationMenu,
NavigationMenuContent,
@@ -133,6 +134,8 @@ export function NavigationBar() {
const { session, loading } = useSession();
const user = session?.user;
const [sessionLoaded, setSessionLoaded] = React.useState(false);
+ const [searchActive, setSearchActive] = React.useState(false);
+ const router = useRouter();
React.useEffect(() => {
if (!loading) {
@@ -140,6 +143,15 @@ export function NavigationBar() {
}
}, [loading]);
+ const handleKeyDown = async (k: React.KeyboardEvent) => {
+ if (k.key === 'Enter') {
+ const query = (k.target as HTMLInputElement).value.trim();
+ if (query) {
+ router.push(`/find?query=${encodeURIComponent(query)}`);
+ }
+ }
+ };
+
const businessComponents = [
{
title: "Businesses",
@@ -249,8 +261,21 @@ export function NavigationBar() {
-
-
+
+ setSearchActive(!searchActive)}
+ className="cursor-pointer"
+ />
+ {/* search bar's input */}
+
diff --git a/src/components/paymentMethod.tsx b/src/components/paymentMethod.tsx
new file mode 100644
index 0000000..36eccc6
--- /dev/null
+++ b/src/components/paymentMethod.tsx
@@ -0,0 +1,122 @@
+"use client";
+
+import { Icons } from "./ui/icons";
+import { Button } from "./ui/button";
+import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./ui/card";
+import { Input } from "./ui/input";
+import { Label } from "./ui/label";
+import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
+
+export function CardsPaymentMethod() {
+ return (
+
+
+ Payment Method
+ Add a new payment method to your account.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ January
+ February
+ March
+ April
+ May
+ June
+ July
+ August
+ September
+ October
+ November
+ December
+
+
+
+
+
+
+
+
+
+
+ {Array.from({ length: 10 }, (_, i) => (
+
+ {new Date().getFullYear() + i}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {/*
+ Continue
+ */}
+
+ );
+}
diff --git a/src/components/recent-funds.tsx b/src/components/recent-funds.tsx
new file mode 100644
index 0000000..493e535
--- /dev/null
+++ b/src/components/recent-funds.tsx
@@ -0,0 +1,59 @@
+import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
+
+const data = [
+ {
+ name: "Olivia Martin",
+ email: "olivia.martin@email.com",
+ amount: "1900.00",
+ avatar: "/avatars/01.png", // psuedo avatar image
+ initials: "OM",
+ },
+ {
+ name: "Jackson Lee",
+ email: "jackson.lee@email.com",
+ amount: "39.00",
+ avatar: "/avatars/02.png",
+ initials: "JL",
+ },
+ {
+ name: "Isabella Nguyen",
+ email: "isabella.nguyen@email.com",
+ amount: "299.00",
+ avatar: "/avatars/03.png",
+ initials: "IN",
+ },
+ {
+ name: "William Kim",
+ email: "will@email.com",
+ amount: "99.00",
+ avatar: "/avatars/04.png",
+ initials: "WK",
+ },
+ {
+ name: "Sofia Davis",
+ email: "sofia.davis@email.com",
+ amount: "39.00",
+ avatar: "/avatars/05.png",
+ initials: "SD",
+ },
+];
+
+export function RecentFunds() {
+ return (
+
+ {data.map((person, index) => (
+
+
+
+ {person.initials}
+
+
+ {person.name}
+ {person.email}
+
+ +${person.amount}
+
+ ))}
+
+ );
+}
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..01ff19c
--- /dev/null
+++ b/src/components/ui/dialog.tsx
@@ -0,0 +1,122 @@
+"use client"
+
+import * as React from "react"
+import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { X } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Dialog = DialogPrimitive.Root
+
+const DialogTrigger = DialogPrimitive.Trigger
+
+const DialogPortal = DialogPrimitive.Portal
+
+const DialogClose = DialogPrimitive.Close
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+))
+DialogContent.displayName = DialogPrimitive.Content.displayName
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogHeader.displayName = "DialogHeader"
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogFooter.displayName = "DialogFooter"
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogTitle.displayName = DialogPrimitive.Title.displayName
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogDescription.displayName = DialogPrimitive.Description.displayName
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogClose,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+}
diff --git a/src/components/ui/icons.tsx b/src/components/ui/icons.tsx
new file mode 100644
index 0000000..ebeede1
--- /dev/null
+++ b/src/components/ui/icons.tsx
@@ -0,0 +1,136 @@
+type IconProps = React.HTMLAttributes;
+
+export const Icons = {
+ logo: (props: IconProps) => (
+
+ ),
+ twitter: (props: IconProps) => (
+
+ ),
+ gitHub: (props: IconProps) => (
+
+ ),
+ radix: (props: IconProps) => (
+
+ ),
+ aria: (props: IconProps) => (
+
+ ),
+ npm: (props: IconProps) => (
+
+ ),
+ yarn: (props: IconProps) => (
+
+ ),
+ pnpm: (props: IconProps) => (
+
+ ),
+ react: (props: IconProps) => (
+
+ ),
+ tailwind: (props: IconProps) => (
+
+ ),
+ google: (props: IconProps) => (
+
+ ),
+ apple: (props: IconProps) => (
+
+ ),
+ paypal: (props: IconProps) => (
+
+ ),
+ spinner: (props: IconProps) => (
+
+ ),
+};
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
new file mode 100644
index 0000000..a921025
--- /dev/null
+++ b/src/components/ui/input.tsx
@@ -0,0 +1,25 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface InputProps
+ extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Input.displayName = "Input"
+
+export { Input }
diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx
new file mode 100644
index 0000000..5341821
--- /dev/null
+++ b/src/components/ui/label.tsx
@@ -0,0 +1,26 @@
+"use client"
+
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+)
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/src/components/ui/overview.tsx b/src/components/ui/overview.tsx
new file mode 100644
index 0000000..dbe45f8
--- /dev/null
+++ b/src/components/ui/overview.tsx
@@ -0,0 +1,110 @@
+"use client";
+
+import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis, LineChart, Line } from "recharts";
+
+const data = [
+ {
+ name: "Jan",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Feb",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Mar",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Apr",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "May",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Jun",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Jul",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Aug",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Sep",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Oct",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Nov",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+ {
+ name: "Dec",
+ total: Math.floor(Math.random() * 5000) + 1000,
+ },
+];
+
+interface OverViewProps{
+ graphType:string;
+}
+
+export function Overview(props: OverViewProps) {
+ return (
+
+ {props.graphType === 'line' ? (
+
+
+ `$${value}`}
+ />
+
+
+ ) : (
+
+
+ `$${value}`}
+ />
+
+
+ )}
+
+ );
+}
diff --git a/src/components/ui/radio-group.tsx b/src/components/ui/radio-group.tsx
new file mode 100644
index 0000000..e9bde17
--- /dev/null
+++ b/src/components/ui/radio-group.tsx
@@ -0,0 +1,44 @@
+"use client"
+
+import * as React from "react"
+import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
+import { Circle } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const RadioGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+ )
+})
+RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
+
+const RadioGroupItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+
+
+
+
+ )
+})
+RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
+
+export { RadioGroup, RadioGroupItem }
diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx
new file mode 100644
index 0000000..7f3502f
--- /dev/null
+++ b/src/components/ui/table.tsx
@@ -0,0 +1,117 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Table = React.forwardRef<
+ HTMLTableElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+
+
+))
+Table.displayName = "Table"
+
+const TableHeader = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableHeader.displayName = "TableHeader"
+
+const TableBody = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableBody.displayName = "TableBody"
+
+const TableFooter = React.forwardRef<
+ HTMLTableSectionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+ tr]:last:border-b-0",
+ className
+ )}
+ {...props}
+ />
+))
+TableFooter.displayName = "TableFooter"
+
+const TableRow = React.forwardRef<
+ HTMLTableRowElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableRow.displayName = "TableRow"
+
+const TableHead = React.forwardRef<
+ HTMLTableCellElement,
+ React.ThHTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableHead.displayName = "TableHead"
+
+const TableCell = React.forwardRef<
+ HTMLTableCellElement,
+ React.TdHTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableCell.displayName = "TableCell"
+
+const TableCaption = React.forwardRef<
+ HTMLTableCaptionElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+TableCaption.displayName = "TableCaption"
+
+export {
+ Table,
+ TableHeader,
+ TableBody,
+ TableFooter,
+ TableHead,
+ TableRow,
+ TableCell,
+ TableCaption,
+}
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
new file mode 100644
index 0000000..26eb109
--- /dev/null
+++ b/src/components/ui/tabs.tsx
@@ -0,0 +1,55 @@
+"use client"
+
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/test_util/global-setup.ts b/test_util/global-setup.ts
new file mode 100644
index 0000000..60e4443
--- /dev/null
+++ b/test_util/global-setup.ts
@@ -0,0 +1,25 @@
+import { firefox, FullConfig } from '@playwright/test';
+
+async function globalSetup(config: FullConfig) {
+ const email = process.env.NEXT_PUBLIC_DUMMY_EMAIL;
+ const password = process.env.NEXT_PUBLIC_DUMMY_PASSWORD;
+ const baseUrl = 'http://127.0.0.1:3000';
+
+ if (!email || !password) {
+ throw new Error('NEXT_PUBLIC_DUMMY_EMAIL and NEXT_PUBLIC_DUMMY_PASSWORD must be set');
+ }
+
+ const browser = await firefox.launch();
+ const page = await browser.newPage();
+ await page.goto(baseUrl + '/auth');
+ await page.fill('id=email', email);
+ await page.fill('id=password', password);
+ await Promise.all([
+ page.waitForURL(baseUrl),
+ page.click('id=login')
+ ]);
+ await page.context().storageState({ path: 'storageState.json' });
+ await browser.close();
+}
+
+export default globalSetup;
\ No newline at end of file
diff --git a/tests/test-1.spec.ts b/tests/test-1.spec.ts
new file mode 100644
index 0000000..8cbae1b
--- /dev/null
+++ b/tests/test-1.spec.ts
@@ -0,0 +1,23 @@
+import { test, expect } from '@playwright/test';
+
+test.use({
+ storageState: './storageState.json'
+});
+
+test('Test search businesses', async ({ page }) => {
+ await page.goto('http://127.0.0.1:3000/');
+ await page.getByLabel('Main').getByRole('img').click();
+
+ const businessInput = page.getByPlaceholder('Enter business name...');
+ await expect(businessInput).toBeVisible();
+ await businessInput.fill('neon');
+ await businessInput.press('Enter');
+
+ const heading = page.getByRole('heading', { name: 'Neon Solution, A dummy company' });
+ await expect(heading).toBeVisible();
+ await heading.click();
+
+ const fundSection = page.locator('div').filter({ hasText: /^Neon raising fund #1$/ });
+ await expect(fundSection).toBeVisible();
+ await fundSection.click();
+});
diff --git a/tests/test-2.spec.ts b/tests/test-2.spec.ts
new file mode 100644
index 0000000..61d200a
--- /dev/null
+++ b/tests/test-2.spec.ts
@@ -0,0 +1,30 @@
+import { test, expect } from '@playwright/test';
+
+test.use({
+ storageState: './storageState.json'
+});
+
+test('Test filter with tags', async ({ page }) => {
+ await page.goto('http://127.0.0.1:3000/');
+ await page.getByRole('button', { name: 'Start Investing' }).click();
+
+ await page.locator('button').filter({ hasText: 'Tags' }).click();
+ await page.getByLabel('AI', { exact: true }).click();
+ await page.locator('span#tag', { hasText: 'AI' });
+
+ await page.locator('button').filter({ hasText: 'AI' }).click();
+ await page.getByLabel('Technology').click();
+ await page.locator('span#tag', { hasText: 'Technology' });
+
+ await page.locator('button').filter({ hasText: 'Technology' }).click();
+ await page.getByText('Consumer Electronics').click();
+ await page.locator('span#tag', { hasText: 'Consumer Electronics' });
+
+ await page.locator('button').filter({ hasText: 'Consumer Electronics' }).click();
+ await page.getByLabel('Software').click();
+ await page.locator('span#tag', { hasText: 'Software' });
+
+ await page.locator('button').filter({ hasText: 'Software' }).click();
+ await page.getByLabel('Internet').click();
+ await page.locator('span#tag', { hasText: 'Internet' });
+});
\ No newline at end of file
diff --git a/tests/test-3.spec.ts b/tests/test-3.spec.ts
new file mode 100644
index 0000000..7ed4a95
--- /dev/null
+++ b/tests/test-3.spec.ts
@@ -0,0 +1,26 @@
+import { test, expect } from '@playwright/test';
+test.use({
+ storageState: './storageState.json'
+});
+
+test('Test dashboard visibility', async ({ page }) => {
+ await page.goto('http://127.0.0.1:3000/dashboard');
+
+ const dashboardHeading = page.locator('h2', { hasText: 'Dashboard' });
+ await expect(dashboardHeading).toBeVisible();
+
+ const profileViewHeading = page.locator('h3', { hasText: 'Profile Views' });
+ await expect(profileViewHeading).toBeVisible();
+
+ const totalFollowerHeading = page.locator('h3', { hasText: 'Total Followers' });
+ await expect(totalFollowerHeading).toBeVisible();
+
+ const fundsRaisedHeading = page.locator('h3', { hasText: 'Total Funds Raised' });
+ await expect(fundsRaisedHeading).toBeVisible();
+
+ const overviewHeading = page.locator('h3', { hasText: 'Overview' });
+ await expect(overviewHeading).toBeVisible();
+
+ const recentFundHeading = page.locator('h3', { hasText: 'Recent Funds' });
+ await expect(recentFundHeading).toBeVisible();
+});
diff --git a/tests/test-4.spec.ts b/tests/test-4.spec.ts
new file mode 100644
index 0000000..da60f32
--- /dev/null
+++ b/tests/test-4.spec.ts
@@ -0,0 +1,107 @@
+import { test, expect, Page } from '@playwright/test';
+
+test.use({
+ storageState: './storageState.json',
+});
+
+test('Investment process test', async ({ page }) => {
+ await page.goto('http://127.0.0.1:3000/');
+
+ // Navigate to the investment page
+ // await page.getByRole('link', { name: 'Card image NVDA Founded in' }).click();
+ await page.click('a[href="/invest"]');
+ await page.getByRole('button', { name: 'Invest in NVIDIA' }).click();
+
+ // Fill investment amount
+ await fillInvestmentAmount(page, '10000');
+
+ // Fill card information
+ await fillCardInformation(page, {
+ name: 'Dummy',
+ city: 'Bangkok',
+ cardNumber: '4111 1111 1111 1111',
+ expirationMonth: 'August',
+ expirationYear: '2032',
+ cvc: '111',
+ });
+
+ // Accept terms
+ await acceptTerms(page, [
+ 'Minimum Investment',
+ 'Investment Horizon',
+ 'Fees',
+ 'Returns',
+ ]);
+
+ // Click Invest button and confirm
+ await page.getByRole('button', { name: 'Invest' }).click();
+ await page.getByRole('button', { name: 'Confirm' }).click();
+
+ // Ensure error message is displayed when not all terms are accepted
+ await ensureErrorMessageDisplayed(page, 'Please accept all terms');
+
+ // Close the error dialog
+ await closeErrorDialog(page);
+
+ // Accept remaining terms
+ await acceptTerms(page, [
+ 'Risk Disclosure',
+ 'Withdrawal Policy',
+ ]);
+
+ // Click Invest button and confirm again
+ await page.getByRole('button', { name: 'Invest' }).click();
+ await page.getByRole('button', { name: 'Confirm' }).click();
+
+ // Ensure that success toast is displayed when investment is successful
+ await expect(
+ page.locator('div[role="status"][aria-live="polite"]').filter({ hasText: /^You successfully invested!$/ })
+ ).toBeVisible();
+
+// Helper functions
+async function fillInvestmentAmount(page: Page, amount: string): Promise {
+ await page.getByPlaceholder('min $').click();
+ await page.getByPlaceholder('min $').fill(amount);
+}
+
+interface CardInfo {
+ name: string;
+ city: string;
+ cardNumber: string;
+ expirationMonth: string;
+ expirationYear: string;
+ cvc: string;
+}
+
+async function fillCardInformation(
+ page: Page,
+ { name, city, cardNumber, expirationMonth, expirationYear, cvc }: CardInfo
+): Promise {
+ await page.getByPlaceholder('First Last').click();
+ await page.getByPlaceholder('First Last').fill(name);
+ await page.getByLabel('City').click();
+ await page.getByLabel('City').fill(city);
+ await page.getByLabel('Card number').click();
+ await page.getByLabel('Card number').fill(cardNumber);
+ await page.getByLabel('Month').click();
+ await page.getByText(expirationMonth).click();
+ await page.getByLabel('Year').click();
+ await page.getByLabel(expirationYear).click();
+ await page.getByPlaceholder('CVC').click();
+ await page.getByPlaceholder('CVC').fill(cvc);
+}
+
+async function acceptTerms(page: Page, terms: string[]): Promise {
+ for (const term of terms) {
+ await page.getByRole('row', { name: new RegExp(term) }).getByRole('checkbox').check();
+ }
+}
+
+async function ensureErrorMessageDisplayed(page: Page, message: string): Promise {
+ await expect(page.getByText(message)).toBeVisible();
+}
+
+async function closeErrorDialog(page: Page): Promise {
+ await page.getByRole('button', { name: 'Close' }).first().click();
+}
+});
\ No newline at end of file
{props.description}
+{props.description}
-
- ${props.totalRaised.toLocaleString()}{" "} - committed and reserved +
+ ${props.totalRaised.toLocaleString()} committed and reserved
-
- {props.totalInvestor.toLocaleString()}{" "} - investors +
+ {props.totalInvestor.toLocaleString()} investors
-
- ${props.minInvestment.toLocaleString()} min. - investment +
+ ${props.minInvestment.toLocaleString()} min. investment
{person.name}
+{person.email}
+
+