From 89e4ac0a343e268bf73bdf1081ac6ab5937e6bca Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Fri, 24 Nov 2023 13:09:48 +0530 Subject: [PATCH] feat: Update version and include e2e tests --- CHANGELOG.md | 20 +++ examples/for-tests/src/App.js | 6 + examples/for-tests/src/testContext.js | 2 + lib/build/genericComponentOverrideContext.js | 2 +- lib/build/version.d.ts | 2 +- lib/ts/version.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- test/end-to-end/getRedirectionURL.test.js | 149 ++++++++++++++++++- test/with-typescript/src/App.tsx | 10 +- 10 files changed, 190 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c84f6e6e..ee239bc98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) +## [0.35.8] - 2023-11-24 + +### Changes + +- `getRedirectionURL` now supports returning `null` to prevent automatic redirection, useful for customizing the behavior after successful sign-in or sign-up. + +Here's an example of how to use this: + +```tsx +EmailPassword.init({ + getRedirectionURL: async (context) => { + if (context.action === "SUCCESS") { + return null; + } + // Returning undefined falls back to the default redirection strategy + return undefined; + }, +}); +``` + ## [0.35.7] - 2023-11-16 ### Added diff --git a/examples/for-tests/src/App.js b/examples/for-tests/src/App.js index add8541e8..631eda9cc 100644 --- a/examples/for-tests/src/App.js +++ b/examples/for-tests/src/App.js @@ -816,6 +816,9 @@ function getEmailPasswordConfigs({ disableDefaultUI, formFieldType }) { console.log(`ST_LOGS EMAIL_PASSWORD GET_REDIRECTION_URL ${context.action}`); if (context.action === "SUCCESS") { setIsNewUserToStorage("emailpassword", context.isNewRecipeUser); + if (testContext.disableRedirectionAfterSuccessfulSignInUp) { + return null; + } return context.redirectToPath || "/dashboard"; } }, @@ -1049,6 +1052,9 @@ function getPasswordlessConfigs({ disableDefaultUI }) { console.log(`ST_LOGS PASSWORDLESS GET_REDIRECTION_URL ${context.action}`); if (context.action === "SUCCESS") { setIsNewUserToStorage("passwordless", context.isNewRecipeUser); + if (testContext.disableRedirectionAfterSuccessfulSignInUp) { + return null; + } return context.redirectToPath || "/dashboard"; } }, diff --git a/examples/for-tests/src/testContext.js b/examples/for-tests/src/testContext.js index 3021a9b92..a11006f5e 100644 --- a/examples/for-tests/src/testContext.js +++ b/examples/for-tests/src/testContext.js @@ -18,6 +18,8 @@ export function getTestContext() { signIn: localStorage.getItem("SIGNIN_SETTING_TYPE"), signUp: localStorage.getItem("SIGNUP_SETTING_TYPE"), }, + disableRedirectionAfterSuccessfulSignInUp: + localStorage.getItem("disableRedirectionAfterSuccessfulSignInUp") === "true", }; return ret; } diff --git a/lib/build/genericComponentOverrideContext.js b/lib/build/genericComponentOverrideContext.js index 9d97a3933..d5182b2dc 100644 --- a/lib/build/genericComponentOverrideContext.js +++ b/lib/build/genericComponentOverrideContext.js @@ -241,7 +241,7 @@ typeof SuppressedError === "function" * License for the specific language governing permissions and limitations * under the License. */ -var package_version = "0.35.7"; +var package_version = "0.35.8"; /* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. * diff --git a/lib/build/version.d.ts b/lib/build/version.d.ts index 61197a780..cfe379a1e 100644 --- a/lib/build/version.d.ts +++ b/lib/build/version.d.ts @@ -1 +1 @@ -export declare const package_version = "0.35.7"; +export declare const package_version = "0.35.8"; diff --git a/lib/ts/version.ts b/lib/ts/version.ts index 8d697de72..a6d28e815 100644 --- a/lib/ts/version.ts +++ b/lib/ts/version.ts @@ -12,4 +12,4 @@ * License for the specific language governing permissions and limitations * under the License. */ -export const package_version = "0.35.7"; +export const package_version = "0.35.8"; diff --git a/package-lock.json b/package-lock.json index f2efb65d3..c16a56d1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-auth-react", - "version": "0.35.7", + "version": "0.35.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "supertokens-auth-react", - "version": "0.35.7", + "version": "0.35.8", "license": "Apache-2.0", "dependencies": { "intl-tel-input": "^17.0.19", diff --git a/package.json b/package.json index eedec85f3..cb820d2cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-auth-react", - "version": "0.35.7", + "version": "0.35.8", "description": "ReactJS SDK that provides login functionality with SuperTokens.", "main": "./index.js", "engines": { diff --git a/test/end-to-end/getRedirectionURL.test.js b/test/end-to-end/getRedirectionURL.test.js index b279fd525..10fa53e0c 100644 --- a/test/end-to-end/getRedirectionURL.test.js +++ b/test/end-to-end/getRedirectionURL.test.js @@ -200,7 +200,6 @@ describe("getRedirectionURL Tests", function () { if (!_isPasswordlessSupported) { didSkip = true; this.skip(); - return; } await backendBeforeEach(); @@ -287,7 +286,6 @@ describe("getRedirectionURL Tests", function () { if (!_isThirdPartyPasswordlessSupported) { didSkip = true; this.skip(); - return; } await backendBeforeEach(); @@ -377,5 +375,152 @@ describe("getRedirectionURL Tests", function () { assert.equal(newUserCheck, "thirdpartypasswordless-true"); }); }); + + describe("No Redirection", function () { + describe("Email Password Recipe", function () { + let browser; + let page; + + before(async function () { + await backendBeforeEach(); + + await fetch(`${TEST_SERVER_BASE_URL}/startst`, { + method: "POST", + }).catch(console.error); + + browser = await puppeteer.launch({ + args: ["--no-sandbox", "--disable-setuid-sandbox"], + headless: true, + }); + + page = await browser.newPage(); + // We need to set the localStorage value before the page loads to ensure ST initialises with the correct value + await page.evaluateOnNewDocument(() => { + localStorage.setItem("disableRedirectionAfterSuccessfulSignInUp", "true"); + localStorage.removeItem("isNewUserCheck"); + }); + + await clearBrowserCookiesWithoutAffectingConsole(page, []); + }); + + after(async function () { + await browser.close(); + await fetch(`${TEST_SERVER_BASE_URL}/after`, { + method: "POST", + }).catch(console.error); + await fetch(`${TEST_SERVER_BASE_URL}/stopst`, { + method: "POST", + }).catch(console.error); + + await screenshotOnFailure(this, browser); + }); + + it("should not do any redirection after successful sign up", async function () { + await Promise.all([ + page.goto(`${TEST_CLIENT_BASE_URL}/auth?authRecipe=emailpassword`), + page.waitForNavigation({ waitUntil: "networkidle0" }), + ]); + + await toggleSignInSignUp(page); + const urlBeforeSignUp = await page.url(); + await defaultSignUp(page); + const urlAfterSignUp = await page.url(); + + const newUserCheck = await page.evaluate(() => localStorage.getItem("isNewUserCheck")); + assert.equal(newUserCheck, "emailpassword-true"); + assert.equal(urlBeforeSignUp, urlAfterSignUp); + }); + }); + + describe("Passwordless recipe", function () { + let browser; + let page; + const exampleEmail = "test@example.com"; + // Mocha calls cleanup functions even if the test block is skipped, this helps skipping the after block + let didSkip = false; + + before(async function () { + let _isPasswordlessSupported = await isPasswordlessSupported(); + if (!_isPasswordlessSupported) { + didSkip = true; + this.skip(); + } + + await backendBeforeEach(); + + await fetch(`${TEST_SERVER_BASE_URL}/startst`, { + method: "POST", + headers: [["content-type", "application/json"]], + body: JSON.stringify({ + coreConfig: { + passwordless_code_lifetime: 4000, + passwordless_max_code_input_attempts: 3, + }, + }), + }).catch(console.error); + + browser = await puppeteer.launch({ + args: ["--no-sandbox", "--disable-setuid-sandbox"], + headless: true, + }); + page = await browser.newPage(); + // We need to set the localStorage value before the page loads to ensure ST initialises with the correct value + await page.evaluateOnNewDocument(() => { + localStorage.setItem("disableRedirectionAfterSuccessfulSignInUp", "true"); + localStorage.removeItem("isNewUserCheck"); + }); + await clearBrowserCookiesWithoutAffectingConsole(page, []); + await Promise.all([ + page.goto( + `${TEST_CLIENT_BASE_URL}/auth?authRecipe=passwordless&passwordlessContactMethodType=EMAIL` + ), + page.waitForNavigation({ waitUntil: "networkidle0" }), + ]); + await setPasswordlessFlowType("EMAIL", "USER_INPUT_CODE"); + }); + + after(async function () { + // Dont cleanup if tests were skipped + if (didSkip) { + return; + } + await browser.close(); + await fetch(`${TEST_SERVER_BASE_URL}/after`, { + method: "POST", + }).catch(console.error); + await fetch(`${TEST_SERVER_BASE_URL}/stopst`, { + method: "POST", + }).catch(console.error); + + return screenshotOnFailure(this, browser); + }); + + it("should not do any redirection after successful sign up", async function () { + await Promise.all([ + page.goto(`${TEST_CLIENT_BASE_URL}/auth`), + page.waitForNavigation({ waitUntil: "networkidle0" }), + ]); + await setInputValues(page, [{ name: "email", value: exampleEmail }]); + await submitForm(page); + await waitForSTElement(page, "[data-supertokens~=input][name=userInputCode]"); + + const urlBeforeSignUp = await page.url(); + + const loginAttemptInfo = JSON.parse( + await page.evaluate(() => localStorage.getItem("supertokens-passwordless-loginAttemptInfo")) + ); + const device = await getPasswordlessDevice(loginAttemptInfo); + await setInputValues(page, [{ name: "userInputCode", value: device.codes[0].userInputCode }]); + await submitForm(page); + // wait until network idle to ensure that the page has not been redirected + await page.waitForNetworkIdle(); + + const urlAfterSignUp = await page.url(); + const newUserCheck = await page.evaluate(() => localStorage.getItem("isNewUserCheck")); + assert.equal(newUserCheck, "passwordless-true"); + assert.equal(urlBeforeSignUp, urlAfterSignUp); + }); + }); + }); }); }); diff --git a/test/with-typescript/src/App.tsx b/test/with-typescript/src/App.tsx index 9931adcc5..4134c05c9 100644 --- a/test/with-typescript/src/App.tsx +++ b/test/with-typescript/src/App.tsx @@ -8,7 +8,7 @@ import EmailPassword, { OnHandleEventContext as EmailPasswordOnHandleEventContext, PreAPIHookContext as EmailPasswordPreAPIHookContext, } from "../../../recipe/emailpassword"; -import Session, { SessionAuth } from "../../../recipe/session"; +import Session, { BooleanClaim, SessionAuth } from "../../../recipe/session"; import Multitenancy, { AllowedDomainsClaim } from "../../../recipe/multitenancy"; import ThirdParty, { GetRedirectionURLContext as ThirdPartyGetRedirectionURLContext, @@ -1501,3 +1501,11 @@ SuperTokens.init({ }), ], }); + +export const PhoneVerifiedClaim = new BooleanClaim({ + id: "phone-verified", + refresh: async () => { + // This is something we have no way of refreshing, so this is a no-op + }, + onFailureRedirection: () => null, +});