Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

fix: simplified mock oauth with mswjs #183

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# APP_ENV=test # if you wish to use test mode (no real github oauth)

NEXT_PUBLIC_FLAGSMITH_ENVIRONMENT_ID=
FLAGSMITH_ENVIRONMENT_API_KEY=

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"lint": "set TRUNK_TELEMETRY=off && trunk check",
"fmt": "set TRUNK_TELEMETRY=off && trunk fmt",
"lint-old": "next lint",
"test": "npx playwright test",
"test": "npx playwright test --trace on",
"prepare": "husky",
"postinstall": "shx cp -n ./.env.example ./.env",
"format:write": "prettier . --write",
Expand Down
7 changes: 4 additions & 3 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://127.0.0.1:3000",
baseURL: "http://localhost:3000",

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
Expand Down Expand Up @@ -71,8 +71,9 @@ export default defineConfig({

/* Run your local dev server before starting the tests */
webServer: {
command: "cross-env APP_ENV=test npm run dev",
url: "http://127.0.0.1:3000",
command:
"cross-env APP_ENV=test GITHUB_ID=aaa GITHUB_SECRET=bbb npm run dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
});
27 changes: 16 additions & 11 deletions src/app/api/auth/[...nextauth]/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

let githubProvierConfig = {
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
authorization: {
params: {
scope: "read:user user:email public_repo read:project",
},
},
};

if (process.env.NEXT_RUNTIME === "nodejs" && process.env.APP_ENV === "test") {
githubProvierConfig.authorization.url =
"http://localhost:3000/api/auth/callback/github?code=abcd";
}

const authOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
authorization: {
params: {
scope: "read:user user:email public_repo read:project",
},
},
}),
],
providers: [GithubProvider(githubProvierConfig)],
callbacks: {
async session({ session, token, user }) {
session.user.id = user.id;
Expand Down
2 changes: 1 addition & 1 deletion src/instrumentation.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export async function register() {
const unmocked = [
"localhost:3000",
"nextjs.org",
"googleapis.com",
"gstatic.com",
"api.flagsmith.com",
Expand Down
23 changes: 12 additions & 11 deletions tests/account/repo/add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,36 @@ import { test, expect } from "@playwright/test";

import { login, logout } from "../../setup/auth";

test("Guest user cannot access add repo", async ({ browser }) => {
const page = await logout(browser);
test("Guest user cannot access add repo", async ({ page }) => {
await logout(page);
await page.goto("/account/repo/add");
await expect(page).toHaveURL(/\//);
await expect(page).toHaveURL("/");
});

test("Logged in user can access add repo", async ({ browser }) => {
const page = await login(browser);
test("Logged in user can access add repo", async ({ page }) => {
await login(page);

await page.goto("/account/repo/add");
await expect(page).toHaveURL(/account\/repo\/add/);
});

test("Logged in user can see add user nav button", async ({ browser }) => {
const page = await login(browser);
test("Logged in user can see add user nav button", async ({ page }) => {
await login(page);
await page.goto("/");
await page.getByRole("link", { name: "Add" }).click();
await expect(page).toHaveURL(/account\/repo\/add/);
});

test("test url required", async ({ browser }) => {
const page = await login(browser);
test("test url required", async ({ page }) => {
await login(page);
await page.goto("/account/repo/add");
await page.getByLabel("url").fill("abcdefg");
await page.getByRole("button", { name: "SAVE" }).click();
await expect(page.locator("#url-error")).toContainText("Invalid url");
});

test("test valid url navigates to the check list page", async ({ browser }) => {
const page = await login(browser);
test("test valid url navigates to the check list page", async ({ page }) => {
await login(page);
await page.goto("/account/repo/add");

await page
Expand Down
5 changes: 5 additions & 0 deletions tests/data/github/access_token.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"access_token": "abcdefg",
"scope": "repo,gist",
"token_type": "bearer"
}
46 changes: 46 additions & 0 deletions tests/data/github/user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"login": "mona",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/mona.png",
"gravatar_id": "",
"url": "https://api.github.com/users/mona",
"html_url": "https://github.com/mona",
"followers_url": "https://api.github.com/users/mona/followers",
"following_url": "https://api.github.com/users/mona/following{/other_user}",
"gists_url": "https://api.github.com/users/mona/gists{/gist_id}",
"starred_url": "https://api.github.com/users/mona/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/mona/subscriptions",
"organizations_url": "https://api.github.com/users/mona/orgs",
"repos_url": "https://api.github.com/users/mona/repos",
"events_url": "https://api.github.com/users/mona/events{/privacy}",
"received_events_url": "https://api.github.com/users/mona/received_events",
"type": "User",
"site_admin": false,
"name": "monalisa octocat",
"company": "GitHub",
"blog": "https://github.com/blog",
"location": "San Francisco",
"email": "[email protected]",
"hireable": false,
"bio": "There once was...",
"twitter_username": "monatheoctocat",
"public_repos": 2,
"public_gists": 1,
"followers": 20,
"following": 0,
"created_at": "2008-01-14T04:33:35Z",
"updated_at": "2008-01-14T04:33:35Z",
"private_gists": 81,
"total_private_repos": 100,
"owned_private_repos": 100,
"disk_usage": 10000,
"collaborators": 8,
"two_factor_authentication": true,
"plan": {
"name": "Medium",
"space": 400,
"private_repos": 20,
"collaborators": 0
}
}
126 changes: 16 additions & 110 deletions tests/setup/auth.js
Original file line number Diff line number Diff line change
@@ -1,120 +1,26 @@
import { encode } from "next-auth/jwt";
import prisma from "@/models/db";
import { expect } from "@playwright/test";

const login = async (
browser,
user = {
name: "Authenticated User",
email: "[email protected]",
},
) => {
const date = new Date();
let testUser;
const login = async (page) => {
// go to login url
const url =
"http://localhost:3000/api/auth/signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2F";
await page.goto(url);
await expect(page).toHaveURL(/\/api\/auth\/signin/);

const userData = {
email: user.email,
name: user.name,
image: "https://github.com/mona.png",
emailVerified: null,
};

try {
testUser = await prisma.user.upsert({
where: { email: user.email },
update: userData,
create: userData,
});

if (!testUser) {
throw new Error("Failed to create or retrieve test authenticated user");
}
} catch (e) {
const error = "Test authenticated user creation failed";
console.error(error, e);
throw new Error(error);
}

const sessionToken = await encode({
token: {
image: "https://github.com/mona.png",
accessToken: "ggg_zZl1pWIvKkf3UDynZ09zLvuyZsm1yC0YoRPt",
...user,
sub: testUser.id,
},
secret: process.env.NEXTAUTH_SECRET,
});

const session = {
sessionToken,
userId: testUser.id,
expires: new Date(date.getFullYear(), date.getMonth() + 1, 0),
};

try {
await prisma.session.upsert({
where: {
sessionToken: sessionToken,
},
update: session,
create: session,
});
} catch (e) {
const error = "Test authenticated session creation failed";
console.error(error, e);
throw new Error(error);
}

const account = {
type: "oauth",
provider: "github",
providerAccountId: testUser.id,
userId: testUser.id,
access_token: "ggg_zZl1pWIvKkf3UDynZ09zLvuyZsm1yC0YoRPt",
token_type: "bearer",
scope: "read:org,read:user,repo,user:email,test:all",
};

try {
await prisma.account.upsert({
where: {
provider_providerAccountId: {
provider: "github",
providerAccountId: testUser.id,
},
},
update: account,
create: account,
});
} catch (e) {
const error = "Test account creation failed";
console.error(error, e);
throw new Error(error);
}

const context = await browser.newContext();
await context.addCookies([
{
name: "next-auth.session-token",
value: sessionToken,
domain: "127.0.0.1",
path: "/",
httpOnly: true,
sameSite: "Lax",
secure: true,
expires: -1,
},
]);

const page = await context.newPage();
// click login
await page.getByRole("button", { name: "Sign in with GitHub" }).click();
await expect(page).toHaveURL("/");

return page;
};

const logout = async (browser) => {
const context = await browser.newContext();
await context.clearCookies();
const logout = async (page) => {
// visit sign out page
await page.goto("/api/auth/signout");

const page = await context.newPage();
// click sign out
await page.getByRole("button", { name: "Sign out" }).click();
await expect(page).toHaveURL("/");

return page;
};
Expand Down
9 changes: 9 additions & 0 deletions tests/setup/mocks/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ import labels from "../../data/github/labels.json";
import projects from "../../data/github/projects.json";
import referrers from "../../data/github/referrers.json";
import views from "../../data/github/views.json";
import user from "../../data/github/user.json";
import accessToken from "../../data/github/access_token.json";

export const handlers = [
// authentication
http.post("https://github.com/login/oauth/access_token", () =>
HttpResponse.json(accessToken),
),
http.get("https://api.github.com/user", () => HttpResponse.json(user)),

// github data
http.get("https://api.github.com/repos/EddieHubCommunity/HealthCheck", () =>
HttpResponse.json(repo),
),
Expand Down
Loading