Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Team management #23

Merged
merged 3 commits into from
Oct 28, 2023
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
16 changes: 12 additions & 4 deletions e2e/applicationForm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ test.describe("application form", () => {
await page
.getByLabel("Label")
.fill("What is your experience with hackathons?");
await page.getByLabel("Name (has to be unique across the form)").fill("experience");
await page
.getByLabel("Name (has to be unique across the form)")
.fill("experience");
await page.getByText("Select a field type").click();
await page.getByLabel("textarea").getByText("textarea").click();
await page.getByLabel("Required").check();
Expand All @@ -74,7 +76,9 @@ test.describe("application form", () => {
await page
.getByLabel("Label")
.fill("I have been at the hackathon in the past.");
await page.getByLabel("Name (has to be unique across the form)").fill("hackathonsPast");
await page
.getByLabel("Name (has to be unique across the form)")
.fill("hackathonsPast");
await page.getByText("Select a field type").click();
await page.getByLabel("checkbox").getByText("checkbox").click();
await page.getByRole("button", { name: "Save new field" }).click();
Expand All @@ -85,7 +89,9 @@ test.describe("application form", () => {

await page.getByRole("button", { name: "Create new field" }).click();
await page.getByLabel("Label").fill("What company do you work for?");
await page.getByLabel("Name (has to be unique across the form)").fill("company");
await page
.getByLabel("Name (has to be unique across the form)")
.fill("company");
await page.getByText("Select a field type").click();
await page.getByLabel("text", { exact: true }).getByText("text").click();
await page.getByRole("button", { name: "Save new field" }).click();
Expand All @@ -107,7 +113,9 @@ test.describe("application form", () => {
await page
.getByLabel("Label")
.fill("I have been at the hackathon in the past.");
await page.getByLabel("Name (has to be unique across the form)").fill("hackathonsPast");
await page
.getByLabel("Name (has to be unique across the form)")
.fill("hackathonsPast");
await page.getByText("Select a field type").click();
await page.getByLabel("checkbox").getByText("checkbox").click();
await page.getByRole("button", { name: "Save new field" }).click();
Expand Down
19 changes: 14 additions & 5 deletions e2e/fixtures/ApplicationPage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Page, Locator } from "@playwright/test";
import { expect } from "@playwright/test";
import type { Page } from "@playwright/test";

export class ApplicationPage {
constructor(public readonly page: Page) {}
Expand All @@ -8,11 +7,21 @@ export class ApplicationPage {
await this.page.goto("/");
}

async openSignedIn() {
async openSignedIn(
{ hackerIndex }: { hackerIndex: number } = { hackerIndex: 1 }
) {
await this.page.getByRole("link", { name: "Sign in" }).click();

await this.page.fill('input[name="email"]', "[email protected]");
await this.page.fill('input[name="password"]', "test123");
if (hackerIndex > 1) {
await this.page.fill(
'input[name="email"]',
`test-hacker-${hackerIndex}@test.com`
);
await this.page.fill('input[name="password"]', "test123");
} else {
await this.page.fill('input[name="email"]', "[email protected]");
await this.page.fill('input[name="password"]', "test123");
}

await this.page.getByRole("button", { name: /^Sign in$/ }).click();
}
Expand Down
36 changes: 35 additions & 1 deletion e2e/helpers/prepareDBBeforeTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ async function clearDb(prisma: PrismaClient) {
await prisma.formField.deleteMany();
await prisma.applicationFormStep.deleteMany();
await prisma.application.deleteMany();
await prisma.team.deleteMany();
await prisma.hacker.deleteMany();
await prisma.organizer.deleteMany();
await prisma.user.deleteMany();
Expand All @@ -16,7 +17,15 @@ async function clearDb(prisma: PrismaClient) {
await prisma.optionList.deleteMany();
}

export async function main(prisma: PrismaClient) {
type Options = {
numberOfHackers?: number;
};
export async function main(
prisma: PrismaClient,
options: Options = {
numberOfHackers: 1,
}
) {
await clearDb(prisma);

const { id: hackathonId } = await prisma.hackathon.create({
Expand Down Expand Up @@ -51,6 +60,31 @@ export async function main(prisma: PrismaClient) {
},
});

if (options.numberOfHackers && options.numberOfHackers > 1) {
for (let i = 0; i < options.numberOfHackers - 1; i++) {
const { id: userId } = await prisma.user.create({
data: {
email: `test-hacker-${i + 2}@test.com`,
password: await hash("test123"),
},
});

const { id: hackerId } = await prisma.hacker.create({
data: {
userId,
hackathonId,
},
});

await prisma.application.create({
data: {
hackerId,
statusId: 1,
},
});
}
}

const { id: userOrganizerId } = await prisma.user.create({
data: {
email: "[email protected]",
Expand Down
203 changes: 203 additions & 0 deletions e2e/team.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { test, expect } from "./fixtures/custom-test";
import { PrismaClient } from "@prisma/client";
import prepareDBBeforeTest from "./helpers/prepareDBBeforeTest";

let teamCode = "";

test.describe("Team", () => {
test.describe.configure({ mode: "serial" });

test.beforeAll(async () => {
const prisma = new PrismaClient();

await prepareDBBeforeTest(prisma, {
numberOfHackers: 2,
});

await prisma.$disconnect();
});

test("unsigned user cannot create a team", async ({
page,
applicationPage,
}) => {
await applicationPage.openUnsigned();

await expect(page.getByText("Your team")).toBeVisible();
await expect(
page.getByText("You can create or join teams after you sign in")
).toBeVisible();
await expect(
page.getByRole("button", { name: "Create new team" })
).not.toBeVisible();

await expect(
page.getByRole("button", { name: "Join existing team" })
).not.toBeVisible();
});

test("signed in hacker can create a team", async ({
page,
applicationPage,
}) => {
await applicationPage.openSignedIn();

await expect(page.getByText("Your team")).toBeVisible();
await expect(
page.getByRole("button", { name: "Create new team" })
).toBeVisible();
await expect(
page.getByRole("button", { name: "Join existing team" })
).toBeVisible();

await page.getByRole("button", { name: "Create new team" }).click();

await expect(
page.getByRole("heading", { name: "Create new team" })
).toBeVisible();
await page.getByLabel("Team name").fill("Best team");
await page.getByRole("button", { name: "Create" }).click();

await expect(page.getByText("Team name:")).toBeVisible();
await expect(page.getByText("Best team")).toBeVisible();
await expect(page.getByText("Team members (1/4):")).toBeVisible();
await expect(page.getByText("[email protected] (owner)")).toBeVisible();
await expect(
page.getByRole("button", { name: "Leave team" })
).not.toBeVisible();

// Editing team name
await page.getByRole("button", { name: "Edit team name" }).click();
await expect(
page.getByRole("heading", { name: "Edit team name" })
).toBeVisible();
await expect(page.getByLabel("Team name", { exact: true })).toHaveValue(
"Best team"
);
await page.getByLabel("Team name", { exact: true }).fill("Test team");
await page.getByRole("button", { name: "Save" }).click();

await expect(page.getByText("Team name:")).toBeVisible();
await expect(page.getByText("Test team")).toBeVisible();
await expect(page.getByText("Best team")).not.toBeVisible();

// Copying team code
await page.getByRole("button", { name: "Copy team code" }).click();
teamCode = await page.evaluate("navigator.clipboard.readText()");
expect(teamCode).not.toBe("");
});

test("signed in hacker can join a team", async ({
page,
applicationPage,
}) => {
await applicationPage.openSignedIn({ hackerIndex: 2 });

await expect(page.getByText("Your team")).toBeVisible();
await expect(
page.getByRole("button", { name: "Join existing team" })
).toBeVisible();

await page.getByRole("button", { name: "Join existing team" }).click();

await expect(
page.getByRole("heading", { name: "Join existing team" })
).toBeVisible();
await page.getByLabel("Team code").fill(teamCode);
await page.getByRole("button", { name: "Join" }).click();

await expect(page.getByText("Team name:")).toBeVisible();
await expect(page.getByText("Test team")).toBeVisible();
await expect(page.getByText("Team members (2/4):")).toBeVisible();
await expect(
page.getByRole("button", { name: "Leave team" })
).toBeVisible();
await expect(page.getByText("[email protected] (owner)")).toBeVisible();
await expect(page.getByText("[email protected]")).toBeVisible();
await expect(page.getByText("Kick")).not.toBeVisible();
});

test("owner can kick a team member", async ({ page, applicationPage }) => {
await applicationPage.openSignedIn();

await expect(page.getByText("Team members (2/4):")).toBeVisible();
await expect(page.getByText("[email protected]")).toBeVisible();

await page.getByRole("button", { name: "Kick" }).click();
await expect(
page.getByText("Are you sure you want to kick [email protected]?")
).toBeVisible();
await page.getByRole("button", { name: "No" }).click();

await expect(
page.getByText("Are you sure you want to kick [email protected]?")
).not.toBeVisible();

await expect(page.getByText("Team members (2/4):")).toBeVisible();
await expect(page.getByText("[email protected]")).toBeVisible();

await page.getByRole("button", { name: "Kick" }).click();
await expect(
page.getByText("Are you sure you want to kick [email protected]?")
).toBeVisible();
await page.getByRole("button", { name: "Yes" }).click();

await expect(page.getByText("Team members (1/4):")).toBeVisible();
await expect(page.getByText("[email protected]")).not.toBeVisible();
});

test("member can leave a team", async ({ page, applicationPage }) => {
await applicationPage.openSignedIn({ hackerIndex: 2 });

await page.getByRole("button", { name: "Join existing team" }).click();
await page.getByLabel("Team code").fill(teamCode);
await page.getByRole("button", { name: "Join" }).click();

await expect(page.getByText("Test team")).toBeVisible();

await page.getByRole("button", { name: "Leave team" }).click();
await expect(
page.getByText("Are you sure you want to leave this team?")
).toBeVisible();
await page.getByRole("button", { name: "No" }).click();

await expect(page.getByText("Test team")).toBeVisible();

await page.getByRole("button", { name: "Leave team" }).click();
await expect(
page.getByText("Are you sure you want to leave this team?")
).toBeVisible();
await page.getByRole("button", { name: "Yes" }).click();

await expect(page.getByText("Test team")).not.toBeVisible();
await expect(
page.getByRole("button", { name: "Join existing team" })
).toBeVisible();
});

test("hacker can join a team after submitting application", async ({
page,
applicationPage,
}) => {
await applicationPage.openSignedIn({ hackerIndex: 2 });

await expect(page.getByText("Application status: open")).toBeVisible();

await page.getByText("General info").click();
await expect(
page.getByRole("heading", { name: "General info" })
).toBeVisible();
await page.getByLabel("Full name").fill("Test Hacker 2");
await page.getByRole("button", { name: "Save" }).click();

await page.getByRole("button", { name: "Submit application" }).click();
await page.getByRole("button", { name: "Yes" }).click();
await expect(page.getByText("Application status: submitted")).toBeVisible();

await page.getByRole("button", { name: "Join existing team" }).click();
await page.getByLabel("Team code").fill(teamCode);
await page.getByRole("button", { name: "Join" }).click();

await expect(page.getByText("Test team")).toBeVisible();
});
});
1 change: 1 addition & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default defineConfig({

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "retry-with-trace",
permissions: ["clipboard-read", "clipboard-write"],
},

/* Configure projects for major browsers */
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const buttonVariants = cva(
secondary:
"bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80",
ghost:
"hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-slate-50",
"text-hkOrange hover:bg-slate-100 dark:hover:bg-slate-800 dark:hover:text-slate-50",
link: "text-slate-900 underline-offset-4 hover:underline dark:text-slate-50",
combobox:
"w-full rounded-md border border-slate-200 bg-white px-3 py-2 text-sm placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-hkOrange disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-800 dark:bg-slate-950 dark:ring-offset-slate-950 dark:placeholder:text-slate-400 dark:focus:ring-slate-300",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function DataTable<TData, TValue>({
/>
</div>
)}
<ScrollArea className="h-[400px]">
<ScrollArea className="max-h-[400px]">
<div className="rounded-md border min-w-full">
<Table>
<TableHeader>
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const stackVariants = cva("flex", {
row: "flex-row",
},
spacing: {
none: "",
small: "gap-2",
medium: "gap-4",
large: "gap-7",
Expand Down
15 changes: 9 additions & 6 deletions src/scenes/Application/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Suspense } from "react";
import ApplicationSteps from "@/scenes/Application/components/ApplicationSteps/ApplicationSteps";
import TeamManager from "@/scenes/Application/components/TeamManager/TeamManager";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Stack } from "@/components/ui/stack";

const Application = () => {
return (
Expand All @@ -10,12 +11,14 @@ const Application = () => {
<CardTitle>Welcome to Hack Kosice Application portal!</CardTitle>
</CardHeader>
<CardContent>
<Suspense fallback={<div>Loading...</div>}>
<ApplicationSteps />
</Suspense>
<Suspense fallback={<div>Loading...</div>}>
<TeamManager />
</Suspense>
<Stack direction="column">
<Suspense fallback={<div>Loading...</div>}>
<ApplicationSteps />
</Suspense>
<Suspense fallback={<div>Loading...</div>}>
<TeamManager />
</Suspense>
</Stack>
</CardContent>
</Card>
);
Expand Down
Loading