Initial user page e2e tests

This commit is contained in:
Kalle 2022-12-12 22:25:41 +02:00
parent 74f6d28c39
commit 23e78f7033
7 changed files with 87 additions and 6 deletions

View File

@ -2,6 +2,7 @@ import clsx from "clsx";
export function Input({ export function Input({
name, name,
id,
className, className,
minLength, minLength,
maxLength, maxLength,
@ -11,10 +12,12 @@ export function Input({
pattern, pattern,
list, list,
"data-cy": dataCy, "data-cy": dataCy,
"aria-label": ariaLabel,
value, value,
onChange, onChange,
}: { }: {
name?: string; name?: string;
id?: string;
className?: string; className?: string;
minLength?: number; minLength?: number;
maxLength?: number; maxLength?: number;
@ -24,6 +27,7 @@ export function Input({
pattern?: string; pattern?: string;
list?: string; list?: string;
"data-cy"?: string; "data-cy"?: string;
"aria-label"?: string;
value?: string; value?: string;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}) { }) {
@ -32,6 +36,7 @@ export function Input({
{leftAddon ? <div className="input-addon">{leftAddon}</div> : null} {leftAddon ? <div className="input-addon">{leftAddon}</div> : null}
<input <input
name={name} name={name}
id={id}
minLength={minLength} minLength={minLength}
maxLength={maxLength} maxLength={maxLength}
defaultValue={defaultValue} defaultValue={defaultValue}
@ -40,6 +45,7 @@ export function Input({
data-cy={dataCy} data-cy={dataCy}
value={value} value={value}
onChange={onChange} onChange={onChange}
aria-label={ariaLabel}
/> />
{icon} {icon}
</div> </div>

View File

@ -211,6 +211,7 @@ function CustomUrlInput({
<Label htmlFor="customUrl">{t("user:customUrl")}</Label> <Label htmlFor="customUrl">{t("user:customUrl")}</Label>
<Input <Input
name="customUrl" name="customUrl"
id="customUrl"
leftAddon="https://sendou.ink/u/" leftAddon="https://sendou.ink/u/"
maxLength={USER.CUSTOM_URL_MAX_LENGTH} maxLength={USER.CUSTOM_URL_MAX_LENGTH}
defaultValue={parentRouteData.customUrl ?? undefined} defaultValue={parentRouteData.customUrl ?? undefined}
@ -235,6 +236,7 @@ function InGameNameInputs({
<Input <Input
className="u-edit__in-game-name-text" className="u-edit__in-game-name-text"
name="inGameNameText" name="inGameNameText"
aria-label="In game name"
maxLength={USER.IN_GAME_NAME_TEXT_MAX_LENGTH} maxLength={USER.IN_GAME_NAME_TEXT_MAX_LENGTH}
defaultValue={inGameNameParts?.[0]} defaultValue={inGameNameParts?.[0]}
/> />
@ -242,6 +244,7 @@ function InGameNameInputs({
<Input <Input
className="u-edit__in-game-name-discriminator" className="u-edit__in-game-name-discriminator"
name="inGameNameDiscriminator" name="inGameNameDiscriminator"
aria-label="In game name discriminator"
maxLength={USER.IN_GAME_NAME_DISCRIMINATOR_LENGTH} maxLength={USER.IN_GAME_NAME_DISCRIMINATOR_LENGTH}
pattern="[0-9]{4}" pattern="[0-9]{4}"
defaultValue={inGameNameParts?.[1]} defaultValue={inGameNameParts?.[1]}
@ -265,8 +268,9 @@ function SensSelects({
return ( return (
<div className="stack horizontal md"> <div className="stack horizontal md">
<div> <div>
<Label>{t("user:stickSens")}</Label> <Label htmlFor="stickSens">{t("user:stickSens")}</Label>
<select <select
id="stickSens"
name="stickSens" name="stickSens"
defaultValue={parentRouteData.stickSens ?? undefined} defaultValue={parentRouteData.stickSens ?? undefined}
className="u-edit__sens-select" className="u-edit__sens-select"
@ -281,8 +285,9 @@ function SensSelects({
</div> </div>
<div> <div>
<Label>{t("user:motionSens")}</Label> <Label htmlFor="motionSens">{t("user:motionSens")}</Label>
<select <select
id="motionSens"
name="motionSens" name="motionSens"
defaultValue={parentRouteData.motionSens ?? undefined} defaultValue={parentRouteData.motionSens ?? undefined}
className="u-edit__sens-select" className="u-edit__sens-select"

View File

@ -37,7 +37,9 @@ export default function UserInfoPage() {
{data.country ? ( {data.country ? (
<div className="u__country"> <div className="u__country">
<span className="u__country-emoji">{data.country.emoji}</span>{" "} <span className="u__country-emoji">{data.country.emoji}</span>{" "}
<span className="u__country-name">{data.country.name}</span> <span className="u__country-name" data-testid="country">
{data.country.name}
</span>
</div> </div>
) : null} ) : null}
<div className="u__socials"> <div className="u__socials">

View File

@ -18,3 +18,11 @@ export async function navigate({ page, url }: { page: Page; url: string }) {
await page.goto(url); await page.goto(url);
page.getByTestId("hydrated"); page.getByTestId("hydrated");
} }
export async function seed(page: Page) {
return page.request.post("/seed");
}
export async function impersonate(page: Page, userId = 1) {
return page.request.post(`/auth/impersonate?id=${userId}`);
}

60
e2e/user-page.spec.ts Normal file
View File

@ -0,0 +1,60 @@
import { expect, type Page, test } from "@playwright/test";
import { ADMIN_DISCORD_ID } from "~/constants";
import { impersonate, navigate, seed } from "~/utils/playwright";
import { userPage } from "~/utils/urls";
const goToEditPage = (page: Page) =>
page.getByText("Edit", { exact: true }).click();
const submitEditForm = (page: Page) =>
page.getByText("Save", { exact: true }).click();
test.describe("User page", () => {
test("edits user profile", async ({ page }) => {
await seed(page);
await impersonate(page);
await navigate({
page,
url: userPage({ discordId: ADMIN_DISCORD_ID, customUrl: "sendou" }),
});
const country = page.getByTestId("country");
await expect(country).toHaveText("Finland");
await goToEditPage(page);
await page
.getByRole("textbox", { name: "In game name", exact: true })
.fill("Lean");
await page
.getByRole("textbox", { name: "In game name discriminator" })
.fill("1234");
await page.getByLabel("R-stick sens").selectOption("0");
await page.getByLabel("Motion sens").selectOption("-50");
await page.getByLabel("Country").selectOption("SE");
await page.getByLabel("Bio").type("My awesome bio");
await submitEditForm(page);
await expect(country).toHaveText("Sweden");
await page.getByText("My awesome bio").isVisible();
await page.getByText("Lean#1234").isVisible();
await page.getByText("Stick 0 / Motion -5").isVisible();
});
test("has redirecting custom url", async ({ page }) => {
await seed(page);
await impersonate(page);
await navigate({
page,
url: userPage({ discordId: ADMIN_DISCORD_ID }),
});
// got redirected
await expect(page).toHaveURL(/sendou/);
await goToEditPage(page);
await page.getByLabel("Custom URL").fill("lean");
await submitEditForm(page);
await expect(page).toHaveURL(/lean/);
});
});

View File

@ -29,7 +29,7 @@
"test:unit": "uvu -r tsm -r tsconfig-paths/register -i e2e", "test:unit": "uvu -r tsm -r tsconfig-paths/register -i e2e",
"test:e2e": "npx playwright test", "test:e2e": "npx playwright test",
"checks": "npm run test:unit && npm run lint:styles && npm run lint:ts && npm run prettier:check && npm run typecheck", "checks": "npm run test:unit && npm run lint:styles && npm run lint:ts && npm run prettier:check && npm run typecheck",
"cf": "npm run test:unit && npm run check-translation-jsons && npm run lint:styles -- --fix && npm run lint:ts -- --fix && npm run prettier:write && npm run typecheck" "cf": "npm run test:unit && npm run check-translation-jsons && npm run lint:styles -- --fix && npm run lint:ts -- --fix && npm run prettier:write && npm run typecheck && npm run test:e2e"
}, },
"dependencies": { "dependencies": {
"@faker-js/faker": "^7.6.0", "@faker-js/faker": "^7.6.0",

View File

@ -27,8 +27,8 @@ const config: PlaywrightTestConfig = {
forbidOnly: !!process.env["CI"], forbidOnly: !!process.env["CI"],
/* Retry on CI only */ /* Retry on CI only */
retries: process.env["CI"] ? 2 : 0, retries: process.env["CI"] ? 2 : 0,
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests. */
workers: process.env["CI"] ? 1 : undefined, workers: 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "list", reporter: "list",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */