Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/[lang]/(hyperjump)/jobs/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default async function JobDetail({ params }: JobDetailProps) {

return (
<section className="container mx-auto max-w-5xl border-b px-4 py-8 pt-20 text-black md:px-20 xl:px-0">
<div className="flex flex-col space-y-8 py-12">
<div className="flex flex-col space-y-8 py-12" data-testid="job-detail">
<div>
<p className="text-gray-500">{job.category}</p>
<h1 className="text-5xl font-bold text-gray-800">{job.title}</h1>
Expand Down
12 changes: 11 additions & 1 deletion app/[lang]/(hyperjump)/jobs/data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
export const data = {
export type Job = {
id: string;
title: string;
category: string;
description: string;
responsibilities: string[];
requirements: string[];
deliverables: string[];
};

export const data: { jobs: Job[] } = {
jobs: [
{
id: "se-frontend",
Expand Down
61 changes: 52 additions & 9 deletions app/[lang]/(hyperjump)/jobs/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import {
supportedLanguages,
type SupportedLanguage
} from "@/locales/.generated/types";
import Link from "next/link";

import GridItemsContainer, {
GridItems,
GridItemsTitle
} from "@/app/components/grid-items";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from "@/components/ui/card";
import type { SupportedLanguage } from "@/locales/.generated/types";
import { supportedLanguages } from "@/locales/.generated/types";

import { type Job } from "./data";
import { data } from "./data";

export const generateStaticParams = async () => {
Expand All @@ -18,14 +25,50 @@ type JobProps = {
};

export default async function Home({ params }: JobProps) {
const { lang } = await params;

return (
<GridItemsContainer className="pt-10">
<GridItemsTitle title="Available Positions" />
<div className="mt-5" />
<GridItems
items={data.jobs.map((job) => ({ ...job, url: `/jobs/${job.id}` }))}
lang={(await params).lang}
/>
<JobCards items={data.jobs} lang={lang} />
</GridItemsContainer>
);
}

type JobCardProps = {
items: Job[];
lang: SupportedLanguage;
};

function JobCards({ items, lang }: JobCardProps) {
return (
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-3">
{items.map(({ id, category, description, title }) => (
<Card
key={id}
data-testid={`job-card-${id}`}
className="flex flex-col overflow-hidden rounded-2xl border-[#D9D9D9] bg-white transition-colors duration-300 ease-in-out hover:bg-white/5 hover:shadow-md hover:shadow-white/10">
<CardHeader>
<p className="bg-hyperjump-black/10 text-hyperjump-black mb-2 w-36 rounded-3xl px-2 py-1.5 text-center text-sm font-medium">
{category}
</p>
<Link
href={`/${lang}/jobs/${id}`}
className="transition hover:underline">
<CardTitle className="text-hyperjump-black text-xl font-semibold md:text-[22px]">
{title}
</CardTitle>
</Link>
</CardHeader>

<CardContent className="-mt-3 flex flex-1 flex-col justify-between gap-4">
<CardDescription className="text-base font-medium transition-all duration-300">
{description}
</CardDescription>
</CardContent>
</Card>
))}
</div>
);
}
305 changes: 131 additions & 174 deletions bun.lock

Large diffs are not rendered by default.

107 changes: 107 additions & 0 deletions e2e/job.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { expect, test } from "@playwright/test";
import { data } from "@/app/[lang]/(hyperjump)/jobs/data";
import { supportedLanguages } from "@/locales/.generated/types";
import {
BASE_URL,
footerTest,
gotoAndWait,
headerTest,
imagesTest,
languageSwitcherTest,
metaTest,
responsiveTest
} from "./shared-test";

for (const locale of supportedLanguages) {
const path = `/${locale}/jobs`;

test.describe("Job page", () => {
test.describe(`${locale.toUpperCase()} locale`, () => {
test.beforeEach(async ({ page }) => {
await gotoAndWait(page, `${BASE_URL}${path}`);
});
test.describe("Header", headerTest(locale, path));
test.describe("Language Switching", languageSwitcherTest(locale, path));
test.describe("Images", imagesTest());
test.describe("Footer", footerTest(locale));
test.describe("Meta title and description should exist", metaTest());
test.describe("Responsive Design", responsiveTest(path));
test.describe("Job links", () => {
for (const {
category,
deliverables,
description,
id,
requirements,
responsibilities,
title
} of data.jobs) {
test(`Job ${title} (${category}) should be visible and link to detail page`, async ({
page
}) => {
// job list page
await expect(
page
.getByTestId(`job-card-${id}`)
.getByText(category, { exact: true })
).toBeVisible();
await expect(
page
.getByTestId(`job-card-${id}`)
.getByText(description, { exact: true })
).toBeVisible();

// click on the job link
await page
.getByTestId(`job-card-${id}`)
.getByRole("link", { name: title })
.click();
await page.waitForURL(new RegExp(`${path}/${id}`));

// job detail page
await expect(
page
.getByTestId("job-detail")
.getByText(category, { exact: true })
).toBeVisible();
await expect(
page.getByRole("heading", { name: title })
).toBeVisible();
await expect(
page
.getByTestId("job-detail")
.getByText(description, { exact: true })
).toBeVisible();
for (const responsibility of responsibilities) {
await expect(
page
.getByTestId("job-detail")
.getByText(responsibility, { exact: true })
).toBeVisible();
}
for (const requirement of requirements) {
await expect(
page
.getByTestId("job-detail")
.getByText(requirement, { exact: true })
).toBeVisible();
}
for (const deliverable of deliverables) {
await expect(
page
.getByTestId("job-detail")
.getByText(deliverable, { exact: true })
).toBeVisible();
}
await expect(
page.getByRole("link", { name: "Apply" })
).toBeVisible();

await page.goBack();
await page.waitForURL(new RegExp(path));
});
}
});
});
});
}
Loading