From 4b7edbdbf0cbccc01bef0aa4aff9275e737b46f4 Mon Sep 17 00:00:00 2001 From: Idris Gadi <85882535+IdrisGit@users.noreply.github.com> Date: Sun, 27 Oct 2024 03:53:38 +0530 Subject: [PATCH 1/5] Fix callback getting appended to the url when signin button is clicked on login page (#1178) Added guard clause to prevent signin() method from being called on login page as it tries to navigate to login page and causing callback url getting appended in the url bar issue. --- components/Nav/Nav.tsx | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/components/Nav/Nav.tsx b/components/Nav/Nav.tsx index 8f653969..299617fe 100644 --- a/components/Nav/Nav.tsx +++ b/components/Nav/Nav.tsx @@ -1,5 +1,6 @@ "use client"; import { api } from "@/server/trpc/react"; +import { usePathname } from "next/navigation"; import { Disclosure, DisclosureButton, @@ -40,6 +41,8 @@ const Nav = ({ enabled: session ? true : false, }); + const pathname = usePathname(); + const userNavigation = [ { name: "Your Profile", @@ -55,6 +58,18 @@ const Nav = ({ const hasNotifications = !!count && count > 0; + const handleSignInPageNavigation = () => { + /** + * * NextAuth.js automatically adds current url to the callbackurl prop in the singin() method. + * * As Navbar is always present, spamming SignIn button causes login page url getting appended in the url bar. + */ + if (pathname === "/get-started") { + return; + } + + signIn(); + }; + return ( {({ close, open }) => ( @@ -117,12 +132,15 @@ const Nav = ({ ) : ( <> - From 5a60d53c70ec3589637efb76d375e9281b6c1bf4 Mon Sep 17 00:00:00 2001 From: John <46611809+JohnAllenTech@users.noreply.github.com> Date: Sat, 26 Oct 2024 16:25:20 -0600 Subject: [PATCH 2/5] chore: added gif request to pr template (#1180) --- .github/pull_request_template.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1bd77156..e04c4489 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,3 +17,8 @@ Fixes #(issue) - IF YOU HAVE ANY SCREENSHOTS, INCLUDE THEM HERE. _( Welcome file extensions include gifs/png screenshots of your feature in action )_ - IF YOU HAVE NO SCREENSHOTS, ENTER 'None' + +## [Optional] What gif best describes this PR or how it makes you feel + +- [HERES](https://stackoverflow.com/questions/34341808/is-there-a-way-to-add-an-animated-gif-to-a-markdown-file) HOW TO EASILY ADD GIFS TO A PR +- IF NO GIF COMES TO MIND, ENTER 'None' From 02ac49f1c7bf68e4a6bbe117f10f79483230c54f Mon Sep 17 00:00:00 2001 From: John <46611809+JohnAllenTech@users.noreply.github.com> Date: Sun, 27 Oct 2024 16:07:03 -0600 Subject: [PATCH 3/5] chore: moving out user auth out into a util for E2E tests (#1181) * chore: moving out from user auth out into a util * chore: missed some updates in the README * chore: adding logging to better understand why user setup is failing when pushed --- .github/workflows/e2e-tests.yml | 2 +- README.md | 6 ++-- drizzle/seed.ts | 2 +- e2e/articles.spec.ts | 8 ++--- e2e/auth.setup.ts | 54 --------------------------------- e2e/home.spec.ts | 7 +++-- e2e/login.spec.ts | 4 +++ e2e/my-posts.spec.ts | 7 +++-- e2e/settings.spec.ts | 7 +++-- e2e/utils/index.ts | 1 + e2e/utils/utils.ts | 25 +++++++++++++++ playwright.config.ts | 10 ------ playwright/.auth/browser.json | 15 --------- sample.env | 2 +- 14 files changed, 52 insertions(+), 98 deletions(-) delete mode 100644 e2e/auth.setup.ts create mode 100644 e2e/utils/index.ts create mode 100644 e2e/utils/utils.ts delete mode 100644 playwright/.auth/browser.json diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 115a8a7e..cf2ebdc2 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -46,7 +46,7 @@ jobs: NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }} E2E_USER_EMAIL: e2e@codu.co E2E_USER_ID: 8e3179ce-f32b-4d0a-ba3b-234d66b836ad - E2E_USER_SESSION_ID: df8a11f2-f20a-43d6-80a0-a213f1efedc1 + E2E_USER_ONE_SESSION_ID: df8a11f2-f20a-43d6-80a0-a213f1efedc1 steps: - name: Checkout repository diff --git a/README.md b/README.md index 89a0ff03..1909e175 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,9 @@ You shouldn't need to change the default value here. This is a variable used by NEXTAUTH_URL=http://localhost:3000/api/auth ``` -### E2E_USER_SESSION_ID +### E2E_USER_ONE_SESSION_ID -This is the sessionToken uuid that . +This is the sessionToken uuid that is used to identify a users current active session. This is currently hardcoded and there is no reason to change this until we require multiple E2E test users within the same test suite ### E2E_USER_ID @@ -173,7 +173,7 @@ Please ensure you have the following variables set in your `.env` file: - `E2E_USER_ID`: The id of the E2E user for testing. - `E2E_USER_EMAIL`: The email of the E2E user for testing. -- `E2E_USER_SESSION_ID`: The session id that the user will use to authenticate. +- `E2E_USER_ONE_SESSION_ID`: The session id that the user will use to authenticate. Note the sample .env [here](./sample.env) is fine to use. diff --git a/drizzle/seed.ts b/drizzle/seed.ts index 6113c6eb..c998ac8a 100644 --- a/drizzle/seed.ts +++ b/drizzle/seed.ts @@ -11,7 +11,7 @@ import postgres from "postgres"; const DATABASE_URL = process.env.DATABASE_URL || ""; // These can be removed in a follow on PR. Until this hits main we cant add E2E_USER_* stuff to the env. const E2E_SESSION_ID = - process.env.E2E_USER_SESSION_ID || "df8a11f2-f20a-43d6-80a0-a213f1efedc1"; + process.env.E2E_USER_ONE_SESSION_ID || "df8a11f2-f20a-43d6-80a0-a213f1efedc1"; const E2E_USER_ID = process.env.E2E_USER_ID || "8e3179ce-f32b-4d0a-ba3b-234d66b836ad"; const E2E_USER_EMAIL = process.env.E2E_USER_EMAIL || "e2e@codu.co"; diff --git a/e2e/articles.spec.ts b/e2e/articles.spec.ts index 539299f5..f006fae6 100644 --- a/e2e/articles.spec.ts +++ b/e2e/articles.spec.ts @@ -1,11 +1,8 @@ import { test, expect } from "playwright/test"; import { randomUUID } from "crypto"; +import { loggedInAsUserOne } from "./utils"; test.describe("Unauthenticated Articles Page", () => { - test.beforeEach(async ({ page }) => { - await page.context().clearCookies(); - }); - test("Should show popular tags", async ({ page, isMobile }) => { await page.goto("http://localhost:3000/articles"); await expect( @@ -133,6 +130,9 @@ test.describe("Unauthenticated Articles Page", () => { }); test.describe("Authenticated Articles Page", () => { + test.beforeEach(async ({ page }) => { + await loggedInAsUserOne(page); + }); test("Should show recent bookmarks", async ({ page, isMobile }) => { await page.goto("http://localhost:3000/articles"); await expect( diff --git a/e2e/auth.setup.ts b/e2e/auth.setup.ts deleted file mode 100644 index ad2347be..00000000 --- a/e2e/auth.setup.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { test as setup, expect } from "@playwright/test"; -import path from "path"; -import dotenv from "dotenv"; -import browserState from "../playwright/.auth/browser.json"; - -// defaults to 1 if expires not passed. This will always fail -const hasFiveMinutes = (expires: number = 1) => { - const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds - return expires - currentTime >= 300; // Check if there's at least 5 minutes until expiry -}; - -dotenv.config(); // Load .env file contents into process.env - -setup("authenticate", async ({ page }) => { - // check if theres already an authenticated browser state with atleast 5 mins until expiry - if ( - browserState.cookies.length && - hasFiveMinutes( - (browserState.cookies as Array<{ name: string; expires: number }>).find( - (cookie: { name: string }) => cookie.name === "next-auth.session-token", - )?.expires, - ) - ) { - console.log( - "Skipping auth setup as there is a currently valid authenticated browser state", - ); - return; - } - - try { - expect(process.env.E2E_USER_SESSION_ID).toBeDefined(); - - await page.context().addCookies([ - { - name: "next-auth.session-token", - value: process.env.E2E_USER_SESSION_ID as string, - domain: "localhost", - path: "/", - sameSite: "Lax", - }, - ]); - - expect( - (await page.context().cookies()).find( - (cookie) => cookie.name === "next-auth.session-token", - ), - ).toBeTruthy(); - await page.context().storageState({ - path: path.join(__dirname, "../playwright/.auth/browser.json"), - }); - } catch (err) { - console.log("Error while authenticating E2E test user", err); - } -}); diff --git a/e2e/home.spec.ts b/e2e/home.spec.ts index acbcfcf8..ebec11a7 100644 --- a/e2e/home.spec.ts +++ b/e2e/home.spec.ts @@ -1,6 +1,10 @@ import { test, expect } from "@playwright/test"; +import { loggedInAsUserOne } from "./utils"; test.describe("Authenticated homepage", () => { + test.beforeEach(async ({ page }) => { + await loggedInAsUserOne(page); + }); test("Homepage view", async ({ page, isMobile }) => { await page.goto("http://localhost:3000/"); @@ -24,9 +28,6 @@ test.describe("Authenticated homepage", () => { }); test.describe("Unauthenticated homepage", () => { - test.beforeEach(async ({ page }) => { - await page.context().clearCookies(); - }); test("Homepage view", async ({ page }) => { await page.goto("http://localhost:3000/"); diff --git a/e2e/login.spec.ts b/e2e/login.spec.ts index 7c5626ec..ea60eac2 100644 --- a/e2e/login.spec.ts +++ b/e2e/login.spec.ts @@ -1,5 +1,6 @@ import { test, expect } from "playwright/test"; import "dotenv/config"; +import { loggedInAsUserOne } from "./utils"; test.describe("Unauthenticated Login Page", () => { test.beforeEach(async ({ page }) => { @@ -31,6 +32,9 @@ test.describe("Unauthenticated Login Page", () => { }); test.describe("Authenticated Login Page", () => { + test.beforeEach(async ({ page }) => { + await loggedInAsUserOne(page); + }); test("Sign up page contains sign up links", async ({ page, isMobile }) => { // authenticated users are kicked back to the homepage if they try to go to /get-started await page.goto("http://localhost:3000/get-started"); diff --git a/e2e/my-posts.spec.ts b/e2e/my-posts.spec.ts index ad9d5398..c1f537f9 100644 --- a/e2e/my-posts.spec.ts +++ b/e2e/my-posts.spec.ts @@ -1,14 +1,15 @@ import test from "@playwright/test"; +import { loggedInAsUserOne } from "./utils"; test.describe("Unauthenticated my-posts Page", () => { - test.beforeEach(async ({ page }) => { - await page.context().clearCookies(); - }); // // Replace with tests for unauthenticated users }); test.describe("Authenticated my-posts Page", () => { + test.beforeEach(async ({ page }) => { + await loggedInAsUserOne(page); + }); // // Replace with tests for authenticated users }); diff --git a/e2e/settings.spec.ts b/e2e/settings.spec.ts index 1aa93631..f53480c0 100644 --- a/e2e/settings.spec.ts +++ b/e2e/settings.spec.ts @@ -1,14 +1,15 @@ import test from "@playwright/test"; +import { loggedInAsUserOne } from "./utils"; test.describe("Unauthenticated setttings Page", () => { - test.beforeEach(async ({ page }) => { - await page.context().clearCookies(); - }); // // Replace with tests for unauthenticated users }); test.describe("Authenticated settings Page", () => { + test.beforeEach(async ({ page }) => { + await loggedInAsUserOne(page); + }); // // Replace with tests for authenticated users }); diff --git a/e2e/utils/index.ts b/e2e/utils/index.ts new file mode 100644 index 00000000..178cd64f --- /dev/null +++ b/e2e/utils/index.ts @@ -0,0 +1 @@ +export * from "./utils"; diff --git a/e2e/utils/utils.ts b/e2e/utils/utils.ts new file mode 100644 index 00000000..e5b8f596 --- /dev/null +++ b/e2e/utils/utils.ts @@ -0,0 +1,25 @@ +import { expect, Page } from "@playwright/test"; + +export const loggedInAsUserOne = async (page: Page) => { + try { + expect(process.env.E2E_USER_ONE_SESSION_ID).toBeDefined(); + + await page.context().addCookies([ + { + name: "next-auth.session-token", + value: process.env.E2E_USER_ONE_SESSION_ID as string, + domain: "localhost", + path: "/", + sameSite: "Lax", + }, + ]); + + expect( + (await page.context().cookies()).find( + (cookie) => cookie.name === "next-auth.session-token", + ), + ).toBeTruthy(); + } catch (err) { + throw Error("Error while authenticating E2E test user one"); + } +}; diff --git a/playwright.config.ts b/playwright.config.ts index a58f2979..f9259f27 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -39,10 +39,6 @@ export default defineConfig({ { name: "setup", testMatch: /auth.setup\.ts/ }, { name: "Desktop Chrome", - use: { - storageState: "playwright/.auth/browser.json", - }, - dependencies: ["setup"], }, // Example other browsers @@ -50,25 +46,19 @@ export default defineConfig({ name: "Desktop Firefox", use: { ...devices["Desktop Firefox"], - storageState: "playwright/.auth/browser.json", }, - dependencies: ["setup"], }, { name: "Mobile Chrome", use: { ...devices["Pixel 9"], - storageState: "playwright/.auth/browser.json", }, - dependencies: ["setup"], }, { name: "Mobile Safari", use: { ...devices["iPhone 16"], - storageState: "playwright/.auth/browser.json", }, - dependencies: ["setup"], }, ], diff --git a/playwright/.auth/browser.json b/playwright/.auth/browser.json deleted file mode 100644 index fc6525ac..00000000 --- a/playwright/.auth/browser.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "cookies": [ - { - "name": "next-auth.session-token", - "value": "df8a11f2-f20a-43d6-80a0-a213f1efedc1", - "domain": "localhost", - "path": "/", - "expires": -1, - "httpOnly": false, - "secure": false, - "sameSite": "Lax" - } - ], - "origins": [] -} diff --git a/sample.env b/sample.env index bbd1f34c..17494bd2 100644 --- a/sample.env +++ b/sample.env @@ -7,4 +7,4 @@ DATABASE_URL=postgresql://postgres:secret@127.0.0.1:5432/postgres E2E_USER_EMAIL=e2e@codu.co E2E_USER_ID=8e3179ce-f32b-4d0a-ba3b-234d66b836ad -E2E_USER_SESSION_ID=df8a11f2-f20a-43d6-80a0-a213f1efedc1 +E2E_USER_ONE_SESSION_ID=df8a11f2-f20a-43d6-80a0-a213f1efedc1 From e760be57896dec1c0d3ab0f36015df9d948df716 Mon Sep 17 00:00:00 2001 From: Peter Cruckshank Date: Mon, 28 Oct 2024 07:31:31 -0400 Subject: [PATCH 4/5] fixed errors on `/app/articles/app/page.tsx` and added types for isomorphic-dompurify (#1179) * fixed errors on /app/articles/app/page and added types * Imported and replace empty string eorr with it. --- app/(app)/articles/[slug]/page.tsx | 13 ++++++++++--- package-lock.json | 2 +- package.json | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/(app)/articles/[slug]/page.tsx b/app/(app)/articles/[slug]/page.tsx index 88a8f58d..614e9f6e 100644 --- a/app/(app)/articles/[slug]/page.tsx +++ b/app/(app)/articles/[slug]/page.tsx @@ -14,10 +14,11 @@ import ArticleAdminPanel from "@/components/ArticleAdminPanel/ArticleAdminPanel" import { type Metadata } from "next"; import { getPost } from "@/server/lib/posts"; import { getCamelCaseFromLower } from "@/utils/utils"; -import { generateHTML } from "@tiptap/html"; +import { generateHTML } from "@tiptap/core"; import { TiptapExtensions } from "@/components/editor/editor/extensions"; import DOMPurify from "isomorphic-dompurify"; import type { JSONContent } from "@tiptap/core"; +import NotFound from "@/components/NotFound/NotFound"; type Props = { params: { slug: string } }; @@ -119,11 +120,17 @@ const ArticlePage = async ({ params }: Props) => { {isTiptapContent ? (
, + }} className="tiptap-content" /> ) : ( -
{renderedContent}
+
+ {Markdoc.renderers.react(renderedContent, React, { + components: markdocComponents, + })} +
)} {post.tags.length > 0 && ( diff --git a/package-lock.json b/package-lock.json index fa5fb01c..37e073df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,6 +99,7 @@ "@svgr/webpack": "^8.1.0", "@tailwindcss/typography": "^0.5.13", "@types/chance": "^1.1.6", + "@types/dompurify": "^3.0.5", "@types/node": "^22.7.5", "@types/nodemailer": "^6.4.15", "@types/pg": "^8.11.5", @@ -8634,7 +8635,6 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", - "license": "MIT", "dependencies": { "@types/trusted-types": "*" } diff --git a/package.json b/package.json index b687eea3..96a67dd0 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ "@svgr/webpack": "^8.1.0", "@tailwindcss/typography": "^0.5.13", "@types/chance": "^1.1.6", + "@types/dompurify": "^3.0.5", "@types/node": "^22.7.5", "@types/nodemailer": "^6.4.15", "@types/pg": "^8.11.5", From beae6ffd607ba657ac2d7ca199cec0a11b30c3f7 Mon Sep 17 00:00:00 2001 From: Niall Maher Date: Mon, 28 Oct 2024 19:55:29 +0000 Subject: [PATCH 5/5] Add this weeks newsletter (#1184) --- .../letters/is-your-domain-haunted/page.mdx | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 app/(app)/letters/is-your-domain-haunted/page.mdx diff --git a/app/(app)/letters/is-your-domain-haunted/page.mdx b/app/(app)/letters/is-your-domain-haunted/page.mdx new file mode 100644 index 00000000..0dcf0d15 --- /dev/null +++ b/app/(app)/letters/is-your-domain-haunted/page.mdx @@ -0,0 +1,60 @@ +Tue, October 29th, 2024 โ€ข Niall Maher + +# ๐ŸŽƒ Is Your Domain Haunted? + Dev Productivity Tips + +Here's a Chrome DevTools trick to share screenshots of elements: the "Capture node screenshot" feature. + +Right-click any element in the Elements panel, select "Capture node screenshot," and instantly get a perfect screenshot of just that component. This is incredibly useful for documentation, bug reports, or sharing UI elements with designers. + +No more awkward cropping in image editors. + +## ๐Ÿ“š This Week's Picks + +**[How to Cancel Async Code in JavaScript (4 min)](https://www.codu.co/articles/how-to-cancel-async-code-in-javascript-k4ate_e8)** +Canceling asynchronous operations in JavaScript has historically been challenging. Not anymore thanks to AbortController API and its companion, AbortSignal. A short guide I wrote to introduce these topics. + +**[Before you buy a domain name, first check to see if it's haunted (6 min)](https://www.bryanbraun.com/2024/10/25/before-you-buy-a-domain-name-first-check-to-see-if-its-haunted/)** +Since it's Halloween season, you probably buy too many domains if you are anything like me. Here's why you check if it's "haunted." + +**[Creating Links to Locations in Google Maps and Apple Maps (2 min)](https://www.codu.co/articles/creating-links-to-locations-in-google-maps-and-apple-maps-eqj0ozor)** +This guide covers different methods for linking to locations in Google Maps and Apple Maps. + +**[SOLID Principles: Building a Strong Foundation for Your Code (2 min)](https://www.codu.co/articles/solid-principles-building-a-strong-foundation-for-your-code-fliy0v84)** +Maintaining and extending code in software development can become challenging as projects grow. Here are how SOLID principles can help. + +**[Why does JavaScript's fetch make me wait TWICE (video)](https://youtu.be/Ki64Cnyf_cA?si=JotsNJdMySFhV-82)** +Ever wonder why you need to use response.json() after fetching in JavaScript? This video will explain why this isn't just a single operation. + +**[Our Journey with Caching (4 min)](https://nextjs.org/blog/our-journey-with-caching)** +This post dives into caching with Next.js. Written by the Next.js team, it is an essential and short guide to getting it right in your applications. + +## ๐Ÿ“– Book of the Week + +**[Inspired: How to Create Tech Products Customers Love](https://amzn.to/3Ai1D7k)** + +This one might be familiar if you've read my newsletter for a while. But I guess it's that time of year again when I re-read my favorite product book. + +Marty Cagan's insights never get old - from how to validate ideas quickly to structuring teams that consistently ship great products. What I love most is how it challenges the usual "build what customers ask for" mindset. Instead, Cagan shows how to dig deeper and solve problems customers didn't even know they had. If you're involved in product decisions at any level, this book, I'd say, is worth revisiting every year. + +## ๐Ÿ› ๏ธ Something Cool + +**[Schotten Totten 2 | Board Game](https://amzn.to/3C6eQks)** + +Yes, it's not code related. But if you're looking for a perfect lunch break game, this is it. It's a two-player card battle where you're trying to breach your opponent's castle walls. What makes it fun is how it packs strategy into 15-minute rounds - think poker hands mixed with tactical positioning. + +Once you get your head around the rules (which might take a game or two if you are slow like me), you'll be discovering new strategies weeks later. A rare gem that's both accessible and strategically rich. + +## ๐Ÿ”— Quick Links + +- Codรบ TikTok: [https://www.tiktok.com/@codu.co](https://newsletter.codu.co/l/OKhlNrmpjTTHP763xCz1UNOw/p8DcaDEDbL8FJC5y1tmH4w/EEOHywhMSrHPNxKbF3Ps892Q) +- Hacktoberfest GitHub Issues: [https://github.com/codu-code/codu/issues](https://newsletter.codu.co/l/OKhlNrmpjTTHP763xCz1UNOw/EWCx6TdpfoYQZxltZdSeZQ/EEOHywhMSrHPNxKbF3Ps892Q) +- Our YouTube channel: [https://www.youtube.com/@codu](https://newsletter.codu.co/l/OKhlNrmpjTTHP763xCz1UNOw/iJvccMa1dTBysEZfCVFzkw/EEOHywhMSrHPNxKbF3Ps892Q) +- Find us on Twitch: [https://www.twitch.tv/codudotco](https://www.twitch.tv/codudotco) + +**If you have any ideas or feedback, reply to this email.** + +Thanks, and stay awesome, + +Niall + +Founder @ [Codรบ](https://www.codu.co/?ref=newsletter)