Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: tests and upgrade to node 22 #1130

Merged
merged 3 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
with:
cache: "yarn"
node-version: "20.x"
node-version: "22.x"
registry-url: "https://npm.pkg.github.com"
scope: "@sesamyab"
- name: Install dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
with:
cache: "yarn"
node-version: "20.x"
node-version: "22.x"
registry-url: "https://npm.pkg.github.com"
scope: "@sesamyab"
- name: Install dependencies
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4

- name: Setup Node.js 20
- name: Setup Node.js 22
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'yarn'
registry-url: 'https://npm.pkg.github.com'

Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20
22
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"@planetscale/database": "1.19.0",
"@xmldom/xmldom": "^0.9.5",
"arctic": "^2.2.2",
"authhero": "^0.13.1",
"authhero": "^0.13.2",
"bcryptjs": "^2.4.3",
"fast-xml-parser": "^4.5.0",
"hono": "4.4.0",
Expand Down
12 changes: 7 additions & 5 deletions src/routes/universal-login/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -474,14 +474,16 @@ export const loginRoutes = new OpenAPIHono<{ Bindings: Env; Variables: Var }>()
algorithm: "bcrypt",
};

try {
// In case the password doesn't exist
await env.data.passwords.create(client.tenant.id, passwordOptions);
} catch {
const existingPassword = await env.data.passwords.get(
client.tenant.id,
user.user_id,
);
if (existingPassword) {
await env.data.passwords.update(client.tenant.id, passwordOptions);
} else {
await env.data.passwords.create(client.tenant.id, passwordOptions);
}

// we could do this on the GET...
if (!user.email_verified) {
await env.data.users.update(client.tenant.id, user.user_id, {
email_verified: true,
Expand Down
10 changes: 8 additions & 2 deletions test/integration/disable-signup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { test, expect } from "vitest";
import { getTestServer } from "./helpers/test-server";
import { testClient } from "hono/testing";
import { snapshotResponse } from "./helpers/playwrightSnapshots";
import { base64url } from "oslo/encoding";
import { AuthorizationResponseType } from "@authhero/adapter-interfaces";

test("only allows existing users to progress to the enter code step", async () => {
Expand Down Expand Up @@ -46,11 +45,16 @@ test("only allows existing users to progress to the enter code step", async () =
});
// this will only render the FB button
await env.data.connections.create("breakit", {
id: "breakit-connection",
name: "facebook",
strategy: "facebook",
options: {},
});
// We need another option so it doesn't redirect straight to facebook
await env.data.connections.create("breakit", {
name: "email",
strategy: "email",
options: {},
});

const response = await oauthClient.authorize.$get({
query: {
Expand All @@ -62,6 +66,8 @@ test("only allows existing users to progress to the enter code step", async () =
state: "state",
},
});

expect(response.status).toBe(302);
const location = response.headers.get("location");
const stateParam = new URLSearchParams(location!.split("?")[1]);
const query = Object.fromEntries(stateParam.entries());
Expand Down
45 changes: 6 additions & 39 deletions test/integration/flows/code-flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,8 @@ describe("code-flow", () => {
"clientId",
);

const {
// these are the fields that change on every test run
exp,
iat,
sid,
sub,
...restOfIdTokenPayload
} = silentAuthIdTokenPayload;

expect(sub).toContain("email|");
expect(restOfIdTokenPayload).toEqual({
expect(silentAuthIdTokenPayload.sub).toContain("email|");
expect(silentAuthIdTokenPayload).toMatchObject({
aud: "clientId",
name: "test@example.com",
email: "test@example.com",
Expand Down Expand Up @@ -256,23 +247,8 @@ describe("code-flow", () => {
"clientId",
);

const {
sub: sub2,
// TO TEST? that these fields are the same as on the first silent auth?
exp: exp2,
iat: iat2,
sid: sid2,
//
family_name: family_name2,
given_name: given_name2,
nickname: nickname2,
locale: locale2,
picture: picture2,
...restOfIdTokenPayload2
} = silentAuthIdTokenPayload2;

expect(sub2).toEqual(sub);
expect(restOfIdTokenPayload2).toEqual({
expect(silentAuthIdTokenPayload2.sub).toEqual(silentAuthIdTokenPayload.sub);
expect(silentAuthIdTokenPayload2).toMatchObject({
aud: "clientId",
name: "test@example.com",
email: "test@example.com",
Expand Down Expand Up @@ -622,19 +598,10 @@ describe("code-flow", () => {
"clientId",
);

const {
// these are the fields that change on every test run
exp,
iat,
sid,
sub,
...restOfIdTokenPayload
} = silentAuthIdTokenPayload;

// this is the id of the primary account
expect(sub).toBe("auth2|userId");
expect(silentAuthIdTokenPayload.sub).toBe("auth2|userId");

expect(restOfIdTokenPayload).toEqual({
expect(silentAuthIdTokenPayload).toMatchObject({
aud: "clientId",
email: "foo@example.com",
email_verified: true,
Expand Down
28 changes: 5 additions & 23 deletions test/integration/flows/magic-link-flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,8 @@ describe("magic link flow", () => {
"clientId",
);

const {
// these are the fields that change on every test run
exp,
iat,
sid,
sub,
...restOfIdTokenPayload
} = silentAuthIdTokenPayload;

expect(sub).toContain("email|");
expect(restOfIdTokenPayload).toEqual({
expect(silentAuthIdTokenPayload.sub).toContain("email|");
expect(silentAuthIdTokenPayload).toMatchObject({
aud: "clientId",
name: "new-user@example.com",
email: "new-user@example.com",
Expand Down Expand Up @@ -279,10 +270,7 @@ describe("magic link flow", () => {
"clientId",
);

const { exp, iat, sid, ...restOfIdTokenPayload } =
silentAuthIdTokenPayload;

expect(restOfIdTokenPayload).toEqual({
expect(silentAuthIdTokenPayload).toMatchObject({
sub: "email|userId2",
aud: "clientId",
name: "",
Expand Down Expand Up @@ -391,10 +379,7 @@ describe("magic link flow", () => {
"clientId",
);

const { exp, iat, sid, ...restOfIdTokenPayload } =
silentAuthIdTokenPayload;

expect(restOfIdTokenPayload).toEqual({
expect(silentAuthIdTokenPayload).toMatchObject({
sub: "auth2|userId",
aud: "clientId",
name: "Åkesson Þorsteinsson",
Expand Down Expand Up @@ -492,10 +477,7 @@ describe("magic link flow", () => {
"clientId",
);

const { exp, iat, sid, ...restOfIdTokenPayload } =
silentAuthIdTokenPayload;

expect(restOfIdTokenPayload).toEqual({
expect(silentAuthIdTokenPayload).toMatchObject({
sub: "auth2|userId",
aud: "clientId",
name: "Åkesson Þorsteinsson",
Expand Down
21 changes: 6 additions & 15 deletions test/integration/flows/password.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,9 @@ describe("password-flow", () => {
"unique-nonce",
"clientId",
);
const {
// these are the fields that change on every test run
exp,
iat,
sid,
sub,
...restOfIdTokenPayload
} = silentAuthIdTokenPayload;
expect(sub).toContain("auth2|");
expect(restOfIdTokenPayload).toEqual({

expect(silentAuthIdTokenPayload.sub).toContain("auth2|");
expect(silentAuthIdTokenPayload).toMatchObject({
aud: "clientId",
email: "password-login-test@example.com",
email_verified: true,
Expand Down Expand Up @@ -520,7 +513,7 @@ describe("password-flow", () => {
});

it("should not allow a new sign up to overwrite the password of an existing signup", async () => {
const { oauthApp, emails, env } = await getTestServer();
const { oauthApp, env } = await getTestServer();
const aNewPassword = "A-new-valid-password-1234!";
const oauthClient = testClient(oauthApp, env);

Expand Down Expand Up @@ -572,7 +565,7 @@ describe("password-flow", () => {
});
describe("Login with password", () => {
it("should login with existing user", async () => {
const { oauthApp, emails, env } = await getTestServer();
const { oauthApp, env } = await getTestServer();
const oauthClient = testClient(oauthApp, env);
// foo@example.com is an existing username-password user, with password - Test!

Expand Down Expand Up @@ -643,9 +636,7 @@ describe("password-flow", () => {
"unique-nonce",
"clientId",
);
const { exp, iat, sid, ...restOfIdTokenPayload } =
silentAuthIdTokenPayload;
expect(restOfIdTokenPayload).toEqual({
expect(silentAuthIdTokenPayload).toMatchObject({
sub: "auth2|userId",
aud: "clientId",
email: "foo@example.com",
Expand Down
26 changes: 6 additions & 20 deletions test/integration/flows/social.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,7 @@ describe("social sign on", () => {
},
);
const newSocialUser = (await newSocialUserRes.json()) as UserResponse;
const {
created_at,
updated_at,
last_login,
...newSocialUserWithoutDates
} = newSocialUser;
expect(newSocialUserWithoutDates).toEqual(EXPECTED_NEW_USER);
expect(newSocialUser).toMatchObject(EXPECTED_NEW_USER);
});

// like apple
Expand Down Expand Up @@ -319,13 +313,7 @@ describe("social sign on", () => {
},
);
const newSocialUser = (await newSocialUserRes.json()) as UserResponse;
const {
created_at,
updated_at,
last_login,
...newSocialUserWithoutDates
} = newSocialUser;
expect(newSocialUserWithoutDates).toEqual(EXPECTED_NEW_USER);
expect(newSocialUser).toMatchObject(EXPECTED_NEW_USER);
});
});
});
Expand Down Expand Up @@ -404,7 +392,7 @@ describe("social sign on", () => {
});

const socialCallbackResponseQuery = new URLSearchParams(
socialCallbackResponse.headers.get("location")?.split("#")[1]!,
socialCallbackResponse.headers.get("location")?.split("#")[1],
);
const accessTokenPayload = parseJwt(
socialCallbackResponseQuery.get("access_token")!,
Expand Down Expand Up @@ -515,7 +503,7 @@ describe("social sign on", () => {
});

const socialCallbackResponse2Query = new URLSearchParams(
socialCallbackResponse2.headers.get("location")?.split("#")[1]!,
socialCallbackResponse2.headers.get("location")?.split("#")[1],
);
expect(
parseJwt(socialCallbackResponse2Query.get("access_token")!).sub,
Expand Down Expand Up @@ -552,9 +540,7 @@ describe("social sign on", () => {
expect(socialCallbackResponseAnotherSSO.status).toBe(302);

const socialCallbackResponseAnotherSSOQuery = new URLSearchParams(
socialCallbackResponseAnotherSSO.headers
.get("location")
?.split("#")[1]!,
socialCallbackResponseAnotherSSO.headers.get("location")?.split("#")[1],
);
// these confirm we are still signing in with the primary user
expect(
Expand Down Expand Up @@ -698,7 +684,7 @@ describe("social sign on", () => {
});

const socialCallbackResponseQuery = new URLSearchParams(
socialCallbackResponse.headers.get("location")?.split("#")[1]!,
socialCallbackResponse.headers.get("location")?.split("#")[1],
);

const accessTokenPayload = parseJwt(
Expand Down
37 changes: 20 additions & 17 deletions test/integration/login/login-hint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,28 @@ it("should prefill email with login_hint if passed to /authorize", async () => {
query: { state: query.state },
});

const browser = await chromium.launch();
const page = await browser.newPage();

const responseText = await getSendCodeResponse.text();
const responseBody = responseText.replace(
"/css/tailwind.css",
"http://auth2.sesamy.dev/css/tailwind.css",
);
await page.setContent(responseBody);

// assert that username input is prefilled with this email address
const usernameInput = await page.$('input[name="username"]');
expect(usernameInput).not.toBeNull();
const usernameValue = await usernameInput!.getAttribute("value");
expect(usernameValue).toBe("suggested-email@example.com");

// @ts-ignore
// @ts-expect-error - dynamic import
if (import.meta.env.TEST_SNAPSHOTS === "true") {
const browser = await chromium.launch();
const page = await browser.newPage();

const responseText = await getSendCodeResponse.text();
const responseBody = responseText.replace(
"/css/tailwind.css",
"http://auth2.sesamy.dev/css/tailwind.css",
);
await page.setContent(responseBody);

// assert that username input is prefilled with this email address
const usernameInput = await page.$('input[name="username"]');
expect(usernameInput).not.toBeNull();
const usernameValue = await usernameInput!.getAttribute("value");
expect(usernameValue).toBe("suggested-email@example.com");

const snapshot = await page.screenshot();
expect(snapshot).toMatchImageSnapshot();

await browser.close();
}
});

Expand Down Expand Up @@ -138,6 +140,7 @@ it("should redirect the user back with a code if the email matches the current s
});

expect(tokenResponse.status).toBe(200);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const tokenBody: any = await tokenResponse.json();
expect(tokenBody.access_token).toBeTypeOf("string");
});
Loading
Loading