Add jest E2E testing

This commit is contained in:
aronmal 2023-07-10 19:36:12 +02:00
parent 0291e6656e
commit 1aaf2ba593
Signed by: aronmal
GPG key ID: 816B7707426FC612
12 changed files with 2615 additions and 87 deletions

View file

@ -1,5 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
__tests__/screenshots/*
# logs
/log

View file

@ -0,0 +1,70 @@
describe("Azure AD", () => {
it("Go to MS sign in", async () => {
await page.goto("https://leaky-ships.mal-noh.de/")
await page.screenshot({ path: "__tests__/screenshots/a.png" })
await page.click("#start")
await page.screenshot({ path: "__tests__/screenshots/b.png" })
await page.waitForSelector("#login")
await page.screenshot({ path: "__tests__/screenshots/c.png" })
await page.click("#login")
await page.screenshot({ path: "__tests__/screenshots/d.png" })
await page.waitForSelector("#microsoft")
await page.screenshot({ path: "__tests__/screenshots/e.png" })
await page.click("#microsoft")
await page.screenshot({ path: "__tests__/screenshots/f.png" })
}, 10000)
it("MS sign in", async () => {
// Listen for frame navigations and log the URL changes
// page.on("framenavigated", (frame) => {
// const frameUrl = frame.url()
// console.log("Window Location Changed:", frameUrl)
// })
await page.screenshot({ path: "__tests__/screenshots/1.png" })
await page.waitForNavigation()
await page.screenshot({ path: "__tests__/screenshots/2.png" })
// await page.screenshot({ path: "__tests__/screenshots/4.png" })
const email = process.env.AUTH_EMAIL
const password = process.env.AUTH_PW
console.log(email)
await page.waitForSelector('input[type="email"]')
await page.waitForSelector('input[type="password"]')
await page.screenshot({ path: "__tests__/screenshots/3.png" })
// Find the email input field and type the email
const emailInput = await page.$('input[type="email"]')
await emailInput.type(email)
await page.screenshot({ path: "__tests__/screenshots/4.png" })
await page.waitForSelector('input[value="Next"]')
// Submit the form (if necessary)
const submitInput1 = await page.$('input[type="submit"]')
await submitInput1.click()
await page.screenshot({ path: "__tests__/screenshots/5.png" })
// Find the password input field and type the password
const passwordInput = await page.$('input[type="password"]')
await passwordInput.type(password)
await page.screenshot({ path: "__tests__/screenshots/6.png" })
await page.waitForSelector('input[value="Sign in"]')
// Submit the form (if necessary)
const submitInput = await page.$('input[value="Sign in"]')
await submitInput.click()
await page.screenshot({ path: "__tests__/screenshots/7.png" })
await page.waitForSelector('input[value="No"]')
// Submit the form (if necessary)
const noInput = await page.$('input[value="No"]')
await noInput.click()
await page.screenshot({ path: "__tests__/screenshots/8.png" })
const callbackUrl = "https://leaky-ships.mal-noh.de/"
await page.waitForFunction(`window.location.href === '${callbackUrl}'`)
// await page.waitForNavigation({ waitUntil: "networkidle0" })
await page.screenshot({ path: "__tests__/screenshots/9.png" })
}, 15000)
})

View file

@ -10,6 +10,7 @@ function BurgerMenu({
}) {
return (
<button
id="menu"
className={classNames(
"absolute left-4 top-4 flex h-16 w-16 items-center justify-center rounded-lg border-b-2 border-shield-gray bg-grayish shadow-lg duration-100 active:border-b-0 active:border-t-2 md:left-6 md:top-6 md:h-20 md:w-20 md:rounded-xl md:border-b-4 md:active:border-t-4 lg:left-8 lg:top-8 xl:left-12 xl:top-12 xl:h-24 xl:w-24",
{ "blur-sm": blur }

View file

@ -3,17 +3,18 @@ import {
FontAwesomeIconProps,
} from "@fortawesome/react-fontawesome"
import classNames from "classnames"
import { ReactNode } from "react"
function OptionButton({
id,
icon,
callback,
children,
node,
disabled,
}: {
id: string
icon: FontAwesomeIconProps["icon"]
callback?: () => void
children: ReactNode
node?: JSX.Element
disabled?: boolean
}) {
return (
@ -28,7 +29,7 @@ function OptionButton({
disabled={disabled}
title={!disabled ? "" : "Please login"}
>
<span className="mx-auto">{children}</span>
<span className="mx-auto">{node ? node : id}</span>
<FontAwesomeIcon
className="ml-2 w-10 text-xl sm:ml-12 sm:text-4xl"
icon={icon}

View file

@ -0,0 +1,6 @@
/** @type {import('jest-environment-puppeteer').JestPuppeteerConfig} */
module.exports = {
launch: {
headless: "new",
},
}

View file

@ -0,0 +1,9 @@
module.exports = {
verbose: true,
preset: "jest-puppeteer",
watchPathIgnorePatterns: [
"<rootDir>/(?!__tests__)/", // Exclude all paths except for __tests__
"<rootDir>/(?!\\.next/)/", // Exclude all paths except for .next/
],
setupFiles: ["dotenv/config"],
}

View file

@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"test": "jest --watchAll"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",
@ -52,6 +53,8 @@
"@types/web-bluetooth": "^0.0.16",
"autoprefixer": "^10.4.14",
"eslint-config-prettier": "^8.8.0",
"jest": "^29.6.1",
"jest-puppeteer": "^9.0.0",
"postcss": "^8.4.24",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.2.8",

View file

@ -17,6 +17,7 @@ export default function Home() {
</video>
</div>
<button
id="start"
className="font-farro rounded-lg border-b-4 border-orange-400 bg-warn px-12 pb-4 pt-5 text-2xl font-bold duration-100 active:border-b-0 active:border-t-4 sm:rounded-xl sm:border-b-[6px] sm:px-14 sm:pb-5 sm:pt-6 sm:text-3xl sm:active:border-t-[6px] md:rounded-2xl md:border-b-8 md:px-20 md:pb-6 md:pt-7 md:text-4xl md:active:border-t-8 xl:px-24 xl:pb-8 xl:pt-10 xl:text-5xl"
onClick={() =>
setTimeout(() => {

View file

@ -84,6 +84,7 @@ function Login() {
onChange={(e) => setEmail(e.target.value)}
/>
<button
id="email"
onClick={login("email")}
className="my-1 rounded-lg bg-blue-500 bg-opacity-75 px-10 py-3 text-white shadow-inner drop-shadow-md backdrop-blur-md transition-colors duration-300 hover:bg-blue-600"
>
@ -111,6 +112,7 @@ function Login() {
/>
</a>
<button
id="microsoft"
onClick={login("azure-ad")}
className="flex w-full justify-evenly rounded-lg border border-gray-400 bg-slate-100 px-5 py-3 text-black drop-shadow-md duration-300 hover:bg-slate-200"
>
@ -129,6 +131,7 @@ function Login() {
{errorType && (
<div className="flex flex-col items-center">
<button
id="back"
onClick={() => router.push("/")}
className="mt-10 rounded-lg border-2 border-gray-400 bg-gray-500 bg-opacity-75 px-16 py-2 text-white shadow-inner drop-shadow-md backdrop-blur-md transition-colors duration-300 hover:border-blue-600"
>

View file

@ -27,6 +27,7 @@ function Logout() {
<div className="flex flex-col justify-start gap-4">
<span>Are you sure you want to sign out?</span>
<button
id="signout"
onClick={() => signOut({ callbackUrl: "/" })}
className="rounded-lg bg-blue-500 bg-opacity-75 px-10 py-3 text-white shadow-inner drop-shadow-md backdrop-blur-md transition-colors duration-300 hover:bg-blue-600"
>

View file

@ -140,6 +140,7 @@ export default function Start() {
<div className="flex flex-col items-center rounded-xl border-4 border-black bg-grayish px-4 py-6 shadow-lg sm:mx-8 sm:p-12 md:w-full">
<div className="flex w-full justify-between">
<button
id="back"
className="-mt-2 h-14 w-20 self-start rounded-xl border-b-4 border-shield-gray bg-voidDark text-2xl text-grayish duration-100 active:border-b-0 active:border-t-4 sm:-mt-6 sm:w-40 sm:px-2 sm:text-5xl"
onClick={() =>
setTimeout(() => {
@ -151,6 +152,7 @@ export default function Start() {
</button>
{!session?.user.id && (
<button
id="login"
className="-mt-2 h-14 w-20 self-start rounded-xl border-b-4 border-orange-500 bg-yellow-500 text-2xl active:border-b-0 active:border-t-4 sm:-mt-6 sm:w-40 sm:px-2 sm:text-4xl"
onClick={() =>
setTimeout(() => {
@ -164,13 +166,13 @@ export default function Start() {
</div>
<div className="flex flex-col items-center gap-6 sm:gap-12">
<OptionButton
id="Raum erstellen"
callback={() => gameFetch()}
icon={faPlus}
disabled={!session}
>
Raum erstellen
</OptionButton>
/>
<OptionButton
id="Raum beitreten"
callback={() => {
router.push({
pathname: router.pathname,
@ -179,25 +181,25 @@ export default function Start() {
}}
icon={faUserPlus}
disabled={!session}
>
{query.join && session ? (
<OtpInput
shouldAutoFocus
containerStyle={{ color: "initial" }}
value={otp}
onChange={setOtp}
numInputs={4}
inputType="number"
inputStyle="inputStyle"
placeholder="0000"
renderSeparator={<span>-</span>}
renderInput={(props) => <input {...props} />}
/>
) : (
"Raum beitreten"
)}
</OptionButton>
node={
query.join && session ? (
<OtpInput
shouldAutoFocus
containerStyle={{ color: "initial" }}
value={otp}
onChange={setOtp}
numInputs={4}
inputType="number"
inputStyle="inputStyle"
placeholder="0000"
renderSeparator={<span>-</span>}
renderInput={(props) => <input {...props} />}
/>
) : undefined
}
/>
<OptionButton
id="Zuschauen"
icon={faEye}
callback={() => {
router.push({
@ -205,24 +207,23 @@ export default function Start() {
query: { q: "watch" },
})
}}
>
{query.watch ? (
<OtpInput
shouldAutoFocus
containerStyle={{ color: "initial" }}
value={otp}
onChange={setOtp}
numInputs={4}
inputType="number"
inputStyle="inputStyle"
placeholder="0000"
renderSeparator={<span>-</span>}
renderInput={(props) => <input {...props} />}
/>
) : (
"Zuschauen"
)}
</OptionButton>
node={
query.watch ? (
<OtpInput
shouldAutoFocus
containerStyle={{ color: "initial" }}
value={otp}
onChange={setOtp}
numInputs={4}
inputType="number"
inputStyle="inputStyle"
placeholder="0000"
renderSeparator={<span>-</span>}
renderInput={(props) => <input {...props} />}
/>
) : undefined
}
/>
</div>
</div>
</div>

File diff suppressed because it is too large Load diff