Introducing Playwright Fixtures - Users Factory (#2293)
* Fix not able to logout using the logout path * Add users fixture for e2e tests * typo Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
parent
2d2df2d4db
commit
0390ae9ee1
5 changed files with 135 additions and 3 deletions
|
@ -112,7 +112,8 @@ export default function Login({
|
|||
else setErrorMessage(errorMessages[res.error] || t("something_went_wrong"));
|
||||
})
|
||||
.catch(() => setErrorMessage(errorMessages[ErrorCode.InternalServerError]));
|
||||
}}>
|
||||
}}
|
||||
data-testid="login-form">
|
||||
<input defaultValue={csrfToken || undefined} type="hidden" hidden {...form.register("csrfToken")} />
|
||||
|
||||
<div className={classNames("space-y-6", { hidden: twoFactorRequired })}>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { CheckIcon } from "@heroicons/react/outline";
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { useSession, signOut } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
@ -16,6 +17,8 @@ import { ssrInit } from "@server/lib/ssr";
|
|||
type Props = inferSSRProps<typeof getServerSideProps>;
|
||||
|
||||
export default function Logout(props: Props) {
|
||||
const { data: session, status } = useSession();
|
||||
if (status === "authenticated") signOut({ redirect: false });
|
||||
const router = useRouter();
|
||||
useEffect(() => {
|
||||
if (props.query?.survey === "true") {
|
||||
|
|
96
apps/web/playwright/fixtures/users.ts
Normal file
96
apps/web/playwright/fixtures/users.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
import type { Page } from "@playwright/test";
|
||||
import { UserPlan } from "@prisma/client";
|
||||
import type Prisma from "@prisma/client";
|
||||
|
||||
import { hashPassword } from "@calcom/lib/auth";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
export interface UsersFixture {
|
||||
create: (opts?: CustomUserOpts) => Promise<UserFixture>;
|
||||
get: () => UserFixture[];
|
||||
logout: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface UserFixture {
|
||||
id: number;
|
||||
self: () => Promise<Prisma.User>;
|
||||
login: () => Promise<void>;
|
||||
debug: (message: Record<string, any>) => Promise<void>;
|
||||
}
|
||||
|
||||
// An alias for the hard to remember timezones strings
|
||||
export enum TimeZoneE {
|
||||
USA = "America/Phoenix",
|
||||
UK = "Europe/London",
|
||||
}
|
||||
|
||||
// creates a user fixture instance and stores the collection
|
||||
export const createUsersFixture = (page: Page): UsersFixture => {
|
||||
let store = { users: [], page } as { users: UserFixture[]; page: typeof page };
|
||||
return {
|
||||
create: async (opts) => {
|
||||
const user = await prisma.user.create({
|
||||
data: await createUser(opts),
|
||||
});
|
||||
const userFixture = createUserFixture(user, store.page!);
|
||||
store.users.push(userFixture);
|
||||
return userFixture;
|
||||
},
|
||||
get: () => store.users,
|
||||
logout: async () => {
|
||||
await page.goto("/auth/logout");
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// creates the single user fixture
|
||||
const createUserFixture = (user: Prisma.User, page: Page): UserFixture => {
|
||||
const store = { user, page };
|
||||
|
||||
// self is a reflective method that return the Prisma object that references this fixture.
|
||||
const self = async () => (await prisma.user.findUnique({ where: { id: store.user.id } }))!;
|
||||
return {
|
||||
id: user.id,
|
||||
self,
|
||||
login: async () => login(await self(), store.page),
|
||||
// ths is for developemnt only aimed to inject debugging messages in the metadata field of the user
|
||||
debug: async (message) => {
|
||||
await prisma.user.update({ where: { id: store.user.id }, data: { metadata: { debug: message } } });
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
type CustomUserOptsKeys = "username" | "plan" | "completedOnboarding" | "locale";
|
||||
type CustomUserOpts = Partial<Pick<Prisma.User, CustomUserOptsKeys>> & { timeZone?: TimeZoneE };
|
||||
|
||||
// creates the actual user in the db.
|
||||
const createUser = async (opts?: CustomUserOpts) => {
|
||||
// build a unique name for our user
|
||||
const uname =
|
||||
(opts?.username ?? opts?.plan?.toLocaleLowerCase() ?? UserPlan.PRO.toLowerCase()) + "-" + Date.now();
|
||||
return {
|
||||
username: uname,
|
||||
name: (opts?.username ?? opts?.plan ?? UserPlan.PRO).toUpperCase(),
|
||||
plan: opts?.plan ?? UserPlan.PRO,
|
||||
email: `${uname}@example.com`,
|
||||
password: await hashPassword(uname),
|
||||
emailVerified: new Date(),
|
||||
completedOnboarding: opts?.completedOnboarding ?? true,
|
||||
timeZone: opts?.timeZone ?? TimeZoneE.UK,
|
||||
locale: opts?.locale ?? "en",
|
||||
};
|
||||
};
|
||||
|
||||
// login using a replay of an E2E routine.
|
||||
async function login(user: Prisma.User, page: Page) {
|
||||
await page.goto("/auth/logout");
|
||||
await page.goto("/");
|
||||
await page.click('input[name="email"]');
|
||||
await page.fill('input[name="email"]', user.email);
|
||||
await page.press('input[name="email"]', "Tab");
|
||||
await page.fill('input[name="password"]', user.username!);
|
||||
await page.press('input[name="password"]', "Enter");
|
||||
|
||||
// 2 seconds of delay before returning to help the session loading well
|
||||
await page.waitForTimeout(2000);
|
||||
}
|
17
apps/web/playwright/lib/fixtures.ts
Normal file
17
apps/web/playwright/lib/fixtures.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { test as base } from "@playwright/test";
|
||||
|
||||
import { createUsersFixture } from "../fixtures/users";
|
||||
import type { UsersFixture } from "../fixtures/users";
|
||||
|
||||
interface Fixtures {
|
||||
users: UsersFixture;
|
||||
}
|
||||
|
||||
export const test = base.extend<Fixtures>({
|
||||
users: async ({ page }, use, testInfo) => {
|
||||
// instantiate the fixture
|
||||
const usersFixture = createUsersFixture(page);
|
||||
// use the fixture within the test
|
||||
await use(usersFixture);
|
||||
},
|
||||
});
|
|
@ -1,13 +1,28 @@
|
|||
import { test } from "@playwright/test";
|
||||
import { expect } from "@playwright/test";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
|
||||
test.describe("Login tests", () => {
|
||||
// Using logged in state from globalSteup
|
||||
test.use({ storageState: "playwright/artifacts/proStorageState.json" });
|
||||
|
||||
test("login with pro@example.com", async ({ page }) => {
|
||||
test("Login with pro@example.com", async ({ page }) => {
|
||||
// Try to go homepage
|
||||
await page.goto("/");
|
||||
// It should redirect you to the event-types page
|
||||
await page.waitForSelector("[data-testid=event-types]");
|
||||
});
|
||||
|
||||
test("Should logout using the logout path", async ({ page, users }) => {
|
||||
// creates a user and login
|
||||
const pro = await users.create();
|
||||
await pro.login();
|
||||
|
||||
// users.logout() action uses the logout route "/auth/logout" to clear the session
|
||||
await users.logout();
|
||||
|
||||
// check if we are at the login page
|
||||
await page.goto("/");
|
||||
await expect(page.locator(`[data-testid=login-form]`)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue