diff --git a/.env.example b/.env.example index a16f82a..ecf6629 100644 --- a/.env.example +++ b/.env.example @@ -1,23 +1,13 @@ -# Since the ".env" file is gitignored, you can use the ".env.example" file to -# build a new ".env" file when you clone the repo. Keep this file up-to-date -# when you add new variables to `.env`. +DATABASE_URL="" -# This file will be committed to version control, so make sure not to have any -# secrets in it. If you are cloning this repo, create a copy of this file named -# ".env" and populate it with your secrets. +NEXT_PUBLIC_SITE_URL="" -# When adding additional environment variables, the schema in "/src/env.mjs" -# should be updated accordingly. - -# Prisma -# https://www.prisma.io/docs/reference/database-reference/connection-urls#env -DATABASE_URL="file:./db.sqlite" - -# Next Auth -# You can generate a new secret on the command line with: -# openssl rand -base64 32 -# https://next-auth.js.org/configuration/options#secret -# NEXTAUTH_SECRET="" -NEXTAUTH_URL="http://localhost:3000" +AUTH0_CLIENT_ID="" +AUTH0_CLIENT_SECRET="" +AUTH0_ISSUER="" +CYPRESS_TEST_USER_EMAIL="" +CYPRESS_TEST_USER_PASSWORD="" +TEST_USER_EMAIL="" +TEST_USER_PASSWORD="" \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index daa4df9..ab1b798 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + # Write the cypress.env.json file + - name: Write the cypress.env.json file 📝 + run: | + echo '${{ secrets.CYPRESS_ENV_CI }}' > cypress.env.json && cat cypress.env.json # Install NPM dependencies, cache them correctly # and run all Cypress tests - name: Cypress run @@ -25,3 +29,6 @@ jobs: AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }} AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }} AUTH0_ISSUER: ${{ secrets.AUTH0_ISSUER }} + TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }} + TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }} + CYPRESS_ENV_CI: ${{ secrets.CYPRESS_ENV_CI }} diff --git a/.gitignore b/.gitignore index f54bd33..c9388fd 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,5 @@ yarn-error.log* cypress/videos cypress/screenshots +cypress/downloads +cypress.env.json diff --git a/cypress.config.ts b/cypress.config.ts index b859024..3f443dc 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,9 +1,6 @@ import { defineConfig } from "cypress"; export default defineConfig({ - env: { - NEXT_PUBLIC_SITE_URL: "http://localhost:3000", - }, viewportHeight: 1000, e2e: { setupNodeEvents(on, config) { diff --git a/cypress/e2e/study/controls.cy.ts b/cypress/e2e/study/controls.cy.ts index 9e0a902..3f58e23 100644 --- a/cypress/e2e/study/controls.cy.ts +++ b/cypress/e2e/study/controls.cy.ts @@ -2,6 +2,7 @@ import gamesBySquares from "../../utils/gamesBySquares"; describe("Check that controls move around", () => { beforeEach(() => { + cy.login(); cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}/study`); cy.playGame(gamesBySquares.shortExample); }); diff --git a/cypress/e2e/study/fen.cy.ts b/cypress/e2e/study/fen.cy.ts index b947833..a3fee6e 100644 --- a/cypress/e2e/study/fen.cy.ts +++ b/cypress/e2e/study/fen.cy.ts @@ -2,6 +2,7 @@ import gamesBySquares from "../../utils/gamesBySquares"; describe("Check that controls move around", () => { beforeEach(() => { + cy.login(); cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}/study`); }); diff --git a/cypress/e2e/study/moves.cy.ts b/cypress/e2e/study/moves.cy.ts index cefe18a..f14ddeb 100644 --- a/cypress/e2e/study/moves.cy.ts +++ b/cypress/e2e/study/moves.cy.ts @@ -2,6 +2,7 @@ import gamesBySquares from "../../utils/gamesBySquares"; describe("Check that PgnViewer component works as expected", () => { beforeEach(() => { + cy.login(); cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}/study`); }); diff --git a/cypress/e2e/study/pgnTools/copyPgn.cy.ts b/cypress/e2e/study/pgnTools/copyPgn.cy.ts index 546cde9..fd876bc 100644 --- a/cypress/e2e/study/pgnTools/copyPgn.cy.ts +++ b/cypress/e2e/study/pgnTools/copyPgn.cy.ts @@ -2,6 +2,7 @@ import gamesBySquares from "../../../utils/gamesBySquares"; describe("Check functioning of PGN copying", () => { beforeEach(() => { + cy.login(); cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}/study`); }); diff --git a/cypress/e2e/study/pgnTools/downloadPgn.cy.ts b/cypress/e2e/study/pgnTools/downloadPgn.cy.ts index 1812fd1..3d48c7b 100644 --- a/cypress/e2e/study/pgnTools/downloadPgn.cy.ts +++ b/cypress/e2e/study/pgnTools/downloadPgn.cy.ts @@ -2,6 +2,7 @@ import gamesBySquares from "../../../utils/gamesBySquares"; describe("Check functioning of PGN download", () => { beforeEach(() => { + cy.login(); cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}/study`); }); @@ -14,7 +15,7 @@ describe("Check functioning of PGN download", () => { cy.data("download-pgn").should("be.enabled"); }); - it.only("Should copy PGN to clipboard", () => { + it("Should copy PGN to clipboard", () => { cy.playGame(gamesBySquares.shortExample); cy.data("pgn-tools-menu").click(); cy.data("download-pgn").click(); diff --git a/cypress/e2e/study/pgnViewer.cy.ts b/cypress/e2e/study/pgnViewer.cy.ts index 0fd372d..733d604 100644 --- a/cypress/e2e/study/pgnViewer.cy.ts +++ b/cypress/e2e/study/pgnViewer.cy.ts @@ -2,6 +2,7 @@ import gamesBySquares from "../../utils/gamesBySquares"; describe("Check that PgnViewer shows correct moves", () => { beforeEach(() => { + cy.login(); cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}/study`); }); diff --git a/cypress/e2e/study/study.cy.ts b/cypress/e2e/study/study.cy.ts index 0bfd47a..1cf46dd 100644 --- a/cypress/e2e/study/study.cy.ts +++ b/cypress/e2e/study/study.cy.ts @@ -2,6 +2,7 @@ import gamesBySquares from "../../utils/gamesBySquares"; describe("Check basic moves in study", () => { beforeEach(() => { + cy.login(); cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}/study`); }); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 1ff5c88..606aaae 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -15,6 +15,7 @@ declare global { testBoardStore(): Chainable; testStudyStore(): Chainable; assertValueCopiedToClipboard(value: string): void; + login(): void; } } } @@ -60,3 +61,30 @@ Cypress.Commands.add("assertValueCopiedToClipboard", (value) => { cy.wrap(win.navigator.clipboard.readText()).should("eq", value); }); }); + +Cypress.Commands.add("login", () => { + cy.session( + "login", + () => { + console.log( + "logging in!", + Cypress.env("TEST_USER_EMAIL"), + Cypress.env("TEST_USER_PASSWORD") + ); + cy.visit(`${Cypress.env("NEXT_PUBLIC_SITE_URL")}`); + cy.data("sign-in-out").click(); + cy.get("button").contains("Sign in with Auth0").click(); + cy.origin("https://opening-expert.us.auth0.com", () => { + cy.get("#username").type(Cypress.env("TEST_USER_EMAIL")); + cy.get("#password") + .type(Cypress.env("TEST_USER_PASSWORD")) + .type("{enter}"); + }); + }, + { + validate: () => { + cy.getCookie("next-auth.session-token").should("exist"); + }, + } + ); +}); diff --git a/src/components/Study/Study.tsx b/src/components/Study/Study.tsx index e522d42..910e8f8 100644 --- a/src/components/Study/Study.tsx +++ b/src/components/Study/Study.tsx @@ -2,8 +2,14 @@ import { HStack } from "@chakra-ui/react"; import PgnViewer from "~/components/PgnViewer/PgnViewer"; import Board from "~/components/Board/Board"; import PositionInfo from "~/components/PositionInfo/PositionInfo"; +import useStudyStore from "~/state/study"; +import { useEffect } from "react"; const Study = () => { + const { loadPGN } = useStudyStore(); + useEffect(() => { + //loadPGN("1. e4 h5 (1... a5) 2. f4"); + }, []); return ( <> diff --git a/src/env.mjs b/src/env.mjs index 7d713ec..87d3de0 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -9,17 +9,6 @@ export const env = createEnv({ server: { DATABASE_URL: z.string(), NODE_ENV: z.enum(["development", "test", "production"]), - NEXTAUTH_SECRET: - process.env.NODE_ENV === "production" - ? z.string().min(1) - : z.string().min(1).optional(), - NEXTAUTH_URL: z.preprocess( - // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL - // Since NextAuth.js automatically uses the VERCEL_URL if present. - (str) => process.env.VERCEL_URL ?? str, - // VERCEL_URL doesn't include `https` so it cant be validated as a URL - process.env.VERCEL ? z.string().min(1) : z.string().url(), - ), AUTH0_CLIENT_ID: z.string().min(1), AUTH0_CLIENT_SECRET: z.string().min(1), AUTH0_ISSUER: z.string().min(1), @@ -42,8 +31,6 @@ export const env = createEnv({ runtimeEnv: { DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, - NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, - NEXTAUTH_URL: process.env.NEXTAUTH_URL, NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL, AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET: process.env.AUTH0_CLIENT_SECRET, diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 68ca395..f32facd 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,7 +1,6 @@ import { type NextPage } from "next"; import { signIn, signOut, useSession } from "next-auth/react"; import Head from "next/head"; -import { api } from "~/api"; import Link from "next/link"; import { Heading } from "@chakra-ui/react"; @@ -25,6 +24,7 @@ const Home: NextPage = () => {

diff --git a/src/pages/study.tsx b/src/pages/study.tsx index 56fe706..d09e652 100644 --- a/src/pages/study.tsx +++ b/src/pages/study.tsx @@ -1,4 +1,7 @@ import Study from "~/components/Study/Study"; +import generateAuthGSSP from "~/utils/generateAuthGSSP"; + +export const getServerSideProps = generateAuthGSSP("/study"); const StudyPage = () => { return ; diff --git a/src/state/study.ts b/src/state/study.ts index 0502d75..6c916de 100644 --- a/src/state/study.ts +++ b/src/state/study.ts @@ -13,11 +13,13 @@ interface MoveData { dests: Dests; } +type MoveDataByIndex = Map; + interface StudyState { id: string; parent: string | null; chess: Chess; - moveDataByIndex: Map; + moveDataByIndex: MoveDataByIndex; branches: Map; currentMoveIndex: Index; setCurrentMoveIndex: (index: Index) => void; @@ -26,6 +28,7 @@ interface StudyState { moveLength: () => number; currentFen: () => FEN; move: (orig: Key, dest: Key) => void; + loadPGN: (pgn: PGN) => void; } const toDests = (fen: FEN): Dests => { @@ -45,7 +48,9 @@ const toDests = (fen: FEN): Dests => { }; const promotionCalcChess = new Chess(); + const isPromotion = (fen: FEN, orig: Key, dest: Key): boolean => { + console.log("fen", fen); promotionCalcChess.load(fen); return promotionCalcChess .move({ from: orig, to: dest, promotion: "q" }) @@ -116,6 +121,27 @@ const useStudyStore = create()( todo(null); } }, + loadPGN: (pgn) => { + const chess = new Chess(); + chess.loadPgn(pgn); + const moves = pgn.split(" "); + const newMoveDataByIndex: MoveDataByIndex = new Map().set(0, { + fen: initialFen, + dests: toDests(initialFen), + }); + chess.history({ verbose: true }).map((move, i) => { + console.log("setting", i + 1, move.after); + newMoveDataByIndex.set(i + 1, { + fen: move.after, + dests: toDests(move.after), + }); + }); + set(() => ({ + chess, + moveDataByIndex: newMoveDataByIndex, + currentMoveIndex: 0, + })); + }, }; }) ); diff --git a/src/utils/generateAuthGSSP.ts b/src/utils/generateAuthGSSP.ts new file mode 100644 index 0000000..5036bf1 --- /dev/null +++ b/src/utils/generateAuthGSSP.ts @@ -0,0 +1,23 @@ +import { GetServerSideProps } from "next"; +import { getServerAuthSession } from "~/server/auth"; + +type GenerateAuthGSSP = (callbackUrl: string) => GetServerSideProps; + +const generateAuthGSSP: GenerateAuthGSSP = (callbackUrl) => async (ctx) => { + const session = await getServerAuthSession(ctx); + if (session) { + return { + props: { + session, + }, + }; + } + return { + redirect: { + destination: `/api/auth/signin?callbackUrl=${callbackUrl}`, + permanent: false, + }, + }; +}; + +export default generateAuthGSSP;