Add jest E2E testing

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

View file

@ -1,5 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
__tests__/screenshots/*
# logs # logs
/log /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 ( return (
<button <button
id="menu"
className={classNames( 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", "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 } { "blur-sm": blur }

View file

@ -3,17 +3,18 @@ import {
FontAwesomeIconProps, FontAwesomeIconProps,
} from "@fortawesome/react-fontawesome" } from "@fortawesome/react-fontawesome"
import classNames from "classnames" import classNames from "classnames"
import { ReactNode } from "react"
function OptionButton({ function OptionButton({
id,
icon, icon,
callback, callback,
children, node,
disabled, disabled,
}: { }: {
id: string
icon: FontAwesomeIconProps["icon"] icon: FontAwesomeIconProps["icon"]
callback?: () => void callback?: () => void
children: ReactNode node?: JSX.Element
disabled?: boolean disabled?: boolean
}) { }) {
return ( return (
@ -28,7 +29,7 @@ function OptionButton({
disabled={disabled} disabled={disabled}
title={!disabled ? "" : "Please login"} title={!disabled ? "" : "Please login"}
> >
<span className="mx-auto">{children}</span> <span className="mx-auto">{node ? node : id}</span>
<FontAwesomeIcon <FontAwesomeIcon
className="ml-2 w-10 text-xl sm:ml-12 sm:text-4xl" className="ml-2 w-10 text-xl sm:ml-12 sm:text-4xl"
icon={icon} 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", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint",
"test": "jest --watchAll"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/fontawesome-svg-core": "^6.4.0",
@ -51,7 +52,10 @@
"@types/react-dom": "^18.2.4", "@types/react-dom": "^18.2.4",
"@types/web-bluetooth": "^0.0.16", "@types/web-bluetooth": "^0.0.16",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"dotenv": "^16.3.1",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
"jest": "^29.6.1",
"jest-puppeteer": "^9.0.0",
"postcss": "^8.4.24", "postcss": "^8.4.24",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.2.8", "prettier-plugin-tailwindcss": "^0.2.8",

View file

@ -17,6 +17,7 @@ export default function Home() {
</video> </video>
</div> </div>
<button <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" 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={() => onClick={() =>
setTimeout(() => { setTimeout(() => {

View file

@ -84,6 +84,7 @@ function Login() {
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
/> />
<button <button
id="email"
onClick={login("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" 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> </a>
<button <button
id="microsoft"
onClick={login("azure-ad")} 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" 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 && ( {errorType && (
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
<button <button
id="back"
onClick={() => router.push("/")} 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" 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"> <div className="flex flex-col justify-start gap-4">
<span>Are you sure you want to sign out?</span> <span>Are you sure you want to sign out?</span>
<button <button
id="signout"
onClick={() => signOut({ callbackUrl: "/" })} 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" 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 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"> <div className="flex w-full justify-between">
<button <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" 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={() => onClick={() =>
setTimeout(() => { setTimeout(() => {
@ -151,6 +152,7 @@ export default function Start() {
</button> </button>
{!session?.user.id && ( {!session?.user.id && (
<button <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" 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={() => onClick={() =>
setTimeout(() => { setTimeout(() => {
@ -164,13 +166,13 @@ export default function Start() {
</div> </div>
<div className="flex flex-col items-center gap-6 sm:gap-12"> <div className="flex flex-col items-center gap-6 sm:gap-12">
<OptionButton <OptionButton
id="Raum erstellen"
callback={() => gameFetch()} callback={() => gameFetch()}
icon={faPlus} icon={faPlus}
disabled={!session} disabled={!session}
> />
Raum erstellen
</OptionButton>
<OptionButton <OptionButton
id="Raum beitreten"
callback={() => { callback={() => {
router.push({ router.push({
pathname: router.pathname, pathname: router.pathname,
@ -179,25 +181,25 @@ export default function Start() {
}} }}
icon={faUserPlus} icon={faUserPlus}
disabled={!session} disabled={!session}
> node={
{query.join && session ? ( query.join && session ? (
<OtpInput <OtpInput
shouldAutoFocus shouldAutoFocus
containerStyle={{ color: "initial" }} containerStyle={{ color: "initial" }}
value={otp} value={otp}
onChange={setOtp} onChange={setOtp}
numInputs={4} numInputs={4}
inputType="number" inputType="number"
inputStyle="inputStyle" inputStyle="inputStyle"
placeholder="0000" placeholder="0000"
renderSeparator={<span>-</span>} renderSeparator={<span>-</span>}
renderInput={(props) => <input {...props} />} renderInput={(props) => <input {...props} />}
/> />
) : ( ) : undefined
"Raum beitreten" }
)} />
</OptionButton>
<OptionButton <OptionButton
id="Zuschauen"
icon={faEye} icon={faEye}
callback={() => { callback={() => {
router.push({ router.push({
@ -205,24 +207,23 @@ export default function Start() {
query: { q: "watch" }, query: { q: "watch" },
}) })
}} }}
> node={
{query.watch ? ( query.watch ? (
<OtpInput <OtpInput
shouldAutoFocus shouldAutoFocus
containerStyle={{ color: "initial" }} containerStyle={{ color: "initial" }}
value={otp} value={otp}
onChange={setOtp} onChange={setOtp}
numInputs={4} numInputs={4}
inputType="number" inputType="number"
inputStyle="inputStyle" inputStyle="inputStyle"
placeholder="0000" placeholder="0000"
renderSeparator={<span>-</span>} renderSeparator={<span>-</span>}
renderInput={(props) => <input {...props} />} renderInput={(props) => <input {...props} />}
/> />
) : ( ) : undefined
"Zuschauen" }
)} />
</OptionButton>
</div> </div>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load diff