From 666d30b0c7231b30bef5c66f8d75f7892e1d092a Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 16 May 2024 12:43:44 +0200 Subject: [PATCH] refactor: test fixes --- lib/build/emailpasswordprebuiltui.js | 4 +- lib/build/index2.js | 14 +- .../recipe/emailpassword/prebuiltui.d.ts | 21 +- .../components/features/mfa/index.d.ts | 2 +- lib/build/recipe/thirdparty/index.d.ts | 3 +- .../components/feature/authPage/authPage.tsx | 16 +- lib/ts/recipe/emailpassword/prebuiltui.tsx | 9 +- .../components/features/mfa/index.tsx | 2 +- lib/ts/recipe/thirdparty/index.ts | 3 +- package-lock.json | 11 +- package.json | 2 +- test/constants.js | 1 + test/end-to-end/mfa.factorscreen.otp.test.js | 91 ++- ...multitenancy.dynamic_login_methods.test.js | 20 +- test/end-to-end/multitenancy.mock.test.js | 10 +- test/end-to-end/passwordless.test.js | 4 +- test/server/index.js | 615 +++++------------- test/server/package-lock.json | 13 +- test/server/package.json | 2 +- test/unit/componentOverrides.test.tsx | 126 +--- test/unit/exports.test.tsx | 26 +- .../emailpassword/emailPassword.test.tsx | 10 +- .../recipe/emailpassword/signInUp.test.tsx | 32 +- .../recipe/passwordless/passwordless.test.ts | 26 - .../recipe/passwordless/signInUp.test.tsx | 27 +- test/unit/recipe/thirdparty/signInUp.test.tsx | 29 +- .../unit/recipe/thirdparty/thirdParty.test.ts | 12 - test/unit/ssr.test.tsx | 1 + 28 files changed, 412 insertions(+), 720 deletions(-) diff --git a/lib/build/emailpasswordprebuiltui.js b/lib/build/emailpasswordprebuiltui.js index 9ac9a7dc3..55045452e 100644 --- a/lib/build/emailpasswordprebuiltui.js +++ b/lib/build/emailpasswordprebuiltui.js @@ -1438,9 +1438,7 @@ var EmailPasswordPreBuiltUI = /** @class */ (function (_super) { EmailPasswordPreBuiltUI.instance = undefined; return; }; - EmailPasswordPreBuiltUI.SignInFeature = SignInFeature; EmailPasswordPreBuiltUI.SignInTheme = SignInTheme; - EmailPasswordPreBuiltUI.SignUpFeature = SignUpFeature; EmailPasswordPreBuiltUI.SignUpTheme = SignUpTheme; EmailPasswordPreBuiltUI.ResetPasswordUsingToken = function (prop) { return EmailPasswordPreBuiltUI.getInstanceOrInitAndGetInstance().getFeatureComponent("resetpassword", prop); @@ -1453,3 +1451,5 @@ var ResetPasswordUsingToken = EmailPasswordPreBuiltUI.ResetPasswordUsingToken; exports.EmailPasswordPreBuiltUI = EmailPasswordPreBuiltUI; exports.ResetPasswordUsingToken = ResetPasswordUsingToken; exports.ResetPasswordUsingTokenTheme = ResetPasswordUsingTokenThemeWrapper; +exports.SignInTheme = SignInTheme; +exports.SignUpTheme = SignUpTheme; diff --git a/lib/build/index2.js b/lib/build/index2.js index 2f8ae6915..cbdb7ee4d 100644 --- a/lib/build/index2.js +++ b/lib/build/index2.js @@ -884,8 +884,8 @@ var AuthPageInner = function (props) { error = _g[0], setError = _g[1]; var _h = React.useState(false), - sessionLoadedAndDidNotExist = _h[0], - setSessionLoadedAndDidNotExist = _h[1]; + sessionLoadedAndNotRedirecting = _h[0], + setSessionLoadedAndNotRedirecting = _h[1]; var st = genericComponentOverrideContext.SuperTokens.getInstanceOrThrow(); var _j = React.useState(props.factors), factorList = _j[0], @@ -933,7 +933,7 @@ var AuthPageInner = function (props) { ); React.useEffect( function () { - if (sessionLoadedAndDidNotExist) { + if (sessionLoadedAndNotRedirecting) { return; } // we want to do this just once, so we supply it with only the loading state. @@ -956,9 +956,11 @@ var AuthPageInner = function (props) { props.navigate ) .catch(rethrowInRender); + } else { + setSessionLoadedAndNotRedirecting(true); } } else { - setSessionLoadedAndDidNotExist(true); + setSessionLoadedAndNotRedirecting(true); } } }, @@ -983,7 +985,7 @@ var AuthPageInner = function (props) { return; } if ( - sessionLoadedAndDidNotExist && + sessionLoadedAndNotRedirecting && (loadedDynamicLoginMethods !== undefined || !genericComponentOverrideContext.SuperTokens.usesDynamicLoginMethods) ) { @@ -1005,7 +1007,7 @@ var AuthPageInner = function (props) { }; }, [ - sessionLoadedAndDidNotExist, + sessionLoadedAndNotRedirecting, rebuildReqCount, setRebuildReqCount, props.preBuiltUIList, diff --git a/lib/build/recipe/emailpassword/prebuiltui.d.ts b/lib/build/recipe/emailpassword/prebuiltui.d.ts index 345ce54a6..f56328c92 100644 --- a/lib/build/recipe/emailpassword/prebuiltui.d.ts +++ b/lib/build/recipe/emailpassword/prebuiltui.d.ts @@ -127,21 +127,7 @@ export declare class EmailPasswordPreBuiltUI extends RecipeRouter { getAuthComponents(): AuthComponent[]; requiresSignUpPage: boolean; static reset(): void; - static SignInFeature: import("react").FC< - import("../../types").AuthComponentProps & { - recipe: EmailPassword; - userContext: UserContext; - useComponentOverrides: () => import("./types").ComponentOverrideMap; - } - >; static SignInTheme: typeof SignInTheme; - static SignUpFeature: import("react").FC< - import("../../types").AuthComponentProps & { - recipe: EmailPassword; - userContext?: UserContext | undefined; - useComponentOverrides: () => import("./types").ComponentOverrideMap; - } - >; static SignUpTheme: typeof SignUpTheme; static ResetPasswordUsingToken: ( prop: FeatureBaseProps<{ @@ -155,4 +141,9 @@ declare const ResetPasswordUsingToken: ( userContext?: UserContext; }> ) => JSX.Element; -export { ResetPasswordUsingToken, ResetPasswordUsingTokenThemeWrapper as ResetPasswordUsingTokenTheme }; +export { + ResetPasswordUsingToken, + SignInTheme, + SignUpTheme, + ResetPasswordUsingTokenThemeWrapper as ResetPasswordUsingTokenTheme, +}; diff --git a/lib/build/recipe/passwordless/components/features/mfa/index.d.ts b/lib/build/recipe/passwordless/components/features/mfa/index.d.ts index 431e2f182..9e7fc5adc 100644 --- a/lib/build/recipe/passwordless/components/features/mfa/index.d.ts +++ b/lib/build/recipe/passwordless/components/features/mfa/index.d.ts @@ -4,7 +4,7 @@ import type Recipe from "../../../recipe"; import type { ComponentOverrideMap, MFAChildProps } from "../../../types"; import type { MFAAction, MFAState } from "../../../types"; import type { RecipeInterface } from "supertokens-web-js/recipe/passwordless"; -import type { PasswordlessFlowType } from "supertokens-web-js/recipe/thirdpartypasswordless"; +import type { PasswordlessFlowType } from "supertokens-web-js/recipe/passwordless/types"; export declare const useFeatureReducer: () => [MFAState, React.Dispatch]; export declare function useChildProps( recipe: Recipe, diff --git a/lib/build/recipe/thirdparty/index.d.ts b/lib/build/recipe/thirdparty/index.d.ts index 830263734..4654d4ad8 100644 --- a/lib/build/recipe/thirdparty/index.d.ts +++ b/lib/build/recipe/thirdparty/index.d.ts @@ -15,8 +15,7 @@ import Okta from "./providers/okta"; import Twitter from "./providers/twitter"; import { UserInput, GetRedirectionURLContext, PreAPIHookContext, OnHandleEventContext } from "./types"; import type { UserContext } from "../../types"; -import type { StateObject } from "supertokens-web-js/recipe/thirdparty"; -import type { RecipeFunctionOptions } from "supertokens-web-js/recipe/thirdpartyemailpassword"; +import type { StateObject, RecipeFunctionOptions } from "supertokens-web-js/recipe/thirdparty"; import type { User } from "supertokens-web-js/types"; export default class Wrapper { static init(config?: UserInput): import("../../types").RecipeInitResult< diff --git a/lib/ts/recipe/authRecipe/components/feature/authPage/authPage.tsx b/lib/ts/recipe/authRecipe/components/feature/authPage/authPage.tsx index cd7544fa0..4b04c7f7c 100644 --- a/lib/ts/recipe/authRecipe/components/feature/authPage/authPage.tsx +++ b/lib/ts/recipe/authRecipe/components/feature/authPage/authPage.tsx @@ -39,7 +39,7 @@ import AuthPageThemeWrapper from "../../theme/authPage"; import type AuthRecipe from "../../.."; import type { TranslationStore } from "../../../../../translation/translationHelpers"; -import type { Navigate, PartialAuthComponent, UserContext } from "../../../../../types"; +import type { AuthComponent, Navigate, PartialAuthComponent, UserContext } from "../../../../../types"; import type { GetLoginMethodsResponseNormalized } from "../../../../multitenancy/types"; import type { RecipeRouter } from "../../../../recipeRouter"; import type { AuthPageThemeProps } from "../../../types"; @@ -102,7 +102,7 @@ const AuthPageInner: React.FC = (props) => { GetLoginMethodsResponseNormalized | undefined >(undefined); const [error, setError] = useState(errorFromQS); - const [sessionLoadedAndDidNotExist, setSessionLoadedAndDidNotExist] = useState(false); + const [sessionLoadedAndNotRedirecting, setSessionLoadedAndNotRedirecting] = useState(false); const st = SuperTokens.getInstanceOrThrow(); const [factorList, setFactorList] = useState(props.factors); const [isSignUp, setIsSignUp] = useState(props.isSignUp ?? isSignUpFromQS ?? st.defaultToSignUp); @@ -131,7 +131,7 @@ const AuthPageInner: React.FC = (props) => { }, [loadedDynamicLoginMethods, setLoadedDynamicLoginMethods]); useEffect(() => { - if (sessionLoadedAndDidNotExist) { + if (sessionLoadedAndNotRedirecting) { return; } @@ -155,9 +155,11 @@ const AuthPageInner: React.FC = (props) => { props.navigate ) .catch(rethrowInRender); + } else { + setSessionLoadedAndNotRedirecting(true); } } else { - setSessionLoadedAndDidNotExist(true); + setSessionLoadedAndNotRedirecting(true); } } }, [sessionContext.loading]); @@ -181,7 +183,7 @@ const AuthPageInner: React.FC = (props) => { } if ( - sessionLoadedAndDidNotExist && + sessionLoadedAndNotRedirecting && (loadedDynamicLoginMethods !== undefined || !SuperTokens.usesDynamicLoginMethods) ) { void buildAndSetChildProps( @@ -201,7 +203,7 @@ const AuthPageInner: React.FC = (props) => { abortCtl.abort(); }; }, [ - sessionLoadedAndDidNotExist, + sessionLoadedAndNotRedirecting, rebuildReqCount, setRebuildReqCount, props.preBuiltUIList, @@ -323,7 +325,7 @@ async function buildAndSetChildProps( const isSignUp = hasSeparateSignUpView && isSignUpState; - const authComps = []; + const authComps: AuthComponent[] = []; for (const ui of recipeRouters) { authComps.push(...ui.getAuthComponents()); diff --git a/lib/ts/recipe/emailpassword/prebuiltui.tsx b/lib/ts/recipe/emailpassword/prebuiltui.tsx index a2b331d53..79b24bc63 100644 --- a/lib/ts/recipe/emailpassword/prebuiltui.tsx +++ b/lib/ts/recipe/emailpassword/prebuiltui.tsx @@ -134,9 +134,7 @@ export class EmailPasswordPreBuiltUI extends RecipeRouter { return; } - static SignInFeature = SignInFeature; static SignInTheme = SignInTheme; - static SignUpFeature = SignUpFeature; static SignUpTheme = SignUpTheme; static ResetPasswordUsingToken = (prop: FeatureBaseProps<{ userContext?: UserContext }>) => @@ -147,4 +145,9 @@ export class EmailPasswordPreBuiltUI extends RecipeRouter { const ResetPasswordUsingToken = EmailPasswordPreBuiltUI.ResetPasswordUsingToken; -export { ResetPasswordUsingToken, ResetPasswordUsingTokenThemeWrapper as ResetPasswordUsingTokenTheme }; +export { + ResetPasswordUsingToken, + SignInTheme, + SignUpTheme, + ResetPasswordUsingTokenThemeWrapper as ResetPasswordUsingTokenTheme, +}; diff --git a/lib/ts/recipe/passwordless/components/features/mfa/index.tsx b/lib/ts/recipe/passwordless/components/features/mfa/index.tsx index 44de3776d..4bff46377 100644 --- a/lib/ts/recipe/passwordless/components/features/mfa/index.tsx +++ b/lib/ts/recipe/passwordless/components/features/mfa/index.tsx @@ -50,7 +50,7 @@ import type Recipe from "../../../recipe"; import type { AdditionalLoginAttemptInfoProperties, ComponentOverrideMap, MFAChildProps } from "../../../types"; import type { MFAAction, MFAState, NormalisedConfig } from "../../../types"; import type { RecipeInterface } from "supertokens-web-js/recipe/passwordless"; -import type { PasswordlessFlowType } from "supertokens-web-js/recipe/thirdpartypasswordless"; +import type { PasswordlessFlowType } from "supertokens-web-js/recipe/passwordless/types"; export const useFeatureReducer = (): [MFAState, React.Dispatch] => { return React.useReducer( diff --git a/lib/ts/recipe/thirdparty/index.ts b/lib/ts/recipe/thirdparty/index.ts index ae7c1b2f8..126364905 100644 --- a/lib/ts/recipe/thirdparty/index.ts +++ b/lib/ts/recipe/thirdparty/index.ts @@ -41,8 +41,7 @@ import { UserInput, GetRedirectionURLContext, PreAPIHookContext, OnHandleEventCo import { redirectToThirdPartyLogin as UtilsRedirectToThirdPartyLogin } from "./utils"; import type { UserContext } from "../../types"; -import type { StateObject } from "supertokens-web-js/recipe/thirdparty"; -import type { RecipeFunctionOptions } from "supertokens-web-js/recipe/thirdpartyemailpassword"; +import type { StateObject, RecipeFunctionOptions } from "supertokens-web-js/recipe/thirdparty"; import type { User } from "supertokens-web-js/types"; export default class Wrapper { diff --git a/package-lock.json b/package-lock.json index 66e4beb78..61e440657 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,7 +103,7 @@ "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0", - "supertokens-web-js": "^0.11.0" + "supertokens-web-js": "github:supertokens/supertokens-web-js#feat/remove_combo_recipes" } }, "eslint": { @@ -23890,8 +23890,8 @@ }, "node_modules/supertokens-web-js": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/supertokens-web-js/-/supertokens-web-js-0.11.0.tgz", - "integrity": "sha512-6jMmwkR+qi+cOpeymuczKqjHGK/9/1Ckfv8MUFN54k4/4Bs1FMF6wJofTuXOZHEZX80wKZmOHJKQERm8IhBWWg==", + "resolved": "git+ssh://git@github.com/supertokens/supertokens-web-js.git#e89306b258cd3579c4379ddf07a37831e5c79bf5", + "license": "Apache-2.0", "peer": true, "dependencies": { "supertokens-js-override": "0.0.4", @@ -43370,9 +43370,8 @@ "integrity": "sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg==" }, "supertokens-web-js": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/supertokens-web-js/-/supertokens-web-js-0.11.0.tgz", - "integrity": "sha512-6jMmwkR+qi+cOpeymuczKqjHGK/9/1Ckfv8MUFN54k4/4Bs1FMF6wJofTuXOZHEZX80wKZmOHJKQERm8IhBWWg==", + "version": "git+ssh://git@github.com/supertokens/supertokens-web-js.git#e89306b258cd3579c4379ddf07a37831e5c79bf5", + "from": "supertokens-web-js@github:supertokens/supertokens-web-js#feat/remove_combo_recipes", "peer": true, "requires": { "supertokens-js-override": "0.0.4", diff --git a/package.json b/package.json index 447db5352..0837e6164 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0", - "supertokens-web-js": "^0.11.0" + "supertokens-web-js": "github:supertokens/supertokens-web-js#feat/remove_combo_recipes" }, "scripts": { "init": "bash ./init.sh", diff --git a/test/constants.js b/test/constants.js index 0c03760d7..636e04db7 100644 --- a/test/constants.js +++ b/test/constants.js @@ -33,6 +33,7 @@ export const SEND_VERIFY_EMAIL_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/u export const VERIFY_EMAIL_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/user/email/verify`; export const SIGN_IN_UP_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/signinup`; export const CREATE_CODE_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/signinup/code`; +export const CONSUME_CODE_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/signinup/code/consume`; export const CREATE_TOTP_DEVICE_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/totp/device`; export const GET_AUTH_URL_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/authorisationurl`; export const LOGIN_METHODS_API = `${TEST_APPLICATION_SERVER_BASE_URL}/auth/loginmethods`; diff --git a/test/end-to-end/mfa.factorscreen.otp.test.js b/test/end-to-end/mfa.factorscreen.otp.test.js index 97af24508..14f132b5f 100644 --- a/test/end-to-end/mfa.factorscreen.otp.test.js +++ b/test/end-to-end/mfa.factorscreen.otp.test.js @@ -35,7 +35,13 @@ import { setAccountLinkingConfig, } from "../helpers"; import fetch from "isomorphic-fetch"; -import { CREATE_CODE_API, CREATE_TOTP_DEVICE_API, MFA_INFO_API, SOMETHING_WENT_WRONG_ERROR } from "../constants"; +import { + CONSUME_CODE_API, + CREATE_CODE_API, + CREATE_TOTP_DEVICE_API, + MFA_INFO_API, + SOMETHING_WENT_WRONG_ERROR, +} from "../constants"; import { TEST_CLIENT_BASE_URL, TEST_SERVER_BASE_URL } from "../constants"; import { getTestPhoneNumber } from "../exampleTestHelpers"; @@ -328,6 +334,89 @@ describe("SuperTokens SignIn w/ MFA", function () { } }); + it("should handle consumeCode restart flow error", async () => { + await setMFAInfo({ + requirements: [factorId], + alreadySetup: [factorId], + allowedToSetup: [factorId], + }); + + await page.setRequestInterception(true); + const requestHandler = (request) => { + if (request.url() === CONSUME_CODE_API && request.method() === "POST") { + return request.respond({ + status: 200, + headers: { + "access-control-allow-origin": TEST_CLIENT_BASE_URL, + "access-control-allow-credentials": "true", + }, + body: JSON.stringify({ + status: "RESTART_FLOW_ERROR", + }), + }); + } + + return request.continue(); + }; + page.on("request", requestHandler); + try { + await tryEmailPasswordSignIn(page, email); + + await completeOTP(page); + + await waitForAccessDenied(page); + } finally { + page.off("request", requestHandler); + await page.setRequestInterception(false); + } + }); + + it("should handle consumeCode restart flow error when setting up factor", async () => { + await setMFAInfo({ + requirements: [factorId], + alreadySetup: [], + allowedToSetup: [factorId], + noContacts: true, + }); + + await page.setRequestInterception(true); + const requestHandler = (request) => { + if (request.url() === CONSUME_CODE_API && request.method() === "POST") { + return request.respond({ + status: 200, + headers: { + "access-control-allow-origin": TEST_CLIENT_BASE_URL, + "access-control-allow-credentials": "true", + }, + body: JSON.stringify({ + status: "RESTART_FLOW_ERROR", + }), + }); + } + + return request.continue(); + }; + page.on("request", requestHandler); + try { + await tryEmailPasswordSignIn(page, email); + + await setInputValues(page, [ + contactMethod === "PHONE" + ? { name: "phoneNumber_text", value: getTestPhoneNumber() } + : { name: "email", value: await getTestEmail() }, + ]); + await submitForm(page); + + await completeOTP(page); + + const error = await getGeneralError(page); + assert.strictEqual("Login unsuccessful. Please try again.", error); + } finally { + page.off("request", requestHandler); + await page.setRequestInterception(false); + } + }); + it("should enable you to change the contact info during setup (w/ contact form)", async () => { await setMFAInfo({ requirements: [factorId], diff --git a/test/end-to-end/multitenancy.dynamic_login_methods.test.js b/test/end-to-end/multitenancy.dynamic_login_methods.test.js index b16d9883d..e61124c7c 100644 --- a/test/end-to-end/multitenancy.dynamic_login_methods.test.js +++ b/test/end-to-end/multitenancy.dynamic_login_methods.test.js @@ -525,7 +525,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartyemailpassword with emailpassword disabled if FE only has tpep but only thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, passwordless: { enabled: false }, @@ -554,7 +554,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartyemailpassword if FE has tpep and both emailpassword and thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: false }, @@ -639,7 +639,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartyemailpassword if FE has tpep and ep and both emailpassword and thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword", "emailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: false }, @@ -667,7 +667,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartypasswordless if FE has tppwless and ep and both emailpassword and thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartypasswordless", "emailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "passwordless", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: false }, @@ -695,7 +695,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartyemailpassword if FE has tpep and tppwless and all 3 enabled in core", async function () { - await setEnabledRecipes(page, ["thirdpartypasswordless", "thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "passwordless", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: true }, @@ -723,7 +723,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartypwless if rid has FE has tpep and tppwless and all 3 enabled in core", async function () { - await setEnabledRecipes(page, ["thirdpartypasswordless", "thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "passwordless", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: true }, @@ -751,7 +751,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartyemailpassword if FE has only tpep and thirdparty is disbled in core", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: false }, @@ -777,7 +777,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartyemailpassword if FE has only tpep and thirdparty is disbled in core", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, passwordless: { enabled: false }, @@ -803,7 +803,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartpasswordless if FE has only tppwless and thirdparty is disbled in core", async function () { - await setEnabledRecipes(page, ["thirdpartypasswordless"]); + await setEnabledRecipes(page, ["thirdparty", "passwordless"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, passwordless: { enabled: true }, @@ -828,7 +828,7 @@ describe("SuperTokens Multitenancy dynamic login methods", function () { }); it("should show thirdpartpasswordless if FE has only tppwless and passwordless is disbled in core", async function () { - await setEnabledRecipes(page, ["thirdpartypasswordless"]); + await setEnabledRecipes(page, ["thirdparty", "passwordless"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, passwordless: { enabled: false }, diff --git a/test/end-to-end/multitenancy.mock.test.js b/test/end-to-end/multitenancy.mock.test.js index a69b94642..2e479ddb9 100644 --- a/test/end-to-end/multitenancy.mock.test.js +++ b/test/end-to-end/multitenancy.mock.test.js @@ -419,7 +419,7 @@ describe.skip("SuperTokens Multitenancy w/ mocked login methods", function () { }); it("should should show thirdpartyemailpassword with emailpassword disabled if FE only has tpep but only thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: false }, passwordless: { enabled: false }, @@ -447,7 +447,7 @@ describe.skip("SuperTokens Multitenancy w/ mocked login methods", function () { }); it("should should show thirdpartyemailpassword if FE has tpep and both emailpassword and thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: false }, @@ -532,7 +532,7 @@ describe.skip("SuperTokens Multitenancy w/ mocked login methods", function () { }); it("should should show thirdpartyemailpassword if FE has tpep and ep and both emailpassword and thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartyemailpassword", "emailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: false }, @@ -560,7 +560,7 @@ describe.skip("SuperTokens Multitenancy w/ mocked login methods", function () { }); it("should should show thirdpartypasswordless if FE has tpep and ep and both emailpassword and thirdparty is enabled", async function () { - await setEnabledRecipes(page, ["thirdpartypasswordless", "emailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "passwordless", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: false }, @@ -588,7 +588,7 @@ describe.skip("SuperTokens Multitenancy w/ mocked login methods", function () { }); it("should should show thirdpartyemailpassword if FE has tpep and tppwless and all 3 enabled in core", async function () { - await setEnabledRecipes(page, ["thirdpartypasswordless", "thirdpartyemailpassword"]); + await setEnabledRecipes(page, ["thirdparty", "passwordless", "emailpassword"]); await enableDynamicLoginMethods(page, { emailPassword: { enabled: true }, passwordless: { enabled: true }, diff --git a/test/end-to-end/passwordless.test.js b/test/end-to-end/passwordless.test.js index 818837d35..f3fd80b29 100644 --- a/test/end-to-end/passwordless.test.js +++ b/test/end-to-end/passwordless.test.js @@ -1328,7 +1328,7 @@ export function getPasswordlessTestCases({ authRecipe, logId, generalErrorRecipe consoleLogs.length = 0; await Promise.all([ - page.goto(device.codes[0].urlWithLinkCode.replace(/[?&]preAuthSessionId=[^&#]+/, "")), + page.goto(device.codes[0].urlWithLinkCode.replace(/([?&])preAuthSessionId=[^&#]+&?/, "$1")), page.waitForNavigation({ waitUntil: "networkidle0" }), ]); @@ -1922,7 +1922,7 @@ async function initBrowser(contactMethod, consoleLogs, authRecipe, { defaultCoun const browser = await puppeteer.launch({ args: ["--no-sandbox", "--disable-setuid-sandbox", "--remote-debugging-port=9222"], - headless: false, + headless: true, }); const page = await browser.newPage(); page.on("console", (consoleObj) => { diff --git a/test/server/index.js b/test/server/index.js index bf5c87b9a..a59381352 100644 --- a/test/server/index.js +++ b/test/server/index.js @@ -18,15 +18,11 @@ let { default: SuperTokensRaw } = require("supertokens-node/lib/build/supertoken const { default: EmailVerificationRaw } = require("supertokens-node/lib/build/recipe/emailverification/recipe"); const { default: EmailPasswordRaw } = require("supertokens-node/lib/build/recipe/emailpassword/recipe"); const { default: ThirdPartyRaw } = require("supertokens-node/lib/build/recipe/thirdparty/recipe"); -const { - default: ThirdPartyEmailPasswordRaw, -} = require("supertokens-node/lib/build/recipe/thirdpartyemailpassword/recipe"); const { default: SessionRaw } = require("supertokens-node/lib/build/recipe/session/recipe"); let Session = require("supertokens-node/recipe/session"); let EmailPassword = require("supertokens-node/recipe/emailpassword"); let ThirdParty = require("supertokens-node/recipe/thirdparty"); let EmailVerification = require("supertokens-node/recipe/emailverification"); -let ThirdPartyEmailPassword = require("supertokens-node/recipe/thirdpartyemailpassword"); let { verifySession } = require("supertokens-node/recipe/session/framework/express"); let { middleware, errorHandler } = require("supertokens-node/framework/express"); let express = require("express"); @@ -49,88 +45,27 @@ let { let { version: nodeSDKVersion } = require("supertokens-node/lib/build/version"); const fetch = require("isomorphic-fetch"); -let passwordlessSupported; -let PasswordlessRaw; -let Passwordless; +const PasswordlessRaw = require("supertokens-node/lib/build/recipe/passwordless/recipe").default; +const Passwordless = require("supertokens-node/recipe/passwordless"); -try { - PasswordlessRaw = require("supertokens-node/lib/build/recipe/passwordless/recipe").default; - Passwordless = require("supertokens-node/recipe/passwordless"); - passwordlessSupported = true; -} catch (ex) { - passwordlessSupported = false; -} - -let thirdPartyPasswordlessSupported; -let ThirdPartyPasswordlessRaw; -let ThirdPartyPasswordless; - -try { - ThirdPartyPasswordlessRaw = require("supertokens-node/lib/build/recipe/thirdpartypasswordless/recipe").default; - ThirdPartyPasswordless = require("supertokens-node/recipe/thirdpartypasswordless"); - thirdPartyPasswordlessSupported = true; -} catch (ex) { - thirdPartyPasswordlessSupported = false; -} +const UserRolesRaw = require("supertokens-node/lib/build/recipe/userroles/recipe").default; +const UserRoles = require("supertokens-node/recipe/userroles"); -try { - UserRolesRaw = require("supertokens-node/lib/build/recipe/userroles/recipe").default; - UserRoles = require("supertokens-node/recipe/userroles"); - userRolesSupported = true; -} catch (ex) { - userRolesSupported = false; -} +const MultitenancyRaw = require("supertokens-node/lib/build/recipe/multitenancy/recipe").default; +const Multitenancy = require("supertokens-node/lib/build/recipe/multitenancy"); -let Multitenancy, MultitenancyRaw, multitenancySupported; -try { - MultitenancyRaw = require("supertokens-node/lib/build/recipe/multitenancy/recipe").default; - Multitenancy = require("supertokens-node/lib/build/recipe/multitenancy"); - multitenancySupported = true; -} catch { - multitenancySupported = false; -} +const AccountLinkingRaw = require("supertokens-node/lib/build/recipe/accountlinking/recipe").default; +const AccountLinking = require("supertokens-node/recipe/accountlinking"); -let AccountLinking, AccountLinkingRaw, accountLinkingSupported; -try { - AccountLinkingRaw = require("supertokens-node/lib/build/recipe/accountlinking/recipe").default; - AccountLinking = require("supertokens-node/recipe/accountlinking"); - accountLinkingSupported = true; -} catch (ex) { - accountLinkingSupported = false; -} +const UserMetadataRaw = require("supertokens-node/lib/build/recipe/usermetadata/recipe").default; +const UserMetadata = require("supertokens-node/recipe/usermetadata"); -/** @type {import("supertokens-node/recipe/usermetadata").default | undefined} */ -let UserMetadata; -let UserMetadataRaw, userMetadataSupported; -try { - UserMetadataRaw = require("supertokens-node/lib/build/recipe/usermetadata/recipe").default; - UserMetadata = require("supertokens-node/recipe/usermetadata"); - userMetadataSupported = true; -} catch (ex) { - userMetadataSupported = false; -} +const MultiFactorAuthRaw = require("supertokens-node/lib/build/recipe/multifactorauth/recipe").default; +const MultiFactorAuth = require("supertokens-node/recipe/multifactorauth"); -/** @type {import("supertokens-node/recipe/multifactorauth").default | undefined} */ -let MultiFactorAuth; -let MultiFactorAuthRaw, multiFactorAuthSupported; -try { - MultiFactorAuthRaw = require("supertokens-node/lib/build/recipe/multifactorauth/recipe").default; - MultiFactorAuth = require("supertokens-node/recipe/multifactorauth"); - multiFactorAuthSupported = true; -} catch (ex) { - multiFactorAuthSupported = false; -} +const TOTPRaw = require("supertokens-node/lib/build/recipe/totp/recipe").default; +const TOTP = require("supertokens-node/recipe/totp"); -/** @type {import("supertokens-node/recipe/totp").default | undefined} */ -let TOTP; -let TOTPRaw, totpSupported; -try { - TOTPRaw = require("supertokens-node/lib/build/recipe/totp/recipe").default; - TOTP = require("supertokens-node/recipe/totp"); - totpSupported = true; -} catch (ex) { - totpSupported = false; -} const OTPAuth = require("otpauth"); let generalErrorSupported; @@ -332,7 +267,7 @@ app.get("/sessioninfo", verifySession(), async (req, res, next) => { res.send({ sessionHandle: session.getHandle(), userId: session.getUserId(), - recipeUserId: accountLinkingSupported ? session.getRecipeUserId().getAsString() : session.getUserId(), + recipeUserId: session.getRecipeUserId().getAsString(), accessTokenPayload, sessionData, }); @@ -342,11 +277,6 @@ app.get("/sessioninfo", verifySession(), async (req, res, next) => { }); app.post("/deleteUser", async (req, res) => { - if (!accountLinkingSupported) { - const user = await EmailPassword.getUserByEmail("public", req.body.email); - return res.send(await SuperTokens.deleteUser(user.id)); - } - const users = await SuperTokens.listUsersByAccountInfo("public", req.body); res.send(await SuperTokens.deleteUser(users[0].id)); }); @@ -381,7 +311,7 @@ app.post("/changeEmail", async (req, res) => { app.get("/unverifyEmail", verifySession(), async (req, res) => { let session = req.session; - await EmailVerification.unverifyEmail(accountLinkingSupported ? session.getRecipeUserId() : session.getUserId()); + await EmailVerification.unverifyEmail(session.getRecipeUserId()); await session.fetchAndSetClaim(EmailVerification.EmailVerificationClaim, {}); res.send({ status: "OK" }); }); @@ -544,35 +474,14 @@ app.post("/test/getTOTPCode", (req, res) => { app.get("/test/featureFlags", (req, res) => { const available = []; - if (passwordlessSupported) { - available.push("passwordless"); - } - - if (thirdPartyPasswordlessSupported) { - available.push("thirdpartypasswordless"); - } - - if (generalErrorSupported) { - available.push("generalerror"); - } - - if (userRolesSupported) { - available.push("userroles"); - } - - if (multitenancySupported) { - available.push("multitenancy"); - available.push("multitenancyManagementEndpoints"); - } - - if (accountLinkingSupported) { - available.push("accountlinking"); - } - - if (multiFactorAuthSupported) { - available.push("mfa"); - } - + available.push("passwordless"); + available.push("thirdpartypasswordless"); + available.push("generalerror"); + available.push("userroles"); + available.push("multitenancy"); + available.push("multitenancyManagementEndpoints"); + available.push("accountlinking"); + available.push("mfa"); available.push("recipeConfig"); res.send({ @@ -615,42 +524,18 @@ server.listen(process.env.NODE_PORT === undefined ? 8080 : process.env.NODE_PORT function initST() { if (process.env.TEST_MODE) { mfaInfo = {}; - if (userRolesSupported) { - UserRolesRaw.reset(); - } - - if (thirdPartyPasswordlessSupported) { - ThirdPartyPasswordlessRaw.reset(); - } - - if (passwordlessSupported) { - PasswordlessRaw.reset(); - } - - if (multitenancySupported) { - MultitenancyRaw.reset(); - } - - if (accountLinkingSupported) { - AccountLinkingRaw.reset(); - } - - if (userMetadataSupported) { - UserMetadataRaw.reset(); - } - - if (multiFactorAuthSupported) { - MultiFactorAuthRaw.reset(); - } - if (totpSupported) { - TOTPRaw.reset(); - } + UserRolesRaw.reset(); + PasswordlessRaw.reset(); + MultitenancyRaw.reset(); + AccountLinkingRaw.reset(); + UserMetadataRaw.reset(); + MultiFactorAuthRaw.reset(); + TOTPRaw.reset(); EmailVerificationRaw.reset(); EmailPasswordRaw.reset(); ThirdPartyRaw.reset(); - ThirdPartyEmailPasswordRaw.reset(); SessionRaw.reset(); SuperTokensRaw.reset(); @@ -822,109 +707,6 @@ function initST() { }, }), ], - [ - "thirdpartyemailpassword", - ThirdPartyEmailPassword.init({ - signUpFeature: { - formFields, - }, - emailDelivery: { - override: (oI) => { - return { - ...oI, - sendEmail: async (input) => { - console.log(input.passwordResetLink); - latestURLWithToken = input.passwordResetLink; - }, - }; - }, - }, - providers: - enabledProviders !== undefined - ? fullProviderList.filter((config) => enabledProviders.includes(config.config)) - : fullProviderList, - override: { - apis: (originalImplementation) => { - return { - ...originalImplementation, - emailPasswordSignUpPOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API sign up", - }; - } - - return originalImplementation.emailPasswordSignUpPOST(input); - }, - passwordResetPOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API reset password consume", - }; - } - return originalImplementation.passwordResetPOST(input); - }, - generatePasswordResetTokenPOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API reset password", - }; - } - return originalImplementation.generatePasswordResetTokenPOST(input); - }, - emailPasswordEmailExistsGET: async function (input) { - let generalError = input.options.req.getKeyValueFromQuery("generalError"); - if (generalError === "true") { - return { - status: "GENERAL_ERROR", - message: "general error from API email exists", - }; - } - return originalImplementation.emailPasswordEmailExistsGET(input); - }, - emailPasswordSignInPOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API sign in", - }; - } - return originalImplementation.emailPasswordSignInPOST(input); - }, - authorisationUrlGET: async function (input) { - let generalErrorFromQuery = input.options.req.getKeyValueFromQuery("generalError"); - if (generalErrorFromQuery === "true") { - return { - status: "GENERAL_ERROR", - message: "general error from API authorisation url get", - }; - } - - return originalImplementation.authorisationUrlGET(input); - }, - thirdPartySignInUpPOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API sign in up", - }; - } - - return originalImplementation.thirdPartySignInUpPOST(input); - }, - }; - }, - }, - }), - ], [ "session", Session.init({ @@ -971,142 +753,65 @@ function initST() { }, ...passwordlessConfig, }; - if (passwordlessSupported) { - recipeList.push([ - "passwordless", - Passwordless.init({ - ...passwordlessConfig, - override: { - apis: (originalImplementation) => { - return { - ...originalImplementation, - createCodePOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API create code", - }; - } - return originalImplementation.createCodePOST(input); - }, - resendCodePOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API resend code", - }; - } - return originalImplementation.resendCodePOST(input); - }, - consumeCodePOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API consume code", - }; - } - - const resp = await originalImplementation.consumeCodePOST(input); - - return resp; - }, - }; - }, - }, - }), - ]); - } - if (thirdPartyPasswordlessSupported) { - recipeList.push([ - "thirdpartypasswordless", - ThirdPartyPasswordless.init({ - ...passwordlessConfig, - providers: - enabledProviders !== undefined - ? fullProviderList.filter((config) => enabledProviders.includes(config.config)) - : fullProviderList, - override: { - apis: (originalImplementation) => { - return { - ...originalImplementation, - authorisationUrlGET: async function (input) { - let generalErrorFromQuery = input.options.req.getKeyValueFromQuery("generalError"); - if (generalErrorFromQuery === "true") { - return { - status: "GENERAL_ERROR", - message: "general error from API authorisation url get", - }; - } + recipeList.push([ + "passwordless", + Passwordless.init({ + ...passwordlessConfig, + override: { + apis: (originalImplementation) => { + return { + ...originalImplementation, + createCodePOST: async function (input) { + let body = await input.options.req.getJSONBody(); + if (body.generalError === true) { + return { + status: "GENERAL_ERROR", + message: "general error from API create code", + }; + } + return originalImplementation.createCodePOST(input); + }, + resendCodePOST: async function (input) { + let body = await input.options.req.getJSONBody(); + if (body.generalError === true) { + return { + status: "GENERAL_ERROR", + message: "general error from API resend code", + }; + } + return originalImplementation.resendCodePOST(input); + }, + consumeCodePOST: async function (input) { + let body = await input.options.req.getJSONBody(); + if (body.generalError === true) { + return { + status: "GENERAL_ERROR", + message: "general error from API consume code", + }; + } - return originalImplementation.authorisationUrlGET(input); - }, - thirdPartySignInUpPOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API sign in up", - }; - } + const resp = await originalImplementation.consumeCodePOST(input); - return originalImplementation.thirdPartySignInUpPOST(input); - }, - createCodePOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API create code", - }; - } - return originalImplementation.createCodePOST(input); - }, - resendCodePOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API resend code", - }; - } - return originalImplementation.resendCodePOST(input); - }, - consumeCodePOST: async function (input) { - let body = await input.options.req.getJSONBody(); - if (body.generalError === true) { - return { - status: "GENERAL_ERROR", - message: "general error from API consume code", - }; - } - return originalImplementation.consumeCodePOST(input); - }, - }; - }, + return resp; + }, + }; }, - }), - ]); - } + }, + }), + ]); - if (userRolesSupported) { - recipeList.push(["userroles", UserRoles.init()]); - } + recipeList.push(["userroles", UserRoles.init()]); - if (multitenancySupported) { - recipeList.push([ - "multitenancy", - Multitenancy.init({ - getAllowedDomainsForTenantId: (tenantId) => [ - `${tenantId}.example.com`, - websiteDomain.replace(/https?:\/\/([^:\/]*).*/, "$1"), - ], - }), - ]); - } + recipeList.push([ + "multitenancy", + Multitenancy.init({ + getAllowedDomainsForTenantId: (tenantId) => [ + `${tenantId}.example.com`, + websiteDomain.replace(/https?:\/\/([^:\/]*).*/, "$1"), + ], + }), + ]); accountLinkingConfig = { enabled: false, @@ -1117,90 +822,84 @@ function initST() { }, ...accountLinkingConfig, }; - if (accountLinkingSupported) { - if (accountLinkingConfig.enabled) { - recipeList.push([ - "accountlinking", - AccountLinking.init({ - shouldDoAutomaticAccountLinking: () => ({ - ...accountLinkingConfig.shouldAutoLink, - }), - }), - ]); - } - } - if (multiFactorAuthSupported) { + if (accountLinkingConfig.enabled) { recipeList.push([ - "multifactorauth", - MultiFactorAuth.init({ - firstFactors: mfaInfo.firstFactors, - override: { - functions: (oI) => ({ - ...oI, - getFactorsSetupForUser: async (input) => { - const res = await oI.getFactorsSetupForUser(input); - if (mfaInfo?.alreadySetup) { - return mfaInfo.alreadySetup; - } - return res; - }, - assertAllowedToSetupFactorElseThrowInvalidClaimError: async (input) => { - if (mfaInfo?.allowedToSetup) { - if (!mfaInfo.allowedToSetup.includes(input.factorId)) { - throw new Session.Error({ - type: "INVALID_CLAIMS", - message: "INVALID_CLAIMS", - payload: [ - { - id: "test", - reason: "test override", - }, - ], - }); - } - } else { - await oI.assertAllowedToSetupFactorElseThrowInvalidClaimError(input); - } - }, - getMFARequirementsForAuth: async (input) => { - const res = await oI.getMFARequirementsForAuth(input); - if (mfaInfo?.requirements) { - return mfaInfo.requirements; - } - return res; - }, - }), - apis: (oI) => ({ - ...oI, - resyncSessionAndFetchMFAInfoPUT: async (input) => { - const res = await oI.resyncSessionAndFetchMFAInfoPUT(input); - - if (res.status === "OK") { - if (mfaInfo.alreadySetup) { - res.factors.alreadySetup = [...mfaInfo.alreadySetup]; - } - } - if (mfaInfo.noContacts) { - res.emails = {}; - res.phoneNumbers = {}; - } - return res; - }, - }), - }, + "accountlinking", + AccountLinking.init({ + shouldDoAutomaticAccountLinking: () => ({ + ...accountLinkingConfig.shouldAutoLink, + }), }), ]); } + recipeList.push([ + "multifactorauth", + MultiFactorAuth.init({ + firstFactors: mfaInfo.firstFactors, + override: { + functions: (oI) => ({ + ...oI, + getFactorsSetupForUser: async (input) => { + const res = await oI.getFactorsSetupForUser(input); + if (mfaInfo?.alreadySetup) { + return mfaInfo.alreadySetup; + } + return res; + }, + assertAllowedToSetupFactorElseThrowInvalidClaimError: async (input) => { + if (mfaInfo?.allowedToSetup) { + if (!mfaInfo.allowedToSetup.includes(input.factorId)) { + throw new Session.Error({ + type: "INVALID_CLAIMS", + message: "INVALID_CLAIMS", + payload: [ + { + id: "test", + reason: "test override", + }, + ], + }); + } + } else { + await oI.assertAllowedToSetupFactorElseThrowInvalidClaimError(input); + } + }, + getMFARequirementsForAuth: async (input) => { + const res = await oI.getMFARequirementsForAuth(input); + if (mfaInfo?.requirements) { + return mfaInfo.requirements; + } + return res; + }, + }), + apis: (oI) => ({ + ...oI, + resyncSessionAndFetchMFAInfoPUT: async (input) => { + const res = await oI.resyncSessionAndFetchMFAInfoPUT(input); - if (totpSupported) { - recipeList.push([ - "totp", - TOTP.init({ - defaultPeriod: 1, - defaultSkew: 30, - }), - ]); - } + if (res.status === "OK") { + if (mfaInfo.alreadySetup) { + res.factors.alreadySetup = [...mfaInfo.alreadySetup]; + } + } + if (mfaInfo.noContacts) { + res.emails = {}; + res.phoneNumbers = {}; + } + return res; + }, + }), + }, + }), + ]); + + recipeList.push([ + "totp", + TOTP.init({ + defaultPeriod: 1, + defaultSkew: 30, + }), + ]); SuperTokens.init({ appInfo: { diff --git a/test/server/package-lock.json b/test/server/package-lock.json index eb2e83d05..981670f8e 100644 --- a/test/server/package-lock.json +++ b/test/server/package-lock.json @@ -16,7 +16,7 @@ "express": "4.17.1", "morgan": "^1.10.0", "otpauth": "^9.2.0", - "supertokens-node": "^17.0.3" + "supertokens-node": "github:supertokens/supertokens-node#base/remove-combination-recipe" } }, "node_modules/accepts": { @@ -1041,9 +1041,9 @@ "integrity": "sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg==" }, "node_modules/supertokens-node": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/supertokens-node/-/supertokens-node-17.0.3.tgz", - "integrity": "sha512-1WiIJLWn7RGa7KDSJKsCLM66ftfFFz7902K70kwK9aWvD6h8trmM2ajlzc9Ooa6IQwWvzbOBTUWcUIP4+ObWmg==", + "version": "17.1.0", + "resolved": "git+ssh://git@github.com/supertokens/supertokens-node.git#0db22dc2269e6078f3343b403cc14cc24d0ec94b", + "license": "Apache-2.0", "dependencies": { "content-type": "^1.0.5", "cookie": "0.4.0", @@ -2013,9 +2013,8 @@ "integrity": "sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg==" }, "supertokens-node": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/supertokens-node/-/supertokens-node-17.0.3.tgz", - "integrity": "sha512-1WiIJLWn7RGa7KDSJKsCLM66ftfFFz7902K70kwK9aWvD6h8trmM2ajlzc9Ooa6IQwWvzbOBTUWcUIP4+ObWmg==", + "version": "git+ssh://git@github.com/supertokens/supertokens-node.git#0db22dc2269e6078f3343b403cc14cc24d0ec94b", + "from": "supertokens-node@github:supertokens/supertokens-node#base/remove-combination-recipe", "requires": { "content-type": "^1.0.5", "cookie": "0.4.0", diff --git a/test/server/package.json b/test/server/package.json index a1b3a68ca..c75d006b9 100644 --- a/test/server/package.json +++ b/test/server/package.json @@ -16,6 +16,6 @@ "express": "4.17.1", "morgan": "^1.10.0", "otpauth": "^9.2.0", - "supertokens-node": "^17.0.3" + "supertokens-node": "github:supertokens/supertokens-node#base/remove-combination-recipe" } } diff --git a/test/unit/componentOverrides.test.tsx b/test/unit/componentOverrides.test.tsx index 4c3b721da..36509f59b 100644 --- a/test/unit/componentOverrides.test.tsx +++ b/test/unit/componentOverrides.test.tsx @@ -9,45 +9,32 @@ import { ComponentOverrideMap as EmailVerificationOverrideMap } from "../../lib/ import { ComponentOverrideMap as PasswordlessOverrideMap } from "../../lib/ts/recipe/passwordless/types"; import { ComponentOverrideMap as TOTPOverrideMap } from "../../lib/ts/recipe/totp/types"; import { ComponentOverrideMap as MFAOverrideMap } from "../../lib/ts/recipe/multifactorauth/types"; +import { ComponentOverrideMap as AuthRecipeOverrideMap } from "../../lib/ts/recipe/authRecipe/types"; import "@testing-library/jest-dom"; import EmailPassword from "../../lib/ts/recipe/emailpassword/recipe"; import { SessionContextType } from "../../lib/ts/recipe/session"; import Session from "../../lib/ts/recipe/session/recipe"; import SuperTokens from "../../lib/ts/superTokens"; -import { EmailPasswordComponentsOverrideProvider } from "../../lib/ts/recipe/emailpassword"; -import { SignInAndUp as SignInAndUpEmailPassword } from "../../lib/ts/recipe/emailpassword/prebuiltui"; -import { SignUp } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signUp"; -import { SignUpHeader } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signUpHeader"; -import { SignInHeader } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signInHeader"; import { ResetPasswordEmail } from "../../lib/ts/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail"; -import { SignIn } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signIn"; -import { SignInFooter } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signInFooter"; -import { SignInForm } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signInForm"; -import { SignUpFooter as EmailPasswordSignUpFooter } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signUpFooter"; -import { SignUpForm } from "../../lib/ts/recipe/emailpassword/components/themes/signInAndUp/signUpForm"; +import { SignInForm } from "../../lib/ts/recipe/emailpassword/components/themes/signIn"; +import { SignUpForm } from "../../lib/ts/recipe/emailpassword/components/themes/signUp"; import { SubmitNewPassword } from "../../lib/ts/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword"; -import { SignUpFooter as ThirdPartySignUpFooter } from "../../lib/ts/recipe/thirdparty/components/themes/signInAndUp/signUpFooter"; -import { SignInAndUpHeader as ThirdPartySignInAndUpHeader } from "../../lib/ts/recipe/thirdparty/components/themes/signInAndUp/signInAndUpHeader"; import { ProvidersForm } from "../../lib/ts/recipe/thirdparty/components/themes/signInAndUp/providersForm"; import { SignInAndUpCallbackTheme } from "../../lib/ts/recipe/thirdparty/components/themes/signInAndUpCallback"; import { SendVerifyEmail } from "../../lib/ts/recipe/emailverification/components/themes/emailVerification/sendVerifyEmail"; import { VerifyEmailLinkClicked } from "../../lib/ts/recipe/emailverification/components/themes/emailVerification/verifyEmailLinkClicked"; import { ComponentOverride } from "../../lib/ts/components/componentOverride/componentOverride"; import { LinkClickedScreen } from "../../lib/ts/recipe/passwordless/components/themes/linkClickedScreen"; -import { LinkSent } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/linkSent"; -import { UserInputCodeFormFooter } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/userInputCodeFormFooter"; -import { UserInputCodeFormHeader } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/userInputCodeFormHeader"; -import { UserInputCodeForm } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/userInputCodeForm"; +import { LinkSent } from "../../lib/ts/recipe/passwordless/components/themes/linkSent/index"; +import { ContinueWithPasswordlessTheme } from "../../lib/ts/recipe/passwordless/components/themes/continueWithPasswordless"; +import { UserInputCodeForm } from "../../lib/ts/recipe/passwordless/components/themes/userInputCodeForm/userInputCodeFormScreen"; +import { UserInputCodeFormHeader } from "../../lib/ts/recipe/passwordless/components/themes/userInputCodeForm/userInputCodeFormHeader"; +import { UserInputCodeFormFooter } from "../../lib/ts/recipe/passwordless/components/themes/userInputCodeForm/userInputCodeFormFooter"; import { EmailForm } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/emailForm"; -import { SignInUpFooter } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/signInUpFooter"; -import { SignInUpHeader } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/signInUpHeader"; import { PhoneForm } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/phoneForm"; import { EmailOrPhoneForm } from "../../lib/ts/recipe/passwordless/components/themes/signInUp/emailOrPhoneForm"; -import ThirdParty from "../../lib/ts/recipe/thirdparty/recipe"; -import { Google, ThirdpartyComponentsOverrideProvider } from "../../lib/ts/recipe/thirdparty"; -import { SignInAndUpCallback } from "../../lib/ts/recipe/thirdparty/prebuiltui"; import { MFAFooter } from "../../lib/ts/recipe/passwordless/components/themes/mfa/mfaFooter"; import { MFAHeader } from "../../lib/ts/recipe/passwordless/components/themes/mfa/mfaHeader"; import { MFAOTPFooter } from "../../lib/ts/recipe/passwordless/components/themes/mfa/mfaOTPFooter"; @@ -65,8 +52,10 @@ import { FactorChooserFooter } from "../../lib/ts/recipe/multifactorauth/compone import { FactorChooserHeader } from "../../lib/ts/recipe/multifactorauth/components/themes/factorChooser/factorChooserHeader"; import { FactorList } from "../../lib/ts/recipe/multifactorauth/components/themes/factorChooser/factorList"; import { FactorOption } from "../../lib/ts/recipe/multifactorauth/components/themes/factorChooser/factorOption"; +import { AuthPageComponentList, AuthPageFooter, AuthPageHeader } from "../../lib/ts/ui"; -type AllComponentsOverrideMap = EmailPasswordOverrideMap & +type AllComponentsOverrideMap = AuthRecipeOverrideMap & + EmailPasswordOverrideMap & ThirdPartyOverrideMap & EmailVerificationOverrideMap & PasswordlessOverrideMap & @@ -79,22 +68,31 @@ const WithProvider: React.FC = ({ overrideMap, children }) => { }; describe("Theme component overrides", () => { + beforeEach(() => { + jest.clearAllMocks(); + SuperTokens.reset(); + + SuperTokens.init({ + useShadowDom: false, + appInfo: { + apiBasePath: "/auth", + apiDomain: "http://localhost:3001", + appName: "JestTest", + websiteBasePath: "/auth", + websiteDomain: "http://localhost:3000", + }, + recipeList: [], + }); + }); + const overrides: { // Required ensures that we cover all available overrides in tests [K in keyof Required]: any; } = { - EmailPasswordSignUpHeader_Override: SignUpHeader, - EmailPasswordSignInHeader_Override: SignInHeader, EmailPasswordResetPasswordEmail_Override: ResetPasswordEmail, - EmailPasswordSignIn_Override: SignIn, - EmailPasswordSignInFooter_Override: SignInFooter, EmailPasswordSignInForm_Override: SignInForm, - EmailPasswordSignUp_Override: SignUp, - EmailPasswordSignUpFooter_Override: EmailPasswordSignUpFooter, EmailPasswordSignUpForm_Override: SignUpForm, EmailPasswordSubmitNewPassword_Override: SubmitNewPassword, - ThirdPartySignUpFooter_Override: ThirdPartySignUpFooter, - ThirdPartySignInAndUpHeader_Override: ThirdPartySignInAndUpHeader, ThirdPartySignInAndUpProvidersForm_Override: ProvidersForm, ThirdPartySignInAndUpCallbackTheme_Override: SignInAndUpCallbackTheme, EmailVerificationSendVerifyEmail_Override: SendVerifyEmail, @@ -102,13 +100,12 @@ describe("Theme component overrides", () => { PasswordlessEmailForm_Override: EmailForm, PasswordlessPhoneForm_Override: PhoneForm, PasswordlessEmailOrPhoneForm_Override: EmailOrPhoneForm, - PasswordlessSignInUpFooter_Override: SignInUpFooter, - PasswordlessSignInUpHeader_Override: SignInUpHeader, PasswordlessUserInputCodeForm_Override: UserInputCodeForm, PasswordlessUserInputCodeFormFooter_Override: UserInputCodeFormFooter, PasswordlessUserInputCodeFormHeader_Override: UserInputCodeFormHeader, PasswordlessLinkSent_Override: LinkSent, PasswordlessLinkClickedScreen_Override: LinkClickedScreen, + PasswordlessContinueWithPasswordless_Override: ContinueWithPasswordlessTheme, PasswordlessMFAFooter_Override: MFAFooter, PasswordlessMFAHeader_Override: MFAHeader, PasswordlessMFAOTPFooter_Override: MFAOTPFooter, @@ -126,6 +123,9 @@ describe("Theme component overrides", () => { MFAFactorChooserHeader_Override: FactorChooserHeader, MFAFactorList_Override: [FactorList, { availableFactors: [] }], MFAFactorOption_Override: [FactorOption, { logo: () =>

!

}], + AuthPageComponentList_Override: AuthPageComponentList, + AuthPageFooter_Override: AuthPageFooter, + AuthPageHeader_Override: AuthPageHeader, }; Object.entries(overrides).forEach(([key, comp]) => { @@ -176,65 +176,3 @@ describe("Theme component overrides", () => { }); }); }); - -const MockSession = { - addEventListener: jest.fn(), - getUserId: jest.fn(), - getAccessTokenPayloadSecurely: jest.fn(), - doesSessionExist: jest.fn(), - validateClaims: jest.fn(), - validateGlobalClaimsAndHandleSuccessRedirection: jest.fn().mockReturnValue(new Promise(() => {})), -}; - -const setMockResolvesSession = (ctx: SessionContextType) => { - if (ctx.loading === true) { - // We "simulate" loading by returning these promises that won't ever resolve - MockSession.getUserId.mockReturnValue(new Promise(() => {})); - MockSession.getAccessTokenPayloadSecurely.mockReturnValue(new Promise(() => {})); - MockSession.doesSessionExist.mockReturnValue(new Promise(() => {})); - MockSession.validateClaims.mockReturnValue(new Promise(() => {})); - } else { - MockSession.getUserId.mockResolvedValue(ctx.userId); - MockSession.getAccessTokenPayloadSecurely.mockResolvedValue(ctx.accessTokenPayload); - MockSession.doesSessionExist.mockResolvedValue(ctx.doesSessionExist); - MockSession.validateClaims.mockReturnValue(ctx.invalidClaims); - } -}; - -jest.spyOn(Session, "getInstanceOrThrow").mockImplementation(() => MockSession as any); - -describe("Components override per recipe provider", () => { - beforeEach(() => { - jest.clearAllMocks(); - SuperTokens.reset(); - EmailPassword.reset(); - - SuperTokens.init({ - appInfo: { - apiBasePath: "/auth", - apiDomain: "http://localhost:3001", - appName: "JestTest", - websiteBasePath: "/auth", - websiteDomain: "http://localhost:3000", - }, - recipeList: [ - ThirdParty.init({ - signInAndUpFeature: { - providers: [Google.init()], - }, - useShadowDom: false, - }), - EmailPassword.init({ - useShadowDom: false, - }), - ], - }); - setMockResolvesSession({ - userId: "mock-user-id", - accessTokenPayload: {}, - invalidClaims: [], - doesSessionExist: false, - loading: false, - }); - }); -}); diff --git a/test/unit/exports.test.tsx b/test/unit/exports.test.tsx index 99e7ba588..23db5d51e 100644 --- a/test/unit/exports.test.tsx +++ b/test/unit/exports.test.tsx @@ -22,8 +22,8 @@ describe("Exports", function () { let { ResetPasswordUsingToken, ResetPasswordUsingTokenTheme, - SignInAndUp, - SignInAndUpTheme, + SignInTheme, + SignUpTheme, } = require("../../recipe/emailpassword/prebuiltui"); assert(init !== undefined && _default.init !== undefined); assert(doesEmailExist !== undefined && _default.doesEmailExist !== undefined); @@ -38,8 +38,8 @@ describe("Exports", function () { ResetPasswordUsingTokenTheme !== undefined && _defaultPreBuiltUI.ResetPasswordUsingTokenTheme !== undefined ); - assert(SignInAndUp !== undefined && _defaultPreBuiltUI.SignInAndUp !== undefined); - assert(SignInAndUpTheme !== undefined && _defaultPreBuiltUI.SignInAndUpTheme !== undefined); + assert(SignInTheme !== undefined && SignInTheme === _defaultPreBuiltUI.SignInTheme); + assert(SignUpTheme !== undefined && SignUpTheme === _defaultPreBuiltUI.SignUpTheme); }); }); @@ -61,7 +61,6 @@ describe("Exports", function () { } = require("../../recipe/thirdparty"); let _defaultPreBuiltUI = require("../../recipe/thirdparty/prebuiltui"); let { - SignInAndUp, SignInAndUpCallback, SignInAndUpCallbackTheme, SignInAndUpTheme, @@ -81,7 +80,6 @@ describe("Exports", function () { assert(Apple !== undefined && _default.Apple !== undefined); assert(Github !== undefined && _default.Github !== undefined); assert(Google !== undefined && _default.Google !== undefined); - assert(SignInAndUp !== undefined && _defaultPreBuiltUI.SignInAndUp !== undefined); assert(SignInAndUpCallback !== undefined && _defaultPreBuiltUI.SignInAndUpCallback !== undefined); assert(SignInAndUpCallbackTheme !== undefined && _defaultPreBuiltUI.SignInAndUpCallbackTheme !== undefined); assert(SignInAndUpTheme !== undefined && _defaultPreBuiltUI.SignInAndUpTheme !== undefined); @@ -106,7 +104,13 @@ describe("Exports", function () { signOut, } = require("../../recipe/passwordless"); let _defaultPreBuiltUI = require("../../recipe/passwordless/prebuiltui"); - let { LinkClicked, SignInUp, SignInUpTheme } = require("../../recipe/passwordless/prebuiltui"); + let { + LinkClicked, + MfaOtpPhone, + MfaOtpEmail, + MFAOTPTheme, + SignInUpTheme, + } = require("../../recipe/passwordless/prebuiltui"); assert(consumeCode !== undefined && _default.consumeCode !== undefined); assert(createCode !== undefined && _default.createCode !== undefined); @@ -121,9 +125,11 @@ describe("Exports", function () { assert(setLoginAttemptInfo !== undefined && _default.setLoginAttemptInfo !== undefined); assert(signOut !== undefined && _default.signOut !== undefined); - assert(LinkClicked !== undefined && _defaultPreBuiltUI.LinkClicked !== undefined); - assert(SignInUp !== undefined && _defaultPreBuiltUI.SignInUp !== undefined); - assert(SignInUpTheme !== undefined && _defaultPreBuiltUI.SignInUpTheme !== undefined); + assert(LinkClicked !== undefined && _defaultPreBuiltUI.LinkClicked === LinkClicked); + assert(MfaOtpPhone !== undefined && _defaultPreBuiltUI.MfaOtpPhone === MfaOtpPhone); + assert(MfaOtpEmail !== undefined && _defaultPreBuiltUI.MfaOtpEmail === MfaOtpEmail); + assert(MFAOTPTheme !== undefined && _defaultPreBuiltUI.MFAOTPTheme === MFAOTPTheme); + assert(SignInUpTheme !== undefined && _defaultPreBuiltUI.SignInUpTheme === SignInUpTheme); }); }); }); diff --git a/test/unit/recipe/emailpassword/emailPassword.test.tsx b/test/unit/recipe/emailpassword/emailPassword.test.tsx index 0ad92d43e..9eed076a3 100644 --- a/test/unit/recipe/emailpassword/emailPassword.test.tsx +++ b/test/unit/recipe/emailpassword/emailPassword.test.tsx @@ -79,7 +79,7 @@ describe("EmailPassword", function () { ["", "test", "test123", "Str0ngP@ssword"], ] ); - assert(EmailPasswordPreBuiltUI.getFeatures()["/auth"] !== undefined); + assert(EmailPassword.getInstanceOrThrow().getFirstFactorsForAuthPage().length !== 0); assert(EmailPasswordPreBuiltUI.getFeatures()["/auth/reset-password"] !== undefined); assert(EmailPasswordPreBuiltUI.getFeatures()["/auth/verify-email"] === undefined); assert.deepStrictEqual(EmailPassword.getInstanceOrThrow().config.recipeId, "emailpassword"); @@ -89,18 +89,14 @@ describe("EmailPassword", function () { EmailPassword.init({ signInAndUpFeature: { disableDefaultUI: true, - signUpForm: { - privacyPolicyLink, - termsOfServiceLink, - }, signInForm: {}, }, resetPasswordUsingTokenFeature: { disableDefaultUI: true, }, }).authReact(SuperTokens.getInstanceOrThrow().appInfo, false); - assert(EmailPasswordPreBuiltUI.getFeatures()["/auth"] === undefined); - assert(EmailPasswordPreBuiltUI.getFeatures()["/auth/reset-password"] === undefined); + assert.strictEqual(EmailPassword.getInstanceOrThrow().getFirstFactorsForAuthPage().length, 0); + assert.strictEqual(EmailPasswordPreBuiltUI.getFeatures()["/auth/reset-password"], undefined); }); it("Initializing EmailPassword with optional custom Fields for SignUp", async function () { diff --git a/test/unit/recipe/emailpassword/signInUp.test.tsx b/test/unit/recipe/emailpassword/signInUp.test.tsx index f256ce3da..f7bd37de7 100644 --- a/test/unit/recipe/emailpassword/signInUp.test.tsx +++ b/test/unit/recipe/emailpassword/signInUp.test.tsx @@ -4,7 +4,8 @@ import { render, waitFor } from "@testing-library/react"; import SuperTokens from "../../../../lib/ts/superTokens"; import Session from "../../../../lib/ts/recipe/session/recipe"; import Recipe from "../../../../lib/ts/recipe/emailpassword/recipe"; -import { SignInAndUp } from "../../../../lib/ts/recipe/emailpassword/prebuiltui"; +import { AuthPage } from "../../../../lib/ts/ui/index"; +import { EmailPasswordPreBuiltUI } from "../../../../lib/ts/recipe/emailpassword/prebuiltui"; import { SessionContextType } from "../../../../lib/ts/recipe/session"; const MockSession = { @@ -14,6 +15,9 @@ const MockSession = { doesSessionExist: jest.fn(), validateClaims: jest.fn(), validateGlobalClaimsAndHandleSuccessRedirection: jest.fn().mockReturnValue(new Promise(() => {})), + config: { + onHandleEvent: jest.fn(), + }, }; const setMockResolvesSession = (ctx: SessionContextType) => { @@ -48,11 +52,8 @@ describe("EmailPassword.SignInAndUp", () => { websiteBasePath: "/auth", websiteDomain, }, - recipeList: [ - Recipe.init({ - useShadowDom: false, - }), - ], + useShadowDom: false, + recipeList: [Recipe.init()], }); setMockResolvesSession({ @@ -66,19 +67,13 @@ describe("EmailPassword.SignInAndUp", () => { test("redirect if session exists", async () => { // when - render(); + render(); // then await waitFor(() => { expect(MockSession.validateGlobalClaimsAndHandleSuccessRedirection).toHaveBeenCalledTimes(1); expect(MockSession.validateGlobalClaimsAndHandleSuccessRedirection).toHaveBeenCalledWith( - { - action: "SUCCESS", - recipeId: "emailpassword", - newSessionCreated: false, - isNewRecipeUser: false, - createdNewUser: false, - }, - "emailpassword", + undefined, + "session", undefined, {}, undefined @@ -88,7 +83,12 @@ describe("EmailPassword.SignInAndUp", () => { test("not redirect if session exists but redirectOnSessionExists=false", async () => { // when - const result = render( mockRenderedText ); + const result = render( + + {" "} + mockRenderedText{" "} + + ); expect(await result.findByText(`mockRenderedText`)).toBeInTheDocument(); // then diff --git a/test/unit/recipe/passwordless/passwordless.test.ts b/test/unit/recipe/passwordless/passwordless.test.ts index 0846b69ba..1495bc829 100644 --- a/test/unit/recipe/passwordless/passwordless.test.ts +++ b/test/unit/recipe/passwordless/passwordless.test.ts @@ -89,30 +89,6 @@ describe("Passwordless", function () { assert.strictEqual(Passwordless.getInstanceOrThrow().config.signInUpFeature.resendEmailOrSMSGapInSeconds, 5); }); - it("Initializing Passwordless with TOS", async function () { - Passwordless.init({ - contactMethod: "PHONE", - signInUpFeature: { - termsOfServiceLink, - privacyPolicyLink, - }, - }).authReact(SuperTokens.getInstanceOrThrow().appInfo, false); - assert.notDeepStrictEqual(Passwordless.getInstanceOrThrow(), undefined); - assert.deepStrictEqual(Passwordless.getInstanceOrThrow().config.recipeId, "passwordless"); - assert.deepStrictEqual( - Passwordless.getInstanceOrThrow().config.appInfo, - SuperTokens.getInstanceOrThrow().appInfo - ); - assert.deepStrictEqual( - Passwordless.getInstanceOrThrow().config.signInUpFeature.termsOfServiceLink, - termsOfServiceLink - ); - assert.deepStrictEqual( - Passwordless.getInstanceOrThrow().config.signInUpFeature.privacyPolicyLink, - privacyPolicyLink - ); - }); - it("Initializing Passwordless with custom authRecipeModule custom configs.", async function () { Passwordless.init({ contactMethod: "PHONE", @@ -125,7 +101,6 @@ describe("Passwordless", function () { getRedirectionURL: () => { throw new Error("GET REDIRECTION HOOK THROWS"); }, - useShadowDom: false, }).authReact(SuperTokens.getInstanceOrThrow().appInfo, false); assert.notDeepStrictEqual(Passwordless.getInstanceOrThrow(), undefined); assert.deepStrictEqual(Passwordless.getInstanceOrThrow().config.recipeId, "passwordless"); @@ -133,7 +108,6 @@ describe("Passwordless", function () { Passwordless.getInstanceOrThrow().config.appInfo, SuperTokens.getInstanceOrThrow().appInfo ); - assert.deepStrictEqual(Passwordless.getInstanceOrThrow().config.useShadowDom, false); assert.throws(() => Passwordless.getInstanceOrThrow().config.preAPIHook({} as any), { message: "PRE API HOOK THROWS", }); diff --git a/test/unit/recipe/passwordless/signInUp.test.tsx b/test/unit/recipe/passwordless/signInUp.test.tsx index 29f0ef725..ecd08c6c2 100644 --- a/test/unit/recipe/passwordless/signInUp.test.tsx +++ b/test/unit/recipe/passwordless/signInUp.test.tsx @@ -4,8 +4,9 @@ import { render, waitFor } from "@testing-library/react"; import SuperTokens from "../../../../lib/ts/superTokens"; import Session from "../../../../lib/ts/recipe/session/recipe"; import Recipe from "../../../../lib/ts/recipe/passwordless/recipe"; -import { SignInUp } from "../../../../lib/ts/recipe/passwordless/prebuiltui"; import { SessionContextType } from "../../../../lib/ts/recipe/session"; +import { AuthPage } from "../../../../lib/ts/ui"; +import { PasswordlessPreBuiltUI } from "../../../../lib/ts/recipe/passwordless/prebuiltui"; const MockSession = { addEventListener: jest.fn(), @@ -14,6 +15,9 @@ const MockSession = { doesSessionExist: jest.fn(), validateClaims: jest.fn(), validateGlobalClaimsAndHandleSuccessRedirection: jest.fn().mockReturnValue(new Promise(() => {})), + config: { + onHandleEvent: jest.fn(), + }, }; const setMockResolvesSession = (ctx: SessionContextType) => { @@ -48,10 +52,10 @@ describe("Passwordless.SingInUp", () => { websiteBasePath: "/auth", websiteDomain, }, + useShadowDom: false, recipeList: [ Recipe.init({ contactMethod: "EMAIL_OR_PHONE", - useShadowDom: false, }), ], }); @@ -67,19 +71,13 @@ describe("Passwordless.SingInUp", () => { test("redirect if session exists", async () => { // when - render(); + render(); // then await waitFor(() => { expect(MockSession.validateGlobalClaimsAndHandleSuccessRedirection).toHaveBeenCalledTimes(1); expect(MockSession.validateGlobalClaimsAndHandleSuccessRedirection).toHaveBeenCalledWith( - { - action: "SUCCESS", - recipeId: "passwordless", - newSessionCreated: false, - isNewRecipeUser: false, - createdNewUser: false, - }, - "passwordless", + undefined, + "session", undefined, {}, undefined @@ -89,7 +87,12 @@ describe("Passwordless.SingInUp", () => { test("not redirect if session exists but redirectOnSessionExists=false", async () => { // when - const result = render( mockRenderedText ); + const result = render( + + {" "} + mockRenderedText{" "} + + ); expect(await result.findByText(`mockRenderedText`)).toBeInTheDocument(); // then diff --git a/test/unit/recipe/thirdparty/signInUp.test.tsx b/test/unit/recipe/thirdparty/signInUp.test.tsx index 71378452e..1e62819f1 100644 --- a/test/unit/recipe/thirdparty/signInUp.test.tsx +++ b/test/unit/recipe/thirdparty/signInUp.test.tsx @@ -5,7 +5,8 @@ import SuperTokens from "../../../../lib/ts/superTokens"; import Session from "../../../../lib/ts/recipe/session/recipe"; import Recipe from "../../../../lib/ts/recipe/thirdparty/recipe"; import { Github } from "../../../../lib/ts/recipe/thirdparty"; -import { SignInAndUp } from "../../../../lib/ts/recipe/thirdparty/prebuiltui"; +import { AuthPage } from "../../../../lib/ts/ui"; +import { ThirdPartyPreBuiltUI } from "../../../../lib/ts/recipe/thirdparty/prebuiltui"; import { SessionContextType } from "../../../../lib/ts/recipe/session"; const MockSession = { @@ -15,6 +16,9 @@ const MockSession = { doesSessionExist: jest.fn(), validateClaims: jest.fn(), validateGlobalClaimsAndHandleSuccessRedirection: jest.fn().mockReturnValue(new Promise(() => {})), + config: { + onHandleEvent: jest.fn(), + }, }; const setMockResolvesSession = (ctx: SessionContextType) => { @@ -50,6 +54,7 @@ describe("ThirdParty.SignInAndUp", () => { websiteBasePath: "/auth", websiteDomain, }, + useShadowDom: false, recipeList: [ Recipe.init({ signInAndUpFeature: { @@ -62,7 +67,6 @@ describe("ThirdParty.SignInAndUp", () => { }, ], }, - useShadowDom: false, }), ], }); @@ -78,19 +82,13 @@ describe("ThirdParty.SignInAndUp", () => { test("redirect if session exists", async () => { // when - render(); + render(); // then await waitFor(() => { expect(MockSession.validateGlobalClaimsAndHandleSuccessRedirection).toHaveBeenCalledTimes(1); expect(MockSession.validateGlobalClaimsAndHandleSuccessRedirection).toHaveBeenCalledWith( - { - action: "SUCCESS", - recipeId: "thirdparty", - newSessionCreated: false, - isNewRecipeUser: false, - createdNewUser: false, - }, - "thirdparty", + undefined, + "session", undefined, {}, undefined @@ -99,13 +97,18 @@ describe("ThirdParty.SignInAndUp", () => { }); test("check if the logo is rendered, when a logo is provided for custom providers", async () => { - const result = render(); + const result = render(); expect(await result.findByText("LOGO")).toBeInTheDocument(); }); test("not redirect if session exists but redirectOnSessionExists=false", async () => { // when - const result = render( mockRenderedText ); + const result = render( + + {" "} + mockRenderedText{" "} + + ); expect(await result.findByText(`mockRenderedText`)).toBeInTheDocument(); // then diff --git a/test/unit/recipe/thirdparty/thirdParty.test.ts b/test/unit/recipe/thirdparty/thirdParty.test.ts index a4efcce4e..05fc6a43b 100644 --- a/test/unit/recipe/thirdparty/thirdParty.test.ts +++ b/test/unit/recipe/thirdparty/thirdParty.test.ts @@ -99,8 +99,6 @@ describe("ThirdParty", function () { it("Initializing ThirdParty with TOS", async function () { ThirdParty.init({ signInAndUpFeature: { - termsOfServiceLink, - privacyPolicyLink, providers: [Google.init()], }, }).authReact(SuperTokens.getInstanceOrThrow().appInfo, false); @@ -110,14 +108,6 @@ describe("ThirdParty", function () { ThirdParty.getInstanceOrThrow().config.appInfo, SuperTokens.getInstanceOrThrow().appInfo ); - assert.deepStrictEqual( - ThirdParty.getInstanceOrThrow().config.signInAndUpFeature.termsOfServiceLink, - termsOfServiceLink - ); - assert.deepStrictEqual( - ThirdParty.getInstanceOrThrow().config.signInAndUpFeature.privacyPolicyLink, - privacyPolicyLink - ); }); it("Initializing ThirdParty with Google/Github provider", async function () { @@ -328,7 +318,6 @@ describe("ThirdParty", function () { getRedirectionURL: () => { throw new Error("GET REDIRECTION HOOK THROWS"); }, - useShadowDom: false, signInAndUpFeature: { providers: [Google.init(), Github.init(), Facebook.init()], }, @@ -340,7 +329,6 @@ describe("ThirdParty", function () { SuperTokens.getInstanceOrThrow().appInfo ); assert.deepStrictEqual(ThirdParty.getInstanceOrThrow().config.signInAndUpFeature.providers.length, 3); - assert.deepStrictEqual(ThirdParty.getInstanceOrThrow().config.useShadowDom, false); assert.throws(() => ThirdParty.getInstanceOrThrow().config.preAPIHook({} as any), { message: "PRE API HOOK THROWS", }); diff --git a/test/unit/ssr.test.tsx b/test/unit/ssr.test.tsx index c30a93766..2a6f15b76 100644 --- a/test/unit/ssr.test.tsx +++ b/test/unit/ssr.test.tsx @@ -18,6 +18,7 @@ describe("Auth wrappers in SSR", () => { render(); }); } + console.log("a"); for (const Auth of authComponents) { test(`${Auth.name} should work without recipe init`, async () => { // We suppress errors from react to not spam the console with uncaught error exceptions