Skip to content

Commit

Permalink
tests: implement STF_01 (#988)
Browse files Browse the repository at this point in the history
  • Loading branch information
typeofweb authored Oct 25, 2023
1 parent 1814677 commit 4ddf710
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 76 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ jobs:
build_and_test:
runs-on: ubuntu-latest
env:
# @TODO replace this with proper e2e environment
NEXT_PUBLIC_SALEOR_API_URL: https://zaiste.saleor.cloud/graphql/
NEXT_PUBLIC_SALEOR_API_URL: https://storefront1.saleor.cloud/graphql/

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -59,7 +58,8 @@ jobs:
id: waitForVercel
with:
token: ${{ secrets.GITHUB_TOKEN }}
max_timeout: 120
max_timeout: 240
check_interval: 5

- name: Run Playwright tests
run: pnpm exec playwright test
Expand Down
31 changes: 31 additions & 0 deletions __tests__/STF_01.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { expect, test } from "@playwright/test";
import {
addCurrentProductToCart,
clickOnRandomProductElement,
getCurrentProductPrice,
openCart,
selectRandomAvailableVariant,
} from "./utils";

test("STF_01", async ({ page }) => {
await page.goto("/");

const product = await clickOnRandomProductElement({ page });
await selectRandomAvailableVariant({ page });

const price = await getCurrentProductPrice({ page });

await expect(page.getByTestId("CartNavItem")).toContainText("0 items");
await addCurrentProductToCart({ page });
await expect(page.getByTestId("CartNavItem")).toContainText("1 item");
await addCurrentProductToCart({ page });
await expect(page.getByTestId("CartNavItem")).toContainText("2 items");

await openCart({ page });

const totalPrice = (price * 2).toFixed(2);
await expect(page.getByTestId("CartProductList").getByRole("listitem")).toHaveCount(1);
await expect(page.getByTestId("CartProductList").getByRole("listitem")).toContainText(product.name);
await expect(page.getByTestId("CartProductList").getByRole("listitem")).toContainText(`Qty: 2`);
await expect(page.getByTestId("CartProductList").getByRole("listitem")).toContainText(totalPrice);
});
37 changes: 0 additions & 37 deletions __tests__/example.spec.ts

This file was deleted.

48 changes: 48 additions & 0 deletions __tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { expect, type Page } from "@playwright/test";

export async function clickOnRandomProductElement({ page }: { page: Page }) {
const productLinks = page.getByTestId("ProductElement");
await productLinks.first().waitFor();
const count = await productLinks.count();
const randomProductLink = productLinks.nth(Math.floor(Math.random() * count));

const name = await randomProductLink.getByRole("heading").textContent();
const priceRange = await randomProductLink.getByTestId("ProductElement_PriceRange").textContent();
const category = await randomProductLink.getByTestId("ProductElement_Category").textContent();

await randomProductLink.click();

await page.waitForURL("**/products/*");

expect(name, "Missing product name").toBeTruthy();
expect(priceRange, "Missing product priceRange").toBeTruthy();
expect(category, "Missing product category").toBeTruthy();
return { name: name!, priceRange: priceRange!, category: category! };
}

export async function getCurrentProductPrice({ page }: { page: Page }) {
const price = await page.getByTestId("ProductElement_Price").textContent();
expect(price, "Missing product price").toBeTruthy();
return Number.parseFloat(price!.replace(/[^0-9\.]/g, ""));
}

export async function selectRandomAvailableVariant({ page }: { page: Page }) {
const variant = page.getByTestId("VariantSelector").getByRole("radio", { disabled: false });
const count = await variant.count();
// some products only have a single variant
if (count > 0) {
await variant.nth(Math.floor(Math.random() * count)).click();
}
await page.waitForURL(/\?variant=.+/);
}

export async function addCurrentProductToCart({ page }: { page: Page }) {
expect(page.url()).toContain("/products/");
expect(page.url()).toContain("?variant=");
await page.getByRole("button", { name: "Add to cart" }).click();
}

export async function openCart({ page }: { page: Page }) {
await page.getByTestId("CartNavItem").click();
await page.getByText("Your Shopping Cart").waitFor();
}
8 changes: 6 additions & 2 deletions src/app/(main)/cart/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ export default async function Page() {
<h1 className="mt-8 text-3xl font-bold text-neutral-900">Your Shopping Cart</h1>
<form className="mt-12">
<div>
<ul role="list" className="divide-y divide-neutral-200 border-b border-t border-neutral-200">
<ul
data-testid="CartProductList"
role="list"
className="divide-y divide-neutral-200 border-b border-t border-neutral-200"
>
{lines.map((item) => (
<li key={item.id} className="flex py-4">
<div className="aspect-square h-24 w-24 flex-shrink-0 overflow-hidden rounded-md border bg-neutral-50 sm:h-32 sm:w-32">
Expand All @@ -39,7 +43,7 @@ export default async function Page() {
<div className="flex justify-between justify-items-start gap-4">
<div className="">
<Link href={`/products/${item.variant.product.slug}?variant=${item.variant.id}`}>
<h3 className="font-medium text-neutral-700">{item.variant?.product?.name}</h3>
<h2 className="font-medium text-neutral-700">{item.variant?.product?.name}</h2>
</Link>
<p className="mt-1 text-sm text-neutral-500">{item.variant?.product?.category?.name}</p>
{item.variant.name !== item.variant.id && Boolean(item.variant.name) && (
Expand Down
4 changes: 3 additions & 1 deletion src/app/(main)/products/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ export default async function Page(props: { params: { slug: string }; searchPara
<h1 className="mb-4 flex-auto text-3xl font-bold tracking-tight text-neutral-900">
{product?.name}
</h1>
<p className="mb-8 text-sm font-medium text-neutral-900">{price}</p>
<p className="mb-8 text-sm font-medium text-neutral-900" data-testid="ProductElement_Price">
{price}
</p>

{variants && (
<VariantSelector selectedVariant={selectedVariant} variants={variants} product={product} />
Expand Down
3 changes: 3 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Saleor Storefront example",
description: "Starter pack for building performant e-commerce experiences with Saleor.",
metadataBase: process.env.NEXT_PUBLIC_STOREFRONT_URL
? new URL(process.env.NEXT_PUBLIC_STOREFRONT_URL)
: undefined,
};

export default function RootLayout(props: { children: ReactNode }) {
Expand Down
6 changes: 4 additions & 2 deletions src/ui/components/ProductElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ export function ProductElement({
<div className="mt-2 flex justify-between">
<div>
<h3 className="mt-1 text-sm font-semibold text-neutral-900">{product.name}</h3>
<p className="mt-1 text-sm text-neutral-500">{product?.category?.name}</p>
<p className="mt-1 text-sm text-neutral-500" data-testid="ProductElement_Category">
{product.category?.name}
</p>
</div>
<p className="mt-1 text-sm font-medium text-neutral-900">
<p className="mt-1 text-sm font-medium text-neutral-900" data-testid="ProductElement_PriceRange">
{formatMoneyRange({
start: product?.pricing?.priceRange?.start?.gross,
stop: product?.pricing?.priceRange?.stop?.gross,
Expand Down
8 changes: 5 additions & 3 deletions src/ui/components/nav/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ export const Nav = () => {
<CartNavItem />
</Suspense>
</div>
<MobileMenu>
<NavLinks />
</MobileMenu>
<Suspense>
<MobileMenu>
<NavLinks />
</MobileMenu>
</Suspense>
</nav>
);
};
7 changes: 3 additions & 4 deletions src/ui/components/nav/components/CartNavItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const CartNavItem = async () => {
const lineCount = checkout ? checkout.lines.reduce((result, line) => result + line.quantity, 0) : 0;

return (
<Link href="/cart" className="relative flex items-center">
<Link href="/cart" className="relative flex items-center" data-testid="CartNavItem">
<ShoppingBagIcon className="h-6 w-6 shrink-0" aria-hidden="true" />
{lineCount > 0 ? (
<div
Expand All @@ -20,11 +20,10 @@ export const CartNavItem = async () => {
lineCount > 9 ? "w-[3ch]" : "w-[2ch]",
)}
>
{lineCount}
<span className="sr-only">items in cart, view bag</span>
{lineCount} <span className="sr-only">item{lineCount > 1 ? "s" : ""} in cart, view bag</span>
</div>
) : (
<span className="sr-only">cart</span>
<span className="sr-only">0 items in cart</span>
)}
</Link>
);
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/nav/components/MobileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Props = {
};

export const MobileMenu = ({ children }: Props) => {
const { closeMenu, openMenu, isOpen } = useMobileMenu({ breakpoint: 768 });
const { closeMenu, openMenu, isOpen } = useMobileMenu();

return (
<>
Expand Down
37 changes: 14 additions & 23 deletions src/ui/components/nav/components/useMobileMenu.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
import { useMemo, useEffect, useState, useCallback } from "react";
import { useEffect, useState, useCallback } from "react";
import { usePathname, useSearchParams } from "next/navigation";

type Args = {
breakpoint: number;
};

export const useMobileMenu = ({ breakpoint }: Args) => {
export const useMobileMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const pathname = usePathname();
const searchParams = useSearchParams();
useSearchParams();

useEffect(() => {
const handleResize = () => {
if (window.innerWidth > breakpoint) {
setIsOpen(false);
}, [pathname]);

useEffect(() => {
const handleResize = (ev: MediaQueryListEvent) => {
if (ev.matches) {
setIsOpen(false);
}
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [breakpoint, isOpen]);

useEffect(() => {
setIsOpen(false);
}, [pathname, searchParams]);
const matchMedia = window.matchMedia(`(min-width: 768px)`);
matchMedia.addEventListener("change", handleResize, { passive: true });
return () => matchMedia.removeEventListener("change", handleResize);
}, []);

const closeMenu = useCallback(() => setIsOpen(false), []);
const openMenu = useCallback(() => setIsOpen(true), []);

return useMemo(
() => ({
isOpen,
closeMenu,
openMenu,
}),
[closeMenu, isOpen, openMenu],
);
return { isOpen, closeMenu, openMenu };
};

0 comments on commit 4ddf710

Please sign in to comment.