From 2127c64782c2b3e808cccd7731b6affa9f222698 Mon Sep 17 00:00:00 2001 From: Amit Badala Date: Fri, 3 Nov 2023 17:02:16 +0530 Subject: [PATCH 01/10] feat: add input component as types, simplify event handlers - emailpassword recipe (#752) * Add inputComponent to exposed types * Add inputComponent to normalised fields * For testing only - use custom type definition for inputComponent * Input component already present in FormFieldThemeProps * Testing if git package is getting installed correctly * Run build for previous commits * Remove inputComp from NormalizedFormField * Add tests for custom fields * Remove testing ele * Move the custom fields tests into existing describe * Update dropdown values to avoid confusion * Add helper func to set dropdown, better test title, use existing describe hooks * Use strict equal * Update request * A seperate func to fetch custom comp not required * Move inputComponent to signup types * Cleanup unwanted imports * Move inputComponent to signup types * Clean types * Update build files * Use explicit values in validate func * Minor cleanup of types * Better type names * Props suggestions working for inputComponent * Enforce strict string check on form values, now onChange function for fields only needs value, no need to supply name or id * Update based on the new onChange func * Ability to add default value with getDefaultValue prop * Handle if getDefaultValue is not a function * instead of form submit apply type test within onChange function itself * Add tests for default value * Remove unwanted abort * Testing email-verification workflow * Reverting onChange changes * onChange function to accept only values * Initialize fieldstates at the start * Remove useEffect * Fix race conditions when setting default value * Add custom default fields to typescript example, plus add tests to show custom error message * Add tests for incorrect default props in formFields * Add tests for incorrect usage of onChange prop * Add change log * Wrap ternary opeators into seperate func for better readibility * Wrap inputComponent in a serperate component to avoid unecessary rerenders * Add change log feedbacks * Better variable names, include formfields directly in typescript example * Add more tests for default & onChange func, updated typescript file to show the latest changes * Add more test, which intercepts request payload * Cleanup comments * Minor formatting * Minor fix * Clean up helper * Update change log & versions --- CHANGELOG.md | 40 ++ examples/for-tests/src/App.js | 171 +++++- lib/build/emailpassword-shared7.js | 138 +++-- lib/build/passwordless-shared3.js | 20 +- .../components/library/input.d.ts | 7 +- .../resetPasswordEmail.d.ts | 2 +- .../submitNewPassword.d.ts | 2 +- .../components/themes/signInAndUp/signIn.d.ts | 2 +- .../themes/signInAndUp/signInForm.d.ts | 2 +- .../components/themes/signInAndUp/signUp.d.ts | 5 +- .../themes/signInAndUp/signUpForm.d.ts | 5 +- lib/build/recipe/emailpassword/types.d.ts | 29 +- lib/build/version.d.ts | 2 +- .../components/library/formBase.tsx | 139 +++-- .../components/library/input.tsx | 22 +- lib/ts/recipe/emailpassword/types.ts | 48 +- .../themes/signInUp/phoneNumberInput.tsx | 20 +- lib/ts/version.ts | 2 +- package-lock.json | 487 +----------------- package.json | 2 +- test/end-to-end/signup.test.js | 310 +++++++++++ test/helpers.js | 11 + test/with-typescript/src/App.tsx | 65 +++ 23 files changed, 864 insertions(+), 667 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce869efbd..d966973be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,46 @@ 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.36.0] - 2023-10-30 + +### Added + +- Introduced the capability to utilize custom components in the Email-Password based recipes' signup form fields by exposing inputComponent types. +- Implemented the functionality to assign default values to the form fields in the Email-Password based recipes. +- Simplified onChange prop usage in inputComponent - id attribute removed. + +Following is an example of how to use above features. + +```tsx +EmailPassword.init({ + signInAndUpFeature: { + signUpForm: { + formFields: [ + { + id: "select-dropdown", + label: "Select Option", + getDefaultValue: () => "option 2", + inputComponent: ({ value, name, onChange }) => ( + + ), + }, + ], + }, + }, +}); +``` + ## [0.35.6] - 2023-10-16 ### Test changes diff --git a/examples/for-tests/src/App.js b/examples/for-tests/src/App.js index f65dac002..2beab128d 100644 --- a/examples/for-tests/src/App.js +++ b/examples/for-tests/src/App.js @@ -168,6 +168,159 @@ const formFields = [ }, ]; +const formFieldsWithDefault = [ + { + id: "country", + label: "Your Country", + placeholder: "Where do you live?", + optional: true, + getDefaultValue: () => "India", + }, + { + id: "select-dropdown", + label: "Select Option", + getDefaultValue: () => "option 2", + inputComponent: ({ value, name, onChange }) => ( + + ), + optional: true, + }, + { + id: "terms", + label: "", + optional: false, + getDefaultValue: () => "true", + inputComponent: ({ name, onChange, value }) => ( +
+ onChange(e.target.checked.toString())}> + I agree to the terms and conditions +
+ ), + validate: async (value) => { + if (value === "true") { + return undefined; + } + return "Please check Terms and conditions"; + }, + }, + { + id: "email", + label: "Email", + getDefaultValue: () => "test@one.com", + }, + { + id: "password", + label: "Password", + getDefaultValue: () => "fakepassword123", + }, +]; + +const incorrectFormFields = [ + { + id: "country", + label: "Your Country", + placeholder: "Where do you live?", + optional: true, + getDefaultValue: () => 23, // return should be a string + }, + { + id: "select-dropdown", + label: "Select Dropdown", + getDefaultValue: "option 2", // should be function + inputComponent: ({ value, name, onChange }) => ( + + ), + optional: true, + }, + { + // onChange accepts only string value, here we pass boolean + id: "terms", + label: "", + optional: false, + inputComponent: ({ name, onChange }) => ( +
+ onChange(e.target.checked)}> + I agree to the terms and conditions +
+ ), + validate: async (value) => { + if (value === "true") { + return undefined; + } + return "Please check Terms and conditions"; + }, + }, +]; + +const customFields = [ + { + id: "select-dropdown", + label: "Select Dropdown", + inputComponent: ({ value, name, onChange }) => ( + + ), + optional: true, + }, + { + id: "terms", + label: "", + optional: false, + inputComponent: ({ name, onChange }) => ( +
+ onChange(e.target.checked.toString())}> + I agree to the terms and conditions +
+ ), + validate: async (value) => { + if (value === "true") { + return undefined; + } + return "Please check Terms and conditions"; + }, + }, +]; + const testContext = getTestContext(); let recipeList = [ @@ -552,6 +705,22 @@ function getEmailVerificationConfigs({ disableDefaultUI }) { }); } +function getFormFields() { + if (localStorage.getItem("SHOW_INCORRECT_FIELDS") === "YES") { + if (localStorage.getItem("INCORRECT_ONCHANGE") === "YES") { + // since page-error blocks all the other errors + // use this filter to test specific error + return incorrectFormFields.filter(({ id }) => id === "terms"); + } + return incorrectFormFields; + } else if (localStorage.getItem("SHOW_CUSTOM_FIELDS_WITH_DEFAULT_VALUES") === "YES") { + return formFieldsWithDefault; + } else if (localStorage.getItem("SHOW_CUSTOM_FIELDS") === "YES") { + return customFields; + } + return formFields; +} + function getEmailPasswordConfigs({ disableDefaultUI }) { return EmailPassword.init({ style: ` @@ -637,7 +806,7 @@ function getEmailPasswordConfigs({ disableDefaultUI }) { style: theme, privacyPolicyLink: "https://supertokens.com/legal/privacy-policy", termsOfServiceLink: "https://supertokens.com/legal/terms-and-conditions", - formFields, + formFields: getFormFields(), }, }, }); diff --git a/lib/build/emailpassword-shared7.js b/lib/build/emailpassword-shared7.js index 6415dc169..0abdcd070 100644 --- a/lib/build/emailpassword-shared7.js +++ b/lib/build/emailpassword-shared7.js @@ -320,26 +320,17 @@ var Input = function (_a) { */ function handleFocus() { if (onInputFocus !== undefined) { - onInputFocus({ - id: name, - value: value, - }); + onInputFocus(value); } } function handleBlur() { if (onInputBlur !== undefined) { - onInputBlur({ - id: name, - value: value, - }); + onInputBlur(value); } } function handleChange(event) { if (onChange) { - onChange({ - id: name, - value: event.target.value, - }); + onChange(event.target.value); } } if (autoComplete === undefined) { @@ -439,6 +430,79 @@ function Label(_a) { ); } +var fetchDefaultValue = function (field) { + if (field.getDefaultValue !== undefined) { + var defaultValue = field.getDefaultValue(); + if (typeof defaultValue !== "string") { + throw new Error("getDefaultValue for ".concat(field.id, " must return a string")); + } else { + return defaultValue; + } + } + return ""; +}; +function InputComponentWrapper(props) { + var field = props.field, + type = props.type, + fstate = props.fstate, + onInputFocus = props.onInputFocus, + onInputBlur = props.onInputBlur, + onInputChange = props.onInputChange; + var useCallbackOnInputFocus = React.useCallback( + function (value) { + onInputFocus({ + id: field.id, + value: value, + }); + }, + [onInputFocus, field] + ); + var useCallbackOnInputBlur = React.useCallback( + function (value) { + onInputBlur({ + id: field.id, + value: value, + }); + }, + [onInputBlur, field] + ); + var useCallbackOnInputChange = React.useCallback( + function (value) { + onInputChange({ + id: field.id, + value: value, + }); + }, + [onInputChange, field] + ); + return field.inputComponent !== undefined + ? jsxRuntime.jsx(field.inputComponent, { + type: type, + name: field.id, + validated: fstate.validated === true, + placeholder: field.placeholder, + value: fstate.value, + autoComplete: field.autoComplete, + autofocus: field.autofocus, + onInputFocus: useCallbackOnInputFocus, + onInputBlur: useCallbackOnInputBlur, + onChange: useCallbackOnInputChange, + hasError: fstate.error !== undefined, + }) + : jsxRuntime.jsx(Input, { + type: type, + name: field.id, + validated: fstate.validated === true, + placeholder: field.placeholder, + value: fstate.value, + autoComplete: field.autoComplete, + onInputFocus: useCallbackOnInputFocus, + onInputBlur: useCallbackOnInputBlur, + onChange: useCallbackOnInputChange, + autofocus: field.autofocus, + hasError: fstate.error !== undefined, + }); +} var FormBase = function (props) { var footer = props.footer, buttonLabel = props.buttonLabel, @@ -458,7 +522,7 @@ var FormBase = function (props) { ); var _a = React.useState( props.formFields.map(function (f) { - return { id: f.id, value: "" }; + return { id: f.id, value: fetchDefaultValue(f) }; }) ), fieldStates = _a[0], @@ -536,6 +600,9 @@ var FormBase = function (props) { ); var onInputChange = React.useCallback( function (field) { + if (typeof field.value !== "string") { + throw new Error("".concat(field.id, " value must be a string")); + } updateFieldState(field.id, function (os) { return genericComponentOverrideContext.__assign(genericComponentOverrideContext.__assign({}, os), { value: field.value, @@ -690,12 +757,10 @@ var FormBase = function (props) { } var fstate = fieldStates.find(function (s) { return s.id === field.id; - }) || { - id: field.id, - validated: false, - error: undefined, - value: "", - }; + }); + if (fstate === undefined) { + throw new Error("Should never come here"); + } return jsxRuntime.jsx( FormRow, genericComponentOverrideContext.__assign( @@ -710,33 +775,14 @@ var FormBase = function (props) { value: field.label, showIsRequired: field.showIsRequired, })), - field.inputComponent !== undefined - ? jsxRuntime.jsx(field.inputComponent, { - type: type, - name: field.id, - validated: fstate.validated === true, - placeholder: field.placeholder, - value: fstate.value, - autoComplete: field.autoComplete, - autofocus: field.autofocus, - onInputFocus: onInputFocus, - onInputBlur: onInputBlur, - onChange: onInputChange, - hasError: fstate.error !== undefined, - }) - : jsxRuntime.jsx(Input, { - type: type, - name: field.id, - validated: fstate.validated === true, - placeholder: field.placeholder, - value: fstate.value, - autoComplete: field.autoComplete, - onInputFocus: onInputFocus, - onInputBlur: onInputBlur, - onChange: onInputChange, - autofocus: field.autofocus, - hasError: fstate.error !== undefined, - }), + jsxRuntime.jsx(InputComponentWrapper, { + type: type, + field: field, + fstate: fstate, + onInputFocus: onInputFocus, + onInputBlur: onInputBlur, + onInputChange: onInputChange, + }), fstate.error && jsxRuntime.jsx(InputError, { error: fstate.error }), ], }), diff --git a/lib/build/passwordless-shared3.js b/lib/build/passwordless-shared3.js index 54b23e5a7..73e421acf 100644 --- a/lib/build/passwordless-shared3.js +++ b/lib/build/passwordless-shared3.js @@ -2569,18 +2569,12 @@ function PhoneNumberInput(_a) { value = _a.value; function handleFocus() { if (onInputFocus !== undefined) { - onInputFocus({ - id: name, - value: value, - }); + onInputFocus(value); } } function handleBlur() { if (onInputBlur !== undefined) { - onInputBlur({ - id: name, - value: value, - }); + onInputBlur(value); } } var _b = React.useState(), @@ -2596,10 +2590,7 @@ function PhoneNumberInput(_a) { var handleChange = React.useCallback( function (newValue) { if (onChangeRef.current !== undefined) { - onChangeRef.current({ - id: name, - value: newValue, - }); + onChangeRef.current(newValue); } }, [onChangeRef] @@ -2607,10 +2598,7 @@ function PhoneNumberInput(_a) { var handleCountryChange = React.useCallback( function (ev) { if (onChangeRef.current !== undefined && phoneInputInstance !== undefined) { - onChangeRef.current({ - id: name, - value: ev.target.value, - }); + onChangeRef.current(ev.target.value); } }, [onChangeRef] diff --git a/lib/build/recipe/emailpassword/components/library/input.d.ts b/lib/build/recipe/emailpassword/components/library/input.d.ts index 44893e14a..973042e7c 100644 --- a/lib/build/recipe/emailpassword/components/library/input.d.ts +++ b/lib/build/recipe/emailpassword/components/library/input.d.ts @@ -1,5 +1,4 @@ /// -import type { APIFormField } from "../../../../types"; export declare type InputProps = { type: string; name: string; @@ -9,9 +8,9 @@ export declare type InputProps = { hasError: boolean; placeholder: string; value: string; - onInputBlur?: (field: APIFormField) => void; - onInputFocus?: (field: APIFormField) => void; - onChange?: (field: APIFormField) => void; + onInputBlur?: (value: string) => void; + onInputFocus?: (value: string) => void; + onChange?: (value: string) => void; }; declare const Input: React.FC; export default Input; diff --git a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts index 8ff8a9b05..bcc6e2b05 100644 --- a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/resetPasswordEmail.d.ts @@ -1,7 +1,7 @@ /// export declare const ResetPasswordEmail: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; + formFields: Omit[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; diff --git a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts index b8ec36fd7..9c795dce2 100644 --- a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts @@ -1,7 +1,7 @@ /// export declare const SubmitNewPassword: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; + formFields: Omit[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts index 2b40c960b..64639bb96 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts @@ -1,7 +1,7 @@ /// export declare const SignIn: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; + formFields: Omit[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts index 7dbc1321c..f57175ca5 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signInForm.d.ts @@ -1,7 +1,7 @@ /// export declare const SignInForm: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; + formFields: Omit[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts index 380a6e333..32bc386b1 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts @@ -1,14 +1,13 @@ /// export declare const SignUp: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; - error: string | undefined; - } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; clearError: () => void; onError: (error: string) => void; config: import("../../../types").NormalisedConfig; signInClicked?: (() => void) | undefined; onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; + formFields: import("../../../types").FormFieldThemeProps[]; + error: string | undefined; } >; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts index bbe4a2eb2..85b7e4c8d 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUpForm.d.ts @@ -1,15 +1,14 @@ /// export declare const SignUpForm: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; - error: string | undefined; - } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; clearError: () => void; onError: (error: string) => void; config: import("../../../types").NormalisedConfig; signInClicked?: (() => void) | undefined; onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; + formFields: import("../../../types").FormFieldThemeProps[]; + error: string | undefined; } & { header?: JSX.Element | undefined; footer?: JSX.Element | undefined; diff --git a/lib/build/recipe/emailpassword/types.d.ts b/lib/build/recipe/emailpassword/types.d.ts index d819f58c8..70c7ffcfd 100644 --- a/lib/build/recipe/emailpassword/types.d.ts +++ b/lib/build/recipe/emailpassword/types.d.ts @@ -26,7 +26,6 @@ import type { NormalisedConfig as NormalisedAuthRecipeModuleConfig, UserInput as AuthRecipeModuleUserInput, } from "../authRecipe/types"; -import type React from "react"; import type { Dispatch } from "react"; import type { OverrideableBuilder } from "supertokens-js-override"; import type { RecipeInterface } from "supertokens-web-js/recipe/emailpassword"; @@ -78,12 +77,18 @@ export declare type NormalisedSignInAndUpFeatureConfig = { signInForm: NormalisedSignInFormFeatureConfig; }; export declare type SignUpFormFeatureUserInput = FeatureBaseConfig & { - formFields?: FormFieldSignUpConfig[]; + formFields?: (FormField & { + inputComponent?: (props: InputProps) => JSX.Element; + getDefaultValue?: () => string; + })[]; privacyPolicyLink?: string; termsOfServiceLink?: string; }; export declare type NormalisedSignUpFormFeatureConfig = NormalisedBaseConfig & { - formFields: NormalisedFormField[]; + formFields: (NormalisedFormField & { + inputComponent?: (props: InputProps) => JSX.Element; + getDefaultValue?: () => string; + })[]; privacyPolicyLink?: string; termsOfServiceLink?: string; }; @@ -94,7 +99,6 @@ export declare type NormalisedSignInFormFeatureConfig = NormalisedBaseConfig & { formFields: NormalisedFormField[]; }; export declare type FormFieldSignInConfig = FormFieldBaseConfig; -export declare type FormFieldSignUpConfig = FormField; export declare type ResetPasswordUsingTokenUserInput = { disableDefaultUI?: boolean; submitNewPasswordForm?: FeatureBaseConfig; @@ -111,11 +115,11 @@ export declare type NormalisedSubmitNewPasswordForm = FeatureBaseConfig & { export declare type NormalisedEnterEmailForm = FeatureBaseConfig & { formFields: NormalisedFormField[]; }; -declare type FormThemeBaseProps = ThemeBaseProps & { - formFields: FormFieldThemeProps[]; +declare type NonSignUpFormThemeBaseProps = ThemeBaseProps & { + formFields: Omit[]; error: string | undefined; }; -export declare type SignInThemeProps = FormThemeBaseProps & { +export declare type SignInThemeProps = NonSignUpFormThemeBaseProps & { recipeImplementation: RecipeInterface; clearError: () => void; onError: (error: string) => void; @@ -124,13 +128,15 @@ export declare type SignInThemeProps = FormThemeBaseProps & { forgotPasswordClick: () => void; onSuccess: (result: { user: User }) => void; }; -export declare type SignUpThemeProps = FormThemeBaseProps & { +export declare type SignUpThemeProps = ThemeBaseProps & { recipeImplementation: RecipeInterface; clearError: () => void; onError: (error: string) => void; config: NormalisedConfig; signInClicked?: () => void; onSuccess: (result: { user: User }) => void; + formFields: FormFieldThemeProps[]; + error: string | undefined; }; export declare type SignInAndUpThemeProps = { signInForm: SignInThemeProps; @@ -144,9 +150,10 @@ export declare type SignInAndUpThemeProps = { }; export declare type FormFieldThemeProps = NormalisedFormField & { labelComponent?: JSX.Element; - inputComponent?: React.FC; showIsRequired?: boolean; clearOnSubmit?: boolean; + inputComponent?: (props: InputProps) => JSX.Element; + getDefaultValue?: () => string; }; export declare type FormFieldError = { id: string; @@ -192,7 +199,7 @@ export declare type ResetPasswordUsingTokenThemeProps = { config: NormalisedConfig; userContext?: any; }; -export declare type EnterEmailProps = FormThemeBaseProps & { +export declare type EnterEmailProps = NonSignUpFormThemeBaseProps & { recipeImplementation: RecipeInterface; error: string | undefined; clearError: () => void; @@ -200,7 +207,7 @@ export declare type EnterEmailProps = FormThemeBaseProps & { config: NormalisedConfig; onBackButtonClicked: () => void; }; -export declare type SubmitNewPasswordProps = FormThemeBaseProps & { +export declare type SubmitNewPasswordProps = NonSignUpFormThemeBaseProps & { recipeImplementation: RecipeInterface; error: string | undefined; clearError: () => void; diff --git a/lib/build/version.d.ts b/lib/build/version.d.ts index 51706b18d..e85100a93 100644 --- a/lib/build/version.d.ts +++ b/lib/build/version.d.ts @@ -1 +1 @@ -export declare const package_version = "0.35.6"; +export declare const package_version = "0.36.0"; diff --git a/lib/ts/recipe/emailpassword/components/library/formBase.tsx b/lib/ts/recipe/emailpassword/components/library/formBase.tsx index 1dabbd92a..f9d5e201b 100644 --- a/lib/ts/recipe/emailpassword/components/library/formBase.tsx +++ b/lib/ts/recipe/emailpassword/components/library/formBase.tsx @@ -25,7 +25,7 @@ import STGeneralError from "supertokens-web-js/utils/error"; import { MANDATORY_FORM_FIELDS_ID_ARRAY } from "../../constants"; import type { APIFormField } from "../../../../types"; -import type { FormBaseProps } from "../../types"; +import type { FormBaseProps, FormFieldThemeProps } from "../../types"; import type { FormEvent } from "react"; import { Button, FormRow, Input, InputError, Label } from "."; @@ -37,6 +37,89 @@ type FieldState = { value: string; }; +const fetchDefaultValue = (field: FormFieldThemeProps): string => { + if (field.getDefaultValue !== undefined) { + const defaultValue = field.getDefaultValue(); + if (typeof defaultValue !== "string") { + throw new Error(`getDefaultValue for ${field.id} must return a string`); + } else { + return defaultValue; + } + } + return ""; +}; + +function InputComponentWrapper(props: { + field: FormFieldThemeProps; + type: string; + fstate: FieldState; + onInputFocus: (field: APIFormField) => void; + onInputBlur: (field: APIFormField) => void; + onInputChange: (field: APIFormField) => void; +}) { + const { field, type, fstate, onInputFocus, onInputBlur, onInputChange } = props; + + const useCallbackOnInputFocus = useCallback<(value: string) => void>( + (value) => { + onInputFocus({ + id: field.id, + value, + }); + }, + [onInputFocus, field] + ); + + const useCallbackOnInputBlur = useCallback<(value: string) => void>( + (value) => { + onInputBlur({ + id: field.id, + value, + }); + }, + [onInputBlur, field] + ); + + const useCallbackOnInputChange = useCallback( + (value) => { + onInputChange({ + id: field.id, + value, + }); + }, + [onInputChange, field] + ); + + return field.inputComponent !== undefined ? ( + + ) : ( + + ); +} + export const FormBase: React.FC> = (props) => { const { footer, buttonLabel, showLabels, validateOnBlur, formFields } = props; @@ -50,7 +133,7 @@ export const FormBase: React.FC> = (props) => { }, [unmounting]); const [fieldStates, setFieldStates] = useState( - props.formFields.map((f) => ({ id: f.id, value: "" })) + props.formFields.map((f) => ({ id: f.id, value: fetchDefaultValue(f) })) ); const [isLoading, setIsLoading] = useState(false); @@ -95,6 +178,9 @@ export const FormBase: React.FC> = (props) => { const onInputChange = useCallback( (field: APIFormField) => { + if (typeof field.value !== "string") { + throw new Error(`${field.id} value must be a string`); + } updateFieldState(field.id, (os) => ({ ...os, value: field.value, error: undefined })); props.clearError(); }, @@ -191,12 +277,11 @@ export const FormBase: React.FC> = (props) => { if (field.id === "confirm-password") { type = "password"; } - const fstate: FieldState = fieldStates.find((s) => s.id === field.id) || { - id: field.id, - validated: false, - error: undefined, - value: "", - }; + + const fstate: FieldState | undefined = fieldStates.find((s) => s.id === field.id); + if (fstate === undefined) { + throw new Error("Should never come here"); + } return ( @@ -208,36 +293,14 @@ export const FormBase: React.FC> = (props) => { diff --git a/lib/ts/recipe/emailpassword/components/library/input.tsx b/lib/ts/recipe/emailpassword/components/library/input.tsx index 70f34f331..0f1877e1b 100644 --- a/lib/ts/recipe/emailpassword/components/library/input.tsx +++ b/lib/ts/recipe/emailpassword/components/library/input.tsx @@ -20,7 +20,6 @@ import CheckedIcon from "../../../../components/assets/checkedIcon"; import ErrorIcon from "../../../../components/assets/errorIcon"; import ShowPasswordIcon from "../../../../components/assets/showPasswordIcon"; -import type { APIFormField } from "../../../../types"; import type { ChangeEvent } from "react"; export type InputProps = { @@ -32,9 +31,9 @@ export type InputProps = { hasError: boolean; placeholder: string; value: string; - onInputBlur?: (field: APIFormField) => void; - onInputFocus?: (field: APIFormField) => void; - onChange?: (field: APIFormField) => void; + onInputBlur?: (value: string) => void; + onInputFocus?: (value: string) => void; + onChange?: (value: string) => void; }; const Input: React.FC = ({ @@ -59,28 +58,19 @@ const Input: React.FC = ({ function handleFocus() { if (onInputFocus !== undefined) { - onInputFocus({ - id: name, - value: value, - }); + onInputFocus(value); } } function handleBlur() { if (onInputBlur !== undefined) { - onInputBlur({ - id: name, - value, - }); + onInputBlur(value); } } function handleChange(event: ChangeEvent) { if (onChange) { - onChange({ - id: name, - value: event.target.value, - }); + onChange(event.target.value); } } diff --git a/lib/ts/recipe/emailpassword/types.ts b/lib/ts/recipe/emailpassword/types.ts index 253329077..8618f6d1e 100644 --- a/lib/ts/recipe/emailpassword/types.ts +++ b/lib/ts/recipe/emailpassword/types.ts @@ -41,7 +41,6 @@ import type { NormalisedConfig as NormalisedAuthRecipeModuleConfig, UserInput as AuthRecipeModuleUserInput, } from "../authRecipe/types"; -import type React from "react"; import type { Dispatch } from "react"; import type { OverrideableBuilder } from "supertokens-js-override"; import type { RecipeInterface } from "supertokens-web-js/recipe/emailpassword"; @@ -135,7 +134,10 @@ export type SignUpFormFeatureUserInput = FeatureBaseConfig & { /* * Form fields for SignUp. */ - formFields?: FormFieldSignUpConfig[]; + formFields?: (FormField & { + inputComponent?: (props: InputProps) => JSX.Element; + getDefaultValue?: () => string; + })[]; /* * Privacy policy link for sign up form. @@ -152,7 +154,10 @@ export type NormalisedSignUpFormFeatureConfig = NormalisedBaseConfig & { /* * Normalised form fields for SignUp. */ - formFields: NormalisedFormField[]; + formFields: (NormalisedFormField & { + inputComponent?: (props: InputProps) => JSX.Element; + getDefaultValue?: () => string; + })[]; /* * Privacy policy link for sign up form. @@ -181,8 +186,6 @@ export type NormalisedSignInFormFeatureConfig = NormalisedBaseConfig & { export type FormFieldSignInConfig = FormFieldBaseConfig; -export type FormFieldSignUpConfig = FormField; - export type ResetPasswordUsingTokenUserInput = { /* * Disable default implementation with default routes. @@ -225,20 +228,16 @@ export type NormalisedEnterEmailForm = FeatureBaseConfig & { formFields: NormalisedFormField[]; }; -/* - * Props Types. - */ - -type FormThemeBaseProps = ThemeBaseProps & { +type NonSignUpFormThemeBaseProps = ThemeBaseProps & { /* - * Form fields to use in the signin form. + * Omit since, custom inputComponent only part of signup */ - formFields: FormFieldThemeProps[]; + formFields: Omit[]; error: string | undefined; }; -export type SignInThemeProps = FormThemeBaseProps & { +export type SignInThemeProps = NonSignUpFormThemeBaseProps & { recipeImplementation: RecipeInterface; clearError: () => void; onError: (error: string) => void; @@ -248,13 +247,15 @@ export type SignInThemeProps = FormThemeBaseProps & { onSuccess: (result: { user: User }) => void; }; -export type SignUpThemeProps = FormThemeBaseProps & { +export type SignUpThemeProps = ThemeBaseProps & { recipeImplementation: RecipeInterface; clearError: () => void; onError: (error: string) => void; config: NormalisedConfig; signInClicked?: () => void; onSuccess: (result: { user: User }) => void; + formFields: FormFieldThemeProps[]; + error: string | undefined; }; export type SignInAndUpThemeProps = { @@ -274,11 +275,6 @@ export type FormFieldThemeProps = NormalisedFormField & { */ labelComponent?: JSX.Element; - /* - * Custom component that replaces the standard input component - */ - inputComponent?: React.FC; - /* * Show Is required (*) next to label */ @@ -288,6 +284,16 @@ export type FormFieldThemeProps = NormalisedFormField & { * Clears the field after calling the API. */ clearOnSubmit?: boolean; + + /* + * Ability to add custom components + */ + inputComponent?: (props: InputProps) => JSX.Element; + + /* + * Ability to add custom components + */ + getDefaultValue?: () => string; }; export type FormFieldError = { @@ -367,7 +373,7 @@ export type ResetPasswordUsingTokenThemeProps = { userContext?: any; }; -export type EnterEmailProps = FormThemeBaseProps & { +export type EnterEmailProps = NonSignUpFormThemeBaseProps & { recipeImplementation: RecipeInterface; error: string | undefined; clearError: () => void; @@ -376,7 +382,7 @@ export type EnterEmailProps = FormThemeBaseProps & { onBackButtonClicked: () => void; }; -export type SubmitNewPasswordProps = FormThemeBaseProps & { +export type SubmitNewPasswordProps = NonSignUpFormThemeBaseProps & { recipeImplementation: RecipeInterface; error: string | undefined; clearError: () => void; diff --git a/lib/ts/recipe/passwordless/components/themes/signInUp/phoneNumberInput.tsx b/lib/ts/recipe/passwordless/components/themes/signInUp/phoneNumberInput.tsx index 584c230dd..2bff1c1d0 100644 --- a/lib/ts/recipe/passwordless/components/themes/signInUp/phoneNumberInput.tsx +++ b/lib/ts/recipe/passwordless/components/themes/signInUp/phoneNumberInput.tsx @@ -45,19 +45,13 @@ function PhoneNumberInput({ }: InputProps & PhoneNumberInputProps): JSX.Element { function handleFocus() { if (onInputFocus !== undefined) { - onInputFocus({ - id: name, - value: value, - }); + onInputFocus(value); } } function handleBlur() { if (onInputBlur !== undefined) { - onInputBlur({ - id: name, - value: value, - }); + onInputBlur(value); } } @@ -71,10 +65,7 @@ function PhoneNumberInput({ const handleChange = useCallback( (newValue: string) => { if (onChangeRef.current !== undefined) { - onChangeRef.current({ - id: name, - value: newValue, - }); + onChangeRef.current(newValue); } }, [onChangeRef] @@ -83,10 +74,7 @@ function PhoneNumberInput({ const handleCountryChange = useCallback( (ev) => { if (onChangeRef.current !== undefined && phoneInputInstance !== undefined) { - onChangeRef.current({ - id: name, - value: ev.target.value, - }); + onChangeRef.current(ev.target.value); } }, [onChangeRef] diff --git a/lib/ts/version.ts b/lib/ts/version.ts index ac4a22bc3..1fd0f9cc7 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.6"; +export const package_version = "0.36.0"; diff --git a/package-lock.json b/package-lock.json index edc7549ce..d49cefd40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-auth-react", - "version": "0.35.6", + "version": "0.36.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "supertokens-auth-react", - "version": "0.35.6", + "version": "0.36.0", "license": "Apache-2.0", "dependencies": { "intl-tel-input": "^17.0.19", @@ -2259,70 +2259,6 @@ "postcss-selector-parser": "^6.0.10" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.14.tgz", - "integrity": "sha512-blODaaL+lngG5bdK/t4qZcQvq2BBqrABmYwqPPcS5VRxrCSGHb9R/rA3fqxh7R18I7WU4KKv+NYkt22FDfalcg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.14.tgz", - "integrity": "sha512-rZ2v+Luba5/3D6l8kofWgTnqE+qsC/L5MleKIKFyllHTKHrNBMqeRCnZI1BtRx8B24xMYxeU32iIddRQqMsOsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.14.tgz", - "integrity": "sha512-qSwh8y38QKl+1Iqg+YhvCVYlSk3dVLk9N88VO71U4FUjtiSFylMWK3Ugr8GC6eTkkP4Tc83dVppt2n8vIdlSGg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.14.tgz", - "integrity": "sha512-9Hl2D2PBeDYZiNbnRKRWuxwHa9v5ssWBBjisXFkVcSP5cZqzZRFBUWEQuqBHO4+PKx4q4wgHoWtfQ1S7rUqJ2Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/darwin-x64": { "version": "0.18.14", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.14.tgz", @@ -2339,278 +2275,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.14.tgz", - "integrity": "sha512-h3OqR80Da4oQCIa37zl8tU5MwHQ7qgPV0oVScPfKJK21fSRZEhLE4IIVpmcOxfAVmqjU6NDxcxhYaM8aDIGRLw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.14.tgz", - "integrity": "sha512-ha4BX+S6CZG4BoH9tOZTrFIYC1DH13UTCRHzFc3GWX74nz3h/N6MPF3tuR3XlsNjMFUazGgm35MPW5tHkn2lzQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.14.tgz", - "integrity": "sha512-5+7vehI1iqru5WRtJyU2XvTOvTGURw3OZxe3YTdE9muNNIdmKAVmSHpB3Vw2LazJk2ifEdIMt/wTWnVe5V98Kg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.14.tgz", - "integrity": "sha512-IXORRe22In7U65NZCzjwAUc03nn8SDIzWCnfzJ6t/8AvGx5zBkcLfknI+0P+hhuftufJBmIXxdSTbzWc8X/V4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.14.tgz", - "integrity": "sha512-BfHlMa0nibwpjG+VXbOoqJDmFde4UK2gnW351SQ2Zd4t1N3zNdmUEqRkw/srC1Sa1DRBE88Dbwg4JgWCbNz/FQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.14.tgz", - "integrity": "sha512-j2/Ex++DRUWIAaUDprXd3JevzGtZ4/d7VKz+AYDoHZ3HjJzCyYBub9CU1wwIXN+viOP0b4VR3RhGClsvyt/xSw==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.14.tgz", - "integrity": "sha512-qn2+nc+ZCrJmiicoAnJXJJkZWt8Nwswgu1crY7N+PBR8ChBHh89XRxj38UU6Dkthl2yCVO9jWuafZ24muzDC/A==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.14.tgz", - "integrity": "sha512-aGzXzd+djqeEC5IRkDKt3kWzvXoXC6K6GyYKxd+wsFJ2VQYnOWE954qV2tvy5/aaNrmgPTb52cSCHFE+Z7Z0yg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.14.tgz", - "integrity": "sha512-8C6vWbfr0ygbAiMFLS6OPz0BHvApkT2gCboOGV76YrYw+sD/MQJzyITNsjZWDXJwPu9tjrFQOVG7zijRzBCnLw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.14.tgz", - "integrity": "sha512-G/Lf9iu8sRMM60OVGOh94ZW2nIStksEcITkXdkD09/T6QFD/o+g0+9WVyR/jajIb3A0LvBJ670tBnGe1GgXMgw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.14.tgz", - "integrity": "sha512-TBgStYBQaa3EGhgqIDM+ECnkreb0wkcKqL7H6m+XPcGUoU4dO7dqewfbm0mWEQYH3kzFHrzjOFNpSAVzDZRSJw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.14.tgz", - "integrity": "sha512-stvCcjyCQR2lMTroqNhAbvROqRjxPEq0oQ380YdXxA81TaRJEucH/PzJ/qsEtsHgXlWFW6Ryr/X15vxQiyRXVg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.14.tgz", - "integrity": "sha512-apAOJF14CIsN5ht1PA57PboEMsNV70j3FUdxLmA2liZ20gEQnfTG5QU0FhENo5nwbTqCB2O3WDsXAihfODjHYw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.14.tgz", - "integrity": "sha512-fYRaaS8mDgZcGybPn2MQbn1ZNZx+UXFSUoS5Hd2oEnlsyUcr/l3c6RnXf1bLDRKKdLRSabTmyCy7VLQ7VhGdOQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.14.tgz", - "integrity": "sha512-1c44RcxKEJPrVj62XdmYhxXaU/V7auELCmnD+Ri+UCt+AGxTvzxl9uauQhrFso8gj6ZV1DaORV0sT9XSHOAk8Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.14.tgz", - "integrity": "sha512-EXAFttrdAxZkFQmpvcAQ2bywlWUsONp/9c2lcfvPUhu8vXBBenCXpoq9YkUvVP639ld3YGiYx0YUQ6/VQz3Maw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.14.tgz", - "integrity": "sha512-K0QjGbcskx+gY+qp3v4/940qg8JitpXbdxFhRDA1aYoNaPff88+aEwoq45aqJ+ogpxQxmU0ZTjgnrQD/w8iiUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -19242,34 +18906,6 @@ "dev": true, "requires": {} }, - "@esbuild/android-arm": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.14.tgz", - "integrity": "sha512-blODaaL+lngG5bdK/t4qZcQvq2BBqrABmYwqPPcS5VRxrCSGHb9R/rA3fqxh7R18I7WU4KKv+NYkt22FDfalcg==", - "dev": true, - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.14.tgz", - "integrity": "sha512-rZ2v+Luba5/3D6l8kofWgTnqE+qsC/L5MleKIKFyllHTKHrNBMqeRCnZI1BtRx8B24xMYxeU32iIddRQqMsOsg==", - "dev": true, - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.14.tgz", - "integrity": "sha512-qSwh8y38QKl+1Iqg+YhvCVYlSk3dVLk9N88VO71U4FUjtiSFylMWK3Ugr8GC6eTkkP4Tc83dVppt2n8vIdlSGg==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.14.tgz", - "integrity": "sha512-9Hl2D2PBeDYZiNbnRKRWuxwHa9v5ssWBBjisXFkVcSP5cZqzZRFBUWEQuqBHO4+PKx4q4wgHoWtfQ1S7rUqJ2Q==", - "dev": true, - "optional": true - }, "@esbuild/darwin-x64": { "version": "0.18.14", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.14.tgz", @@ -19277,125 +18913,6 @@ "dev": true, "optional": true }, - "@esbuild/freebsd-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.14.tgz", - "integrity": "sha512-h3OqR80Da4oQCIa37zl8tU5MwHQ7qgPV0oVScPfKJK21fSRZEhLE4IIVpmcOxfAVmqjU6NDxcxhYaM8aDIGRLw==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.14.tgz", - "integrity": "sha512-ha4BX+S6CZG4BoH9tOZTrFIYC1DH13UTCRHzFc3GWX74nz3h/N6MPF3tuR3XlsNjMFUazGgm35MPW5tHkn2lzQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.14.tgz", - "integrity": "sha512-5+7vehI1iqru5WRtJyU2XvTOvTGURw3OZxe3YTdE9muNNIdmKAVmSHpB3Vw2LazJk2ifEdIMt/wTWnVe5V98Kg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.14.tgz", - "integrity": "sha512-IXORRe22In7U65NZCzjwAUc03nn8SDIzWCnfzJ6t/8AvGx5zBkcLfknI+0P+hhuftufJBmIXxdSTbzWc8X/V4w==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.14.tgz", - "integrity": "sha512-BfHlMa0nibwpjG+VXbOoqJDmFde4UK2gnW351SQ2Zd4t1N3zNdmUEqRkw/srC1Sa1DRBE88Dbwg4JgWCbNz/FQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.14.tgz", - "integrity": "sha512-j2/Ex++DRUWIAaUDprXd3JevzGtZ4/d7VKz+AYDoHZ3HjJzCyYBub9CU1wwIXN+viOP0b4VR3RhGClsvyt/xSw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.14.tgz", - "integrity": "sha512-qn2+nc+ZCrJmiicoAnJXJJkZWt8Nwswgu1crY7N+PBR8ChBHh89XRxj38UU6Dkthl2yCVO9jWuafZ24muzDC/A==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.14.tgz", - "integrity": "sha512-aGzXzd+djqeEC5IRkDKt3kWzvXoXC6K6GyYKxd+wsFJ2VQYnOWE954qV2tvy5/aaNrmgPTb52cSCHFE+Z7Z0yg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.14.tgz", - "integrity": "sha512-8C6vWbfr0ygbAiMFLS6OPz0BHvApkT2gCboOGV76YrYw+sD/MQJzyITNsjZWDXJwPu9tjrFQOVG7zijRzBCnLw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.14.tgz", - "integrity": "sha512-G/Lf9iu8sRMM60OVGOh94ZW2nIStksEcITkXdkD09/T6QFD/o+g0+9WVyR/jajIb3A0LvBJ670tBnGe1GgXMgw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.14.tgz", - "integrity": "sha512-TBgStYBQaa3EGhgqIDM+ECnkreb0wkcKqL7H6m+XPcGUoU4dO7dqewfbm0mWEQYH3kzFHrzjOFNpSAVzDZRSJw==", - "dev": true, - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.14.tgz", - "integrity": "sha512-stvCcjyCQR2lMTroqNhAbvROqRjxPEq0oQ380YdXxA81TaRJEucH/PzJ/qsEtsHgXlWFW6Ryr/X15vxQiyRXVg==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.14.tgz", - "integrity": "sha512-apAOJF14CIsN5ht1PA57PboEMsNV70j3FUdxLmA2liZ20gEQnfTG5QU0FhENo5nwbTqCB2O3WDsXAihfODjHYw==", - "dev": true, - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.14.tgz", - "integrity": "sha512-fYRaaS8mDgZcGybPn2MQbn1ZNZx+UXFSUoS5Hd2oEnlsyUcr/l3c6RnXf1bLDRKKdLRSabTmyCy7VLQ7VhGdOQ==", - "dev": true, - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.14.tgz", - "integrity": "sha512-1c44RcxKEJPrVj62XdmYhxXaU/V7auELCmnD+Ri+UCt+AGxTvzxl9uauQhrFso8gj6ZV1DaORV0sT9XSHOAk8Q==", - "dev": true, - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.14.tgz", - "integrity": "sha512-EXAFttrdAxZkFQmpvcAQ2bywlWUsONp/9c2lcfvPUhu8vXBBenCXpoq9YkUvVP639ld3YGiYx0YUQ6/VQz3Maw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.18.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.14.tgz", - "integrity": "sha512-K0QjGbcskx+gY+qp3v4/940qg8JitpXbdxFhRDA1aYoNaPff88+aEwoq45aqJ+ogpxQxmU0ZTjgnrQD/w8iiUg==", - "dev": true, - "optional": true - }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", diff --git a/package.json b/package.json index 98d858cee..f666f121f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-auth-react", - "version": "0.35.6", + "version": "0.36.0", "description": "ReactJS SDK that provides login functionality with SuperTokens.", "main": "./index.js", "engines": { diff --git a/test/end-to-end/signup.test.js b/test/end-to-end/signup.test.js index 857bfbc42..829ad2c4f 100644 --- a/test/end-to-end/signup.test.js +++ b/test/end-to-end/signup.test.js @@ -39,6 +39,8 @@ import { getGeneralError, waitForSTElement, backendBeforeEach, + setSelectDropdownValue, + getInputField, } from "../helpers"; import { @@ -342,6 +344,314 @@ describe("SuperTokens SignUp", function () { assert.deepStrictEqual(emailError, "This email already exists. Please sign in instead."); }); }); + + describe("Signup custom fields test", function () { + beforeEach(async function () { + // set cookie and reload which loads the form with custom field + await page.evaluate(() => window.localStorage.setItem("SHOW_CUSTOM_FIELDS", "YES")); + + await page.reload({ + waitUntil: "domcontentloaded", + }); + await toggleSignInSignUp(page); + }); + + it("Check if the custom fields are loaded", async function () { + let text = await getAuthPageHeaderText(page); + assert.deepStrictEqual(text, "Sign Up"); + + // check if select dropdown is loaded + const selectDropdownExists = await waitForSTElement(page, "select"); + assert.ok(selectDropdownExists, "Select dropdown exists"); + + // check if checbox is loaded + const checkboxExists = await waitForSTElement(page, 'input[type="checkbox"]'); + assert.ok(checkboxExists, "Checkbox exists"); + }); + + it("Should show error messages, based on optional flag", async function () { + await submitForm(page); + let formFieldErrors = await getFieldErrors(page); + + // 2 regular form field errors + + // 1 required custom field => terms checkbox + assert.deepStrictEqual(formFieldErrors, [ + "Field is not optional", + "Field is not optional", + "Field is not optional", + ]); + + // supply values for regular-required fields only + await setInputValues(page, [ + { name: "email", value: "jack.doe@supertokens.io" }, + { name: "password", value: "Str0ngP@ssw0rd" }, + ]); + + await submitForm(page); + formFieldErrors = await getFieldErrors(page); + assert.deepStrictEqual(formFieldErrors, ["Field is not optional"]); + + // check terms and condition checkbox + let termsCheckbox = await waitForSTElement(page, '[name="terms"]'); + await page.evaluate((e) => e.click(), termsCheckbox); + + //un-checking the required checkbox should throw custom error message + await page.evaluate((e) => e.click(), termsCheckbox); + + await submitForm(page); + formFieldErrors = await getFieldErrors(page); + assert.deepStrictEqual(formFieldErrors, ["Please check Terms and conditions"]); + }); + + it("Check if custom values are part of the signup payload", async function () { + const customFields = { + terms: "true", + "select-dropdown": "option 3", + }; + let assertionError = null; + let interceptionPassed = false; + + const requestHandler = async (request) => { + if (request.url().includes(SIGN_UP_API) && request.method() === "POST") { + try { + const postData = JSON.parse(request.postData()); + Object.keys(customFields).forEach((key) => { + let findFormData = postData.formFields.find((inputData) => inputData.id === key); + if (findFormData) { + assert.strictEqual( + findFormData["value"], + customFields[key], + `Mismatch in payload for key: ${key}` + ); + } else { + throw new Error("Field not found in req.data"); + } + }); + interceptionPassed = true; + return request.respond({ + status: 200, + headers: { + "access-control-allow-origin": TEST_CLIENT_BASE_URL, + "access-control-allow-credentials": "true", + }, + body: JSON.stringify({ + status: "OK", + }), + }); + } catch (error) { + assertionError = error; // Store the error + } + } + return request.continue(); + }; + + await page.setRequestInterception(true); + page.on("request", requestHandler); + + try { + // Fill and submit the form with custom fields + await setInputValues(page, [ + { name: "email", value: "john.doe@supertokens.io" }, + { name: "password", value: "Str0ngP@assw0rd" }, + ]); + + await setSelectDropdownValue(page, "select", customFields["select-dropdown"]); + + // Check terms and condition checkbox + let termsCheckbox = await waitForSTElement(page, '[name="terms"]'); + await page.evaluate((e) => e.click(), termsCheckbox); + + // Perform the button click and wait for all network activity to finish + await Promise.all([page.waitForNetworkIdle(), submitForm(page)]); + } finally { + page.off("request", requestHandler); + await page.setRequestInterception(false); + } + + if (assertionError) { + throw assertionError; + } + + if (!interceptionPassed) { + throw new Error("test failed"); + } + }); + }); + + // Default values test + describe("Signup default value for fields test", function () { + beforeEach(async function () { + // set cookie and reload which loads the form fields with default values + await page.evaluate(() => window.localStorage.setItem("SHOW_CUSTOM_FIELDS_WITH_DEFAULT_VALUES", "YES")); + + await page.reload({ + waitUntil: "domcontentloaded", + }); + await toggleSignInSignUp(page); + }); + + it("Check if default values are set already", async function () { + const fieldsWithDefault = { + country: "India", + "select-dropdown": "option 2", + terms: true, + }; + + // regular input field default value + const countryInput = await getInputField(page, "country"); + const defaultCountry = await countryInput.evaluate((f) => f.value); + assert.strictEqual(defaultCountry, fieldsWithDefault["country"]); + + // custom dropdown default value is also getting set correctly + const selectDropdown = await waitForSTElement(page, "select"); + const defaultOption = await selectDropdown.evaluate((f) => f.value); + assert.strictEqual(defaultOption, fieldsWithDefault["select-dropdown"]); + + // custom dropdown default value is also getting set correctly + const termsCheckbox = await waitForSTElement(page, '[name="terms"]'); + // checkbox is checked + const defaultChecked = await termsCheckbox.evaluate((f) => f.checked); + assert.strictEqual(defaultChecked, fieldsWithDefault["terms"]); + // also the value = string + const defaultValue = await termsCheckbox.evaluate((f) => f.value); + assert.strictEqual(defaultValue, fieldsWithDefault["terms"].toString()); + }); + + it("Check if changing the field value actually overwrites the default value", async function () { + const updatedFields = { + country: "USA", + "select-dropdown": "option 3", + }; + + await setInputValues(page, [{ name: "country", value: updatedFields["country"] }]); + await setSelectDropdownValue(page, "select", updatedFields["select-dropdown"]); + + // input field default value + const countryInput = await getInputField(page, "country"); + const updatedCountry = await countryInput.evaluate((f) => f.value); + assert.strictEqual(updatedCountry, updatedFields["country"]); + + // dropdown default value is also getting set correctly + const selectDropdown = await waitForSTElement(page, "select"); + const updatedOption = await selectDropdown.evaluate((f) => f.value); + assert.strictEqual(updatedOption, updatedFields["select-dropdown"]); + }); + + it("Check if default values are getting sent in signup-payload", async function () { + // directly submit the form and test the payload + const expectedDefautlValues = [ + { id: "email", value: "test@one.com" }, + { id: "password", value: "fakepassword123" }, + { id: "terms", value: "true" }, + { id: "select-dropdown", value: "option 2" }, + { id: "country", value: "India" }, + ]; + + let assertionError = null; + let interceptionPassed = false; + + const requestHandler = async (request) => { + if (request.url().includes(SIGN_UP_API) && request.method() === "POST") { + try { + const postData = JSON.parse(request.postData()); + expectedDefautlValues.forEach(({ id, value }) => { + let findFormData = postData.formFields.find((inputData) => inputData.id === id); + if (findFormData) { + assert.strictEqual(findFormData["value"], value, `Mismatch in payload for key: ${id}`); + } else { + throw new Error("Field not found in req.data"); + } + }); + interceptionPassed = true; + return request.respond({ + status: 200, + headers: { + "access-control-allow-origin": TEST_CLIENT_BASE_URL, + "access-control-allow-credentials": "true", + }, + body: JSON.stringify({ + status: "OK", + }), + }); + } catch (error) { + assertionError = error; // Store the error + } + } + return request.continue(); + }; + + await page.setRequestInterception(true); + page.on("request", requestHandler); + + try { + // Perform the button click and wait for all network activity to finish + await Promise.all([page.waitForNetworkIdle(), submitForm(page)]); + } finally { + page.off("request", requestHandler); + await page.setRequestInterception(false); + } + + if (assertionError) { + throw assertionError; + } + + if (!interceptionPassed) { + throw new Error("test failed"); + } + }); + }); + + describe("Incorrect field config test", function () { + beforeEach(async function () { + // set cookie and reload which loads the form fields with default values + await page.evaluate(() => window.localStorage.setItem("SHOW_INCORRECT_FIELDS", "YES")); + + await page.reload({ + waitUntil: "domcontentloaded", + }); + }); + + it("Check if incorrect getDefaultValue throws error", async function () { + let pageErrorMessage = ""; + page.on("pageerror", (err) => { + pageErrorMessage = err.message; + }); + + await page.reload({ + waitUntil: "domcontentloaded", + }); + await toggleSignInSignUp(page); + + const expectedErrorMessage = "getDefaultValue for country must return a string"; + assert( + pageErrorMessage.includes(expectedErrorMessage), + `Expected "${expectedErrorMessage}" to be included in page-error` + ); + }); + + it("Check if non-string params to onChange throws error", async function () { + await page.evaluate(() => window.localStorage.setItem("INCORRECT_ONCHANGE", "YES")); + await page.reload({ + waitUntil: "domcontentloaded", + }); + await toggleSignInSignUp(page); + + let pageErrorMessage = ""; + page.on("pageerror", (err) => { + pageErrorMessage = err.message; + }); + + // check terms and condition checkbox since it emits non-string value => boolean + let termsCheckbox = await waitForSTElement(page, '[name="terms"]'); + await page.evaluate((e) => e.click(), termsCheckbox); + + const expectedErrorMessage = "terms value must be a string"; + assert( + pageErrorMessage.includes(expectedErrorMessage), + `Expected "${expectedErrorMessage}" to be included in page-error` + ); + }); + }); }); describe("SuperTokens SignUp => Server Error", function () { diff --git a/test/helpers.js b/test/helpers.js index bc7138534..78a93b800 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -417,6 +417,17 @@ export async function setInputValues(page, fields) { return await new Promise((r) => setTimeout(r, 300)); } +export async function setSelectDropdownValue(page, selector, optionValue) { + return await page.evaluate( + ({ selector, ST_ROOT_SELECTOR, optionValue }) => { + const select = document.querySelector(ST_ROOT_SELECTOR).shadowRoot.querySelector(selector); + select.value = optionValue; + select.dispatchEvent(new Event("change", { bubbles: true })); + }, + { selector, ST_ROOT_SELECTOR, optionValue } + ); +} + export async function clearBrowserCookiesWithoutAffectingConsole(page, console) { let toReturn = [...console]; const client = await page.target().createCDPSession(); diff --git a/test/with-typescript/src/App.tsx b/test/with-typescript/src/App.tsx index 65b0a3a5d..1f8a4b3fa 100644 --- a/test/with-typescript/src/App.tsx +++ b/test/with-typescript/src/App.tsx @@ -373,6 +373,71 @@ function getEmailPasswordConfigs() { placeholder: "Where do you live?", optional: true, }, + { + id: "terms", + label: "", + optional: false, + getDefaultValue: () => "true", + inputComponent: (inputProps) => ( +
+ { + if (inputProps.onChange) { + inputProps.onChange(e.target.checked.toString()); + } + }}> + I agree to the terms and conditions +
+ ), + validate: async (value) => { + if (value === "true") { + return undefined; + } + return "Please check Terms and conditions"; + }, + }, + { + id: "select", + label: "Select", + getDefaultValue: () => "option 2", + inputComponent: (inputProps) => ( + + ), + optional: true, + }, ], }, }, From 7b9896e359ae3663fd7d96d0d891cd64f9d1d475 Mon Sep 17 00:00:00 2001 From: Amit Badala Date: Tue, 7 Nov 2023 17:29:41 +0530 Subject: [PATCH 02/10] feat: add getDefaultValue to signin & signup form fields (#756) * Add getDefaultValue to signin, hence shuffle the existing types * Add tests for default signin feature * Add default feature to typescript * Fix failed test * Reset password now supports getDefaultValue * Add tests for resetPassword email field * Revert "Add tests for resetPassword email field" This reverts commit 363b575b7251bac02b250dc6e6965d39a8d68e4a. * Revert "Reset password now supports getDefaultValue" This reverts commit be4c00a024401d093a5b80033a6c3e7ea9839cb1. --- examples/for-tests/src/App.js | 20 +++++ lib/build/recipe/emailpassword/types.d.ts | 3 - lib/build/types.d.ts | 2 + lib/ts/recipe/emailpassword/types.ts | 7 -- lib/ts/types.ts | 10 +++ test/end-to-end/signin.test.js | 89 +++++++++++++++++++++++ test/with-typescript/src/App.tsx | 7 ++ 7 files changed, 128 insertions(+), 10 deletions(-) diff --git a/examples/for-tests/src/App.js b/examples/for-tests/src/App.js index 2beab128d..505beeb61 100644 --- a/examples/for-tests/src/App.js +++ b/examples/for-tests/src/App.js @@ -721,6 +721,25 @@ function getFormFields() { return formFields; } +function getSignInFormFields() { + let showDefaultFields = localStorage.getItem("SHOW_SIGNIN_DEFAULT_FIELDS"); + if (showDefaultFields === "YES") { + return { + formFields: [ + { + id: "email", + getDefaultValue: () => "abc@xyz.com", + }, + { + id: "password", + getDefaultValue: () => "fakepassword123", + }, + ], + }; + } + return {}; +} + function getEmailPasswordConfigs({ disableDefaultUI }) { return EmailPassword.init({ style: ` @@ -801,6 +820,7 @@ function getEmailPasswordConfigs({ disableDefaultUI }) { defaultToSignUp, signInForm: { style: theme, + ...getSignInFormFields(), }, signUpForm: { style: theme, diff --git a/lib/build/recipe/emailpassword/types.d.ts b/lib/build/recipe/emailpassword/types.d.ts index 70c7ffcfd..d1cb6e9ea 100644 --- a/lib/build/recipe/emailpassword/types.d.ts +++ b/lib/build/recipe/emailpassword/types.d.ts @@ -79,7 +79,6 @@ export declare type NormalisedSignInAndUpFeatureConfig = { export declare type SignUpFormFeatureUserInput = FeatureBaseConfig & { formFields?: (FormField & { inputComponent?: (props: InputProps) => JSX.Element; - getDefaultValue?: () => string; })[]; privacyPolicyLink?: string; termsOfServiceLink?: string; @@ -87,7 +86,6 @@ export declare type SignUpFormFeatureUserInput = FeatureBaseConfig & { export declare type NormalisedSignUpFormFeatureConfig = NormalisedBaseConfig & { formFields: (NormalisedFormField & { inputComponent?: (props: InputProps) => JSX.Element; - getDefaultValue?: () => string; })[]; privacyPolicyLink?: string; termsOfServiceLink?: string; @@ -153,7 +151,6 @@ export declare type FormFieldThemeProps = NormalisedFormField & { showIsRequired?: boolean; clearOnSubmit?: boolean; inputComponent?: (props: InputProps) => JSX.Element; - getDefaultValue?: () => string; }; export declare type FormFieldError = { id: string; diff --git a/lib/build/types.d.ts b/lib/build/types.d.ts index 1aa7f2568..93605a26e 100644 --- a/lib/build/types.d.ts +++ b/lib/build/types.d.ts @@ -89,6 +89,7 @@ export declare type FormFieldBaseConfig = { id: string; label: string; placeholder?: string; + getDefaultValue?: () => string; }; export declare type FormField = FormFieldBaseConfig & { validate?: (value: any) => Promise; @@ -106,6 +107,7 @@ export declare type NormalisedFormField = { optional: boolean; autoComplete?: string; autofocus?: boolean; + getDefaultValue?: () => string; }; export declare type ReactComponentClass

= ComponentClass | ((props: P) => JSX.Element); export declare type FeatureBaseConfig = { diff --git a/lib/ts/recipe/emailpassword/types.ts b/lib/ts/recipe/emailpassword/types.ts index 8618f6d1e..5ffe5b3d1 100644 --- a/lib/ts/recipe/emailpassword/types.ts +++ b/lib/ts/recipe/emailpassword/types.ts @@ -136,7 +136,6 @@ export type SignUpFormFeatureUserInput = FeatureBaseConfig & { */ formFields?: (FormField & { inputComponent?: (props: InputProps) => JSX.Element; - getDefaultValue?: () => string; })[]; /* @@ -156,7 +155,6 @@ export type NormalisedSignUpFormFeatureConfig = NormalisedBaseConfig & { */ formFields: (NormalisedFormField & { inputComponent?: (props: InputProps) => JSX.Element; - getDefaultValue?: () => string; })[]; /* @@ -289,11 +287,6 @@ export type FormFieldThemeProps = NormalisedFormField & { * Ability to add custom components */ inputComponent?: (props: InputProps) => JSX.Element; - - /* - * Ability to add custom components - */ - getDefaultValue?: () => string; }; export type FormFieldError = { diff --git a/lib/ts/types.ts b/lib/ts/types.ts index 8399585b5..16beba60b 100644 --- a/lib/ts/types.ts +++ b/lib/ts/types.ts @@ -222,6 +222,11 @@ export type FormFieldBaseConfig = { * placeholder of the input field. */ placeholder?: string; + + /* + *Ability to provide default value to input field. + */ + getDefaultValue?: () => string; }; export type FormField = FormFieldBaseConfig & { @@ -283,6 +288,11 @@ export type NormalisedFormField = { * Moves focus to the input element when it mounts */ autofocus?: boolean; + + /* + *Ability to provide default value to input field. + */ + getDefaultValue?: () => string; }; export type ReactComponentClass

= ComponentClass | ((props: P) => JSX.Element); diff --git a/test/end-to-end/signin.test.js b/test/end-to-end/signin.test.js index 86a204645..44c474c6d 100644 --- a/test/end-to-end/signin.test.js +++ b/test/end-to-end/signin.test.js @@ -53,6 +53,7 @@ import { getInvalidClaimsJSON as getInvalidClaims, waitForText, backendBeforeEach, + getInputField, } from "../helpers"; import fetch from "isomorphic-fetch"; import { SOMETHING_WENT_WRONG_ERROR } from "../constants"; @@ -669,6 +670,94 @@ describe("SuperTokens SignIn", function () { }); }); }); + + describe("SignIn default field tests", function () { + it("Should contain email and password fields prefilled", async function () { + await page.evaluate(() => window.localStorage.setItem("SHOW_SIGNIN_DEFAULT_FIELDS", "YES")); + + await page.reload({ + waitUntil: "domcontentloaded", + }); + + const expectedDefaultValues = { + email: "abc@xyz.com", + password: "fakepassword123", + }; + + const emailInput = await getInputField(page, "email"); + const defaultEmail = await emailInput.evaluate((f) => f.value); + assert.strictEqual(defaultEmail, expectedDefaultValues["email"]); + + const passwordInput = await getInputField(page, "password"); + const defaultPassword = await passwordInput.evaluate((f) => f.value); + assert.strictEqual(defaultPassword, expectedDefaultValues["password"]); + }); + + it("Should have default values in the signin request payload", async function () { + await page.evaluate(() => window.localStorage.setItem("SHOW_SIGNIN_DEFAULT_FIELDS", "YES")); + + await page.reload({ + waitUntil: "domcontentloaded", + }); + + const expectedDefautlValues = [ + { id: "email", value: "abc@xyz.com" }, + { id: "password", value: "fakepassword123" }, + ]; + + let assertionError = null; + let interceptionPassed = false; + + const requestHandler = async (request) => { + if (request.url().includes(SIGN_IN_API) && request.method() === "POST") { + try { + const postData = JSON.parse(request.postData()); + expectedDefautlValues.forEach(({ id, value }) => { + let findFormData = postData.formFields.find((inputData) => inputData.id === id); + if (findFormData) { + assert.strictEqual(findFormData["value"], value, `Mismatch in payload for key: ${id}`); + } else { + throw new Error("Field not found in req.data"); + } + }); + interceptionPassed = true; + return request.respond({ + status: 200, + headers: { + "access-control-allow-origin": TEST_CLIENT_BASE_URL, + "access-control-allow-credentials": "true", + }, + body: JSON.stringify({ + status: "OK", + }), + }); + } catch (error) { + assertionError = error; // Store the error + } + } + return request.continue(); + }; + + await page.setRequestInterception(true); + page.on("request", requestHandler); + + try { + // Perform the button click and wait for all network activity to finish + await Promise.all([page.waitForNetworkIdle(), submitForm(page)]); + } finally { + page.off("request", requestHandler); + await page.setRequestInterception(false); + } + + if (assertionError) { + throw assertionError; + } + + if (!interceptionPassed) { + throw new Error("test failed"); + } + }); + }); }); describe("SuperTokens SignIn => Server Error", function () { diff --git a/test/with-typescript/src/App.tsx b/test/with-typescript/src/App.tsx index 1f8a4b3fa..c06d3fff7 100644 --- a/test/with-typescript/src/App.tsx +++ b/test/with-typescript/src/App.tsx @@ -339,6 +339,13 @@ function getEmailPasswordConfigs() { signInAndUpFeature: { signInForm: { style: theme.style, + formFields: [ + { + id: "email", + label: "Email", + getDefaultValue: () => "abc@xyz.com", + }, + ], }, signUpForm: { style: theme.style, From 52f8d93d54268716d4afcaee73313e34bc44f7fc Mon Sep 17 00:00:00 2001 From: Amit Badala Date: Sat, 11 Nov 2023 11:45:02 +0530 Subject: [PATCH 03/10] feat: add nonOptionalErrorMsg props (#757) * Add nonOptionalErr types * Now supports nonOptionalErrorMsg attribute * Add tests and fix signin types to include nonOptionalMsg * Enforce no api request are made on blank forms * Clean up signup * Throw error if invalid nonOptionalErrorMsg, add tests for the same * Better error message * Handle incorrect optional flag * fixes redundant normalisation --------- Co-authored-by: rishabhpoddar --- examples/for-tests/src/App.js | 22 ++++ lib/build/emailpassword-shared4.js | 7 + lib/build/emailpassword-shared7.js | 47 +++++-- .../submitNewPassword.d.ts | 2 +- .../components/themes/signInAndUp/signIn.d.ts | 4 +- .../components/themes/signInAndUp/signUp.d.ts | 7 +- .../components/themes/translations.d.ts | 6 - .../components/themes/translations.d.ts | 5 - lib/build/types.d.ts | 2 + .../components/library/formBase.tsx | 16 ++- lib/ts/recipe/emailpassword/utils.ts | 7 + lib/ts/types.ts | 10 ++ test/end-to-end/signin.test.js | 44 +++++++ test/end-to-end/signup.test.js | 124 +++++++++++++++++- test/with-typescript/src/App.tsx | 2 + 15 files changed, 272 insertions(+), 33 deletions(-) diff --git a/examples/for-tests/src/App.js b/examples/for-tests/src/App.js index 505beeb61..7c546a35d 100644 --- a/examples/for-tests/src/App.js +++ b/examples/for-tests/src/App.js @@ -279,12 +279,19 @@ const incorrectFormFields = [ return "Please check Terms and conditions"; }, }, + { + id: "city", + label: "Your city", + optional: false, + nonOptionalErrorMsg: "", // empty string should throw error + }, ]; const customFields = [ { id: "select-dropdown", label: "Select Dropdown", + nonOptionalErrorMsg: "Select dropdown is not an optional", inputComponent: ({ value, name, onChange }) => ( { + if (inputProps.onChange) { + inputProps.onChange(e.target.checked.toString()); + } + }}> + I agree to the terms and conditions + + ), + validate: async (value) => { + if (value === "true") { + return undefined; + } + return "Please check Terms and conditions"; + }, + }, + { + id: "select", + label: "Select", + getDefaultValue: () => "option 2", + inputComponent: (inputProps) => ( + + ), + optional: true, + }, + ], }, providers: [ ThirdPartyEmailPassword.Github.init(), From f0a98a6aa4c41b8b9213d53e41b94fc2904e84f7 Mon Sep 17 00:00:00 2001 From: amitbadala Date: Mon, 13 Nov 2023 18:14:24 +0530 Subject: [PATCH 06/10] Run build-pretty --- .../themes/resetPasswordUsingToken/submitNewPassword.d.ts | 2 +- .../components/themes/signInAndUp/signIn.d.ts | 4 ++-- .../components/themes/signInAndUp/signUp.d.ts | 7 +++---- .../components/themes/translations.d.ts | 6 ++++++ .../components/themes/translations.d.ts | 5 +++++ 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts index b8ec36fd7..9c795dce2 100644 --- a/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/resetPasswordUsingToken/submitNewPassword.d.ts @@ -1,7 +1,7 @@ /// export declare const SubmitNewPassword: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; + formFields: Omit[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts index 46dd93ed6..64639bb96 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signIn.d.ts @@ -1,7 +1,7 @@ /// export declare const SignIn: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; + formFields: Omit[]; error: string | undefined; } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; @@ -10,6 +10,6 @@ export declare const SignIn: import("react").ComponentType< config: import("../../../types").NormalisedConfig; signUpClicked?: (() => void) | undefined; forgotPasswordClick: () => void; - onSuccess: () => void; + onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; } >; diff --git a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts index 1a0268aa8..32bc386b1 100644 --- a/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts +++ b/lib/build/recipe/emailpassword/components/themes/signInAndUp/signUp.d.ts @@ -1,14 +1,13 @@ /// export declare const SignUp: import("react").ComponentType< import("../../../../../types").ThemeBaseProps & { - formFields: import("../../../types").FormFieldThemeProps[]; - error: string | undefined; - } & { recipeImplementation: import("supertokens-web-js/recipe/emailpassword").RecipeInterface; clearError: () => void; onError: (error: string) => void; config: import("../../../types").NormalisedConfig; signInClicked?: (() => void) | undefined; - onSuccess: () => void; + onSuccess: (result: { user: import("supertokens-web-js/types").User }) => void; + formFields: import("../../../types").FormFieldThemeProps[]; + error: string | undefined; } >; diff --git a/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts b/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts index bb256d116..454ff978e 100644 --- a/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts +++ b/lib/build/recipe/thirdpartyemailpassword/components/themes/translations.d.ts @@ -58,6 +58,9 @@ export declare const defaultTranslationsThirdPartyEmailPassword: { "Password must contain at least one alphabet": undefined; "Password must contain at least one number": undefined; "Email is invalid": undefined; + "Reset password link was not created because of account take over risk. Please contact support. (ERR_CODE_001)": undefined; + "Cannot sign up due to security reasons. Please try logging in, use a different login method or contact support. (ERR_CODE_007)": undefined; + "Cannot sign in due to security reasons. Please try resetting your password, use a different login method or contact support. (ERR_CODE_008)": undefined; EMAIL_VERIFICATION_RESEND_SUCCESS: string; EMAIL_VERIFICATION_SEND_TITLE: string; EMAIL_VERIFICATION_SEND_DESC_START: string; @@ -86,5 +89,8 @@ export declare const defaultTranslationsThirdPartyEmailPassword: { THIRD_PARTY_PROVIDER_DEFAULT_BTN_START: string; THIRD_PARTY_PROVIDER_DEFAULT_BTN_END: string; THIRD_PARTY_ERROR_NO_EMAIL: string; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_004)": undefined; + "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_006)": undefined; }; }; diff --git a/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts b/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts index dbe4e941e..b24b59e60 100644 --- a/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts +++ b/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts @@ -57,6 +57,8 @@ export declare const defaultTranslationsThirdPartyPasswordless: { "Failed to generate a one time code. Please try again": undefined; "Phone number is invalid": undefined; "Email is invalid": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_002)": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_003)": undefined; BRANDING_POWERED_BY_START: string; BRANDING_POWERED_BY_END: string; SOMETHING_WENT_WRONG_ERROR: string; @@ -69,5 +71,8 @@ export declare const defaultTranslationsThirdPartyPasswordless: { THIRD_PARTY_PROVIDER_DEFAULT_BTN_START: string; THIRD_PARTY_PROVIDER_DEFAULT_BTN_END: string; THIRD_PARTY_ERROR_NO_EMAIL: string; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_004)": undefined; + "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)": undefined; + "Cannot sign in / up due to security reasons. Please try a different login method or contact support. (ERR_CODE_006)": undefined; }; }; From b43bf9b01a294a75702958820a72d9db79232d75 Mon Sep 17 00:00:00 2001 From: amitbadala Date: Tue, 14 Nov 2023 11:33:20 +0530 Subject: [PATCH 07/10] Set correct flag --- test/end-to-end/thirdpartyemailpassword.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/end-to-end/thirdpartyemailpassword.test.js b/test/end-to-end/thirdpartyemailpassword.test.js index 6d956421e..bff13ae99 100644 --- a/test/end-to-end/thirdpartyemailpassword.test.js +++ b/test/end-to-end/thirdpartyemailpassword.test.js @@ -75,7 +75,7 @@ describe("SuperTokens Third Party Email Password", function () { browser = await puppeteer.launch({ args: ["--no-sandbox", "--disable-setuid-sandbox"], - headless: false, + headless: true, }); page = await browser.newPage(); page.on("console", (consoleObj) => { From 45dd28cfdb81f0e18e58d581c6590c0f9bc07c3d Mon Sep 17 00:00:00 2001 From: Amit Badala Date: Mon, 20 Nov 2023 14:29:58 +0530 Subject: [PATCH 08/10] fix: display required indicator only for non-empty labels and Improve test structure (#762) * Show required sign only if label is valid * Better func names & consistent return type * Use assert instead of throw error * Consistent tests description * Remove unecessary code * Add correct version number * Update changelog, add thirdparty example * Minor fox * Read from testContext * Refactor tests to ensure its easy to maintain different configurations * Update third party tests * Clean up * Minor copy update * Trim the label text * Add build * Handle if label is not supplied * Highlight var in changelog, minor update * Update custom payload to test for trimmed-version label --- CHANGELOG.md | 43 ++++++- examples/for-tests/src/App.js | 70 ++++++------ examples/for-tests/src/testContext.js | 4 + lib/build/emailpassword-shared7.js | 2 +- lib/build/version.d.ts | 2 +- .../components/library/label.tsx | 2 +- lib/ts/version.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- test/end-to-end/signin.test.js | 38 ++---- test/end-to-end/signup.test.js | 108 ++++++++---------- .../thirdpartyemailpassword.test.js | 32 +++--- 12 files changed, 161 insertions(+), 148 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d966973be..1c84f6e6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,15 @@ 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.36.0] - 2023-10-30 +## [0.35.7] - 2023-11-16 ### Added -- Introduced the capability to utilize custom components in the Email-Password based recipes' signup form fields by exposing inputComponent types. -- Implemented the functionality to assign default values to the form fields in the Email-Password based recipes. -- Simplified onChange prop usage in inputComponent - id attribute removed. +- EmailPassword and ThirdPartyEmailPassword recipe enhancements: + - Introduced the capability to utilize custom components by exposing `inputComponent` types. + - Allow setting default values in signup/signin form fields. + - Made the `onChange` prop in `inputComponent` simpler by removing the need for an `id` attribute. + - Added a feature to customize the "Field is not optional" error message for each form field with the `nonOptionalErrorMsg` prop. Following is an example of how to use above features. @@ -24,6 +26,7 @@ EmailPassword.init({ id: "select-dropdown", label: "Select Option", getDefaultValue: () => "option 2", + nonOptionalErrorMsg: "Select dropdown is required", inputComponent: ({ value, name, onChange }) => ( onChange(e.target.checked.toString())}> + I agree to the terms and conditions + + ), + }, + ], + }, + }, +}); ``` ## [0.35.6] - 2023-10-16 diff --git a/examples/for-tests/src/App.js b/examples/for-tests/src/App.js index 62b155995..add8541e8 100644 --- a/examples/for-tests/src/App.js +++ b/examples/for-tests/src/App.js @@ -306,7 +306,7 @@ const customFields = [ }, { id: "terms", - label: "", + label: " ", optional: false, nonOptionalErrorMsg: "You must accept the terms and conditions", inputComponent: ({ name, onChange }) => ( @@ -713,32 +713,29 @@ function getEmailVerificationConfigs({ disableDefaultUI }) { }); } -function getFormFields() { - if (localStorage.getItem("SHOW_INCORRECT_FIELDS") === "YES") { - if (localStorage.getItem("INCORRECT_ONCHANGE") === "YES") { - // since page-error blocks all the other errors - // use this filter to test specific error +function getSignUpFormFields(formType) { + switch (formType) { + case "INCORRECT_FIELDS": + return incorrectFormFields; + case "INCORRECT_ONCHANGE": return incorrectFormFields.filter(({ id }) => id === "terms"); - } else if (localStorage.getItem("INCORRECT_NON_OPTIONAL_ERROR_MSG") === "YES") { + case "INCORRECT_NON_OPTIONAL_ERROR_MSG": return incorrectFormFields.filter(({ id }) => id === "city"); - } else if (localStorage.getItem("INCORRECT_GETDEFAULT") === "YES") { + case "INCORRECT_GETDEFAULT": return incorrectFormFields.filter(({ id }) => id === "country"); - } - return incorrectFormFields; - } else if (localStorage.getItem("SHOW_CUSTOM_FIELDS_WITH_DEFAULT_VALUES") === "YES") { - return formFieldsWithDefault; - } else if (localStorage.getItem("SHOW_CUSTOM_FIELDS") === "YES") { - return customFields; + case "CUSTOM_FIELDS_WITH_DEFAULT_VALUES": + return formFieldsWithDefault; + case "CUSTOM_FIELDS": + return customFields; + default: + return formFields; } - return formFields; } -function getSignInFormFields() { - let showDefaultFields = localStorage.getItem("SHOW_SIGNIN_DEFAULT_FIELDS"); - let showFieldsWithNonOptionalErrMsg = localStorage.getItem("SHOW_SIGNIN_WITH_NON_OPTIONAL_ERROR_MESSAGE"); - if (showDefaultFields === "YES") { - return { - formFields: [ +function getSignInFormFields(formType) { + switch (formType) { + case "DEFAULT_FIELDS": + return [ { id: "email", getDefaultValue: () => "abc@xyz.com", @@ -747,22 +744,20 @@ function getSignInFormFields() { id: "password", getDefaultValue: () => "fakepassword123", }, - ], - }; - } else if (showFieldsWithNonOptionalErrMsg === "YES") { - return { - formFields: [ + ]; + case "FIELDS_WITH_NON_OPTIONAL_ERROR_MESSAGE": + return [ { id: "email", nonOptionalErrorMsg: "Please add email", }, - ], - }; + ]; + default: + return; } - return {}; } -function getEmailPasswordConfigs({ disableDefaultUI }) { +function getEmailPasswordConfigs({ disableDefaultUI, formFieldType }) { return EmailPassword.init({ style: ` [data-supertokens~=container] { @@ -842,13 +837,13 @@ function getEmailPasswordConfigs({ disableDefaultUI }) { defaultToSignUp, signInForm: { style: theme, - ...getSignInFormFields(), + formFields: getSignInFormFields(formFieldType.signIn), }, signUpForm: { style: theme, privacyPolicyLink: "https://supertokens.com/legal/privacy-policy", termsOfServiceLink: "https://supertokens.com/legal/terms-and-conditions", - formFields: getFormFields(), + formFields: getSignUpFormFields(formFieldType.signUp), }, }, }); @@ -1192,7 +1187,12 @@ function getThirdPartyConfigs({ staticProviderList, disableDefaultUI, thirdParty }); } -function getThirdPartyEmailPasswordConfigs({ staticProviderList, disableDefaultUI, thirdPartyRedirectURL }) { +function getThirdPartyEmailPasswordConfigs({ + staticProviderList, + disableDefaultUI, + thirdPartyRedirectURL, + formFieldType, +}) { let providers = [ ThirdParty.Github.init(), ThirdParty.Google.init(), @@ -1372,10 +1372,10 @@ function getThirdPartyEmailPasswordConfigs({ staticProviderList, disableDefaultU signInAndUpFeature: { disableDefaultUI, signInForm: { - ...getSignInFormFields(), + formFields: getSignInFormFields(formFieldType.signIn), }, signUpForm: { - formFields: getFormFields(), + formFields: getSignUpFormFields(formFieldType.signUp), privacyPolicyLink: "https://supertokens.com/legal/privacy-policy", termsOfServiceLink: "https://supertokens.com/legal/terms-and-conditions", }, diff --git a/examples/for-tests/src/testContext.js b/examples/for-tests/src/testContext.js index 618e990ba..3021a9b92 100644 --- a/examples/for-tests/src/testContext.js +++ b/examples/for-tests/src/testContext.js @@ -14,6 +14,10 @@ export function getTestContext() { staticProviderList: localStorage.getItem("staticProviderList"), mockTenantId: localStorage.getItem("mockTenantId"), clientType: localStorage.getItem("clientType") || undefined, + formFieldType: { + signIn: localStorage.getItem("SIGNIN_SETTING_TYPE"), + signUp: localStorage.getItem("SIGNUP_SETTING_TYPE"), + }, }; return ret; } diff --git a/lib/build/emailpassword-shared7.js b/lib/build/emailpassword-shared7.js index db90a2acb..e48dc5334 100644 --- a/lib/build/emailpassword-shared7.js +++ b/lib/build/emailpassword-shared7.js @@ -425,7 +425,7 @@ function Label(_a) { "div", genericComponentOverrideContext.__assign( { "data-supertokens": "label" }, - { children: [t(value), showIsRequired && " *"] } + { children: [t(value), showIsRequired && value && value.trim() !== "" && " *"] } ) ); } diff --git a/lib/build/version.d.ts b/lib/build/version.d.ts index e85100a93..61197a780 100644 --- a/lib/build/version.d.ts +++ b/lib/build/version.d.ts @@ -1 +1 @@ -export declare const package_version = "0.36.0"; +export declare const package_version = "0.35.7"; diff --git a/lib/ts/recipe/emailpassword/components/library/label.tsx b/lib/ts/recipe/emailpassword/components/library/label.tsx index 5096b636a..428122455 100644 --- a/lib/ts/recipe/emailpassword/components/library/label.tsx +++ b/lib/ts/recipe/emailpassword/components/library/label.tsx @@ -26,7 +26,7 @@ export default function Label({ value, showIsRequired }: LabelProps): JSX.Elemen return (

{t(value)} - {showIsRequired && " *"} + {showIsRequired && value && value.trim() !== "" && " *"}
); } diff --git a/lib/ts/version.ts b/lib/ts/version.ts index 1fd0f9cc7..8d697de72 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.36.0"; +export const package_version = "0.35.7"; diff --git a/package-lock.json b/package-lock.json index d49cefd40..f2efb65d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-auth-react", - "version": "0.36.0", + "version": "0.35.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "supertokens-auth-react", - "version": "0.36.0", + "version": "0.35.7", "license": "Apache-2.0", "dependencies": { "intl-tel-input": "^17.0.19", diff --git a/package.json b/package.json index f666f121f..eedec85f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-auth-react", - "version": "0.36.0", + "version": "0.35.7", "description": "ReactJS SDK that provides login functionality with SuperTokens.", "main": "./index.js", "engines": { diff --git a/test/end-to-end/signin.test.js b/test/end-to-end/signin.test.js index 160a66c09..49f20b9a9 100644 --- a/test/end-to-end/signin.test.js +++ b/test/end-to-end/signin.test.js @@ -671,9 +671,9 @@ describe("SuperTokens SignIn", function () { }); }); - describe("SignIn default field tests", function () { + describe("Default fields", function () { it("Should contain email and password fields prefilled", async function () { - await page.evaluate(() => window.localStorage.setItem("SHOW_SIGNIN_DEFAULT_FIELDS", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNIN_SETTING_TYPE", "DEFAULT_FIELDS")); await page.reload({ waitUntil: "domcontentloaded", @@ -694,7 +694,7 @@ describe("SuperTokens SignIn", function () { }); it("Should have default values in the signin request payload", async function () { - await page.evaluate(() => window.localStorage.setItem("SHOW_SIGNIN_DEFAULT_FIELDS", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNIN_SETTING_TYPE", "DEFAULT_FIELDS")); await page.reload({ waitUntil: "domcontentloaded", @@ -714,11 +714,8 @@ describe("SuperTokens SignIn", function () { const postData = JSON.parse(request.postData()); expectedDefautlValues.forEach(({ id, value }) => { let findFormData = postData.formFields.find((inputData) => inputData.id === id); - if (findFormData) { - assert.strictEqual(findFormData["value"], value, `Mismatch in payload for key: ${id}`); - } else { - throw new Error("Field not found in req.data"); - } + assert.ok(findFormData, "Field not found in req.data"); + assert.strictEqual(findFormData["value"], value, `Mismatch in payload for key: ${id}`); }); interceptionPassed = true; return request.respond({ @@ -748,24 +745,16 @@ describe("SuperTokens SignIn", function () { page.off("request", requestHandler); await page.setRequestInterception(false); } - - if (assertionError) { - throw assertionError; - } - - if (!interceptionPassed) { - throw new Error("test failed"); - } + assert.ok(!assertionError, assertionError?.message); + assert.ok(interceptionPassed, "Test Failed"); }); }); - describe("Check if nonOptionalErrorMsg works as expected", function () { - it("Check on blank form submit nonOptionalErrorMsg gets displayed as expected", async function () { - await page.evaluate(() => localStorage.removeItem("SHOW_SIGNIN_DEFAULT_FIELDS")); - + describe("nonOptionalErrorMsg", function () { + it("Should be displayed on a blank form submit", async function () { // set cookie and reload which loads the form with custom field await page.evaluate(() => - window.localStorage.setItem("SHOW_SIGNIN_WITH_NON_OPTIONAL_ERROR_MESSAGE", "YES") + window.localStorage.setItem("SIGNIN_SETTING_TYPE", "FIELDS_WITH_NON_OPTIONAL_ERROR_MESSAGE") ); await page.reload({ waitUntil: "domcontentloaded", @@ -782,8 +771,6 @@ describe("SuperTokens SignIn", function () { request.continue(); } }; - - await page.setRequestInterception(true); page.on("request", requestHandler); try { @@ -794,12 +781,9 @@ describe("SuperTokens SignIn", function () { assert.deepStrictEqual(formFieldErrors, ["Please add email", "Field is not optional"]); } finally { page.off("request", requestHandler); - await page.setRequestInterception(false); } - if (apiCallMade) { - throw new Error("Empty form making API request to signin"); - } + assert.ok(!apiCallMade, "Empty form making API request to signin"); }); }); }); diff --git a/test/end-to-end/signup.test.js b/test/end-to-end/signup.test.js index 1285e6ec0..81b5b4bd8 100644 --- a/test/end-to-end/signup.test.js +++ b/test/end-to-end/signup.test.js @@ -345,10 +345,10 @@ describe("SuperTokens SignUp", function () { }); }); - describe("Signup custom fields test", function () { + describe("Custom fields tests", function () { beforeEach(async function () { // set cookie and reload which loads the form with custom field - await page.evaluate(() => window.localStorage.setItem("SHOW_CUSTOM_FIELDS", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "CUSTOM_FIELDS")); await page.reload({ waitUntil: "domcontentloaded", @@ -356,7 +356,7 @@ describe("SuperTokens SignUp", function () { await toggleSignInSignUp(page); }); - it("Check if the custom fields are loaded", async function () { + it("Should load the custom fields", async function () { let text = await getAuthPageHeaderText(page); assert.deepStrictEqual(text, "Sign Up"); @@ -367,6 +367,12 @@ describe("SuperTokens SignUp", function () { // check if checbox is loaded const checkboxExists = await waitForSTElement(page, 'input[type="checkbox"]'); assert.ok(checkboxExists, "Checkbox exists"); + + // check if labels are loaded correctly + // non-optional field with empty labels should'nt show * sign + const expectedLabels = ["Email *", "Password *", "Select Dropdown", ""]; + const labelNames = await getLabelsText(page); + assert.deepStrictEqual(labelNames, expectedLabels); }); it("Should show error messages, based on optional flag", async function () { @@ -403,7 +409,7 @@ describe("SuperTokens SignUp", function () { assert.deepStrictEqual(formFieldErrors, ["Please check Terms and conditions"]); }); - it("Check if custom values are part of the signup payload", async function () { + it("Should be part of the signup payload", async function () { const customFields = { terms: "true", "select-dropdown": "option 3", @@ -417,15 +423,12 @@ describe("SuperTokens SignUp", function () { const postData = JSON.parse(request.postData()); Object.keys(customFields).forEach((key) => { let findFormData = postData.formFields.find((inputData) => inputData.id === key); - if (findFormData) { - assert.strictEqual( - findFormData["value"], - customFields[key], - `Mismatch in payload for key: ${key}` - ); - } else { - throw new Error("Field not found in req.data"); - } + assert.ok(findFormData, "Field not found in req.data"); + assert.strictEqual( + findFormData["value"], + customFields[key], + `Mismatch in payload for key: ${key}` + ); }); interceptionPassed = true; return request.respond({ @@ -468,16 +471,11 @@ describe("SuperTokens SignUp", function () { await page.setRequestInterception(false); } - if (assertionError) { - throw assertionError; - } - - if (!interceptionPassed) { - throw new Error("test failed"); - } + assert.ok(!assertionError, assertionError?.message); + assert.ok(interceptionPassed, "Test Failed"); }); - it("Check on blank form submit nonOptionalErrorMsg gets displayed as expected", async function () { + it("Should display nonOptionalErrorMsg on blank form submit", async function () { let apiCallMade = false; const requestHandler = (request) => { @@ -490,7 +488,6 @@ describe("SuperTokens SignUp", function () { } }; - await page.setRequestInterception(true); page.on("request", requestHandler); try { @@ -505,15 +502,12 @@ describe("SuperTokens SignUp", function () { ]); } finally { page.off("request", requestHandler); - await page.setRequestInterception(false); } - if (apiCallMade) { - throw new Error("Empty form making API request to sign-up"); - } + assert.ok(!apiCallMade, "Empty form making signup API request"); }); - it("Check if nonOptionalErrorMsg overwrites server error message for non-optional fields", async function () { + it("Should overwrite server error message for non-optional fields with nonOptionalErrorMsg", async function () { const requestHandler = (request) => { if (request.method() === "POST" && request.url() === SIGN_UP_API) { request.respond({ @@ -573,10 +567,12 @@ describe("SuperTokens SignUp", function () { }); // Default values test - describe("Signup default value for fields test", function () { + describe("Default fields tests", function () { beforeEach(async function () { // set cookie and reload which loads the form fields with default values - await page.evaluate(() => window.localStorage.setItem("SHOW_CUSTOM_FIELDS_WITH_DEFAULT_VALUES", "YES")); + await page.evaluate(() => + window.localStorage.setItem("SIGNUP_SETTING_TYPE", "CUSTOM_FIELDS_WITH_DEFAULT_VALUES") + ); await page.reload({ waitUntil: "domcontentloaded", @@ -584,7 +580,7 @@ describe("SuperTokens SignUp", function () { await toggleSignInSignUp(page); }); - it("Check if default values are set already", async function () { + it("Should prefill the fields with default values", async function () { const fieldsWithDefault = { country: "India", "select-dropdown": "option 2", @@ -611,7 +607,7 @@ describe("SuperTokens SignUp", function () { assert.strictEqual(defaultValue, fieldsWithDefault["terms"].toString()); }); - it("Check if changing the field value actually overwrites the default value", async function () { + it("Should be able to overwrite the default values", async function () { const updatedFields = { country: "USA", "select-dropdown": "option 3", @@ -631,7 +627,7 @@ describe("SuperTokens SignUp", function () { assert.strictEqual(updatedOption, updatedFields["select-dropdown"]); }); - it("Check if default values are getting sent in signup-payload", async function () { + it("Should ensure signup-payload contains default values", async function () { // directly submit the form and test the payload const expectedDefautlValues = [ { id: "email", value: "test@one.com" }, @@ -650,11 +646,8 @@ describe("SuperTokens SignUp", function () { const postData = JSON.parse(request.postData()); expectedDefautlValues.forEach(({ id, value }) => { let findFormData = postData.formFields.find((inputData) => inputData.id === id); - if (findFormData) { - assert.strictEqual(findFormData["value"], value, `Mismatch in payload for key: ${id}`); - } else { - throw new Error("Field not found in req.data"); - } + assert.ok(findFormData, "Field not found in req.data"); + assert.strictEqual(findFormData["value"], value, `Mismatch in payload for key: ${id}`); }); interceptionPassed = true; return request.respond({ @@ -685,28 +678,23 @@ describe("SuperTokens SignUp", function () { await page.setRequestInterception(false); } - if (assertionError) { - throw assertionError; - } - - if (!interceptionPassed) { - throw new Error("test failed"); - } + assert.ok(!assertionError, assertionError?.message); + assert.ok(interceptionPassed, "Test Failed"); }); }); describe("Incorrect field config test", function () { beforeEach(async function () { // set cookie and reload which loads the form fields with default values - await page.evaluate(() => window.localStorage.setItem("SHOW_INCORRECT_FIELDS", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_FIELDS")); await page.reload({ waitUntil: "domcontentloaded", }); }); - it("Check if incorrect getDefaultValue throws error", async function () { - await page.evaluate(() => window.localStorage.setItem("INCORRECT_GETDEFAULT", "YES")); + it("Should throw error for incorrect getDefaultValue func call", async function () { + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_GETDEFAULT")); let pageErrorMessage = ""; page.on("pageerror", (err) => { pageErrorMessage = err.message; @@ -724,9 +712,8 @@ describe("SuperTokens SignUp", function () { ); }); - it("Check if non-string params to onChange throws error", async function () { - await page.evaluate(() => window.localStorage.removeItem("INCORRECT_GETDEFAULT")); - await page.evaluate(() => window.localStorage.setItem("INCORRECT_ONCHANGE", "YES")); + it("Should throw error for non-string params to onChange", async function () { + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_ONCHANGE")); await page.reload({ waitUntil: "domcontentloaded", }); @@ -748,28 +735,25 @@ describe("SuperTokens SignUp", function () { ); }); - it("Check if empty string for nonOptionalErrorMsg throws error", async function () { + it("Should throw error for empty string for nonOptionalErrorMsg", async function () { const expectedErrorMessage = "nonOptionalErrorMsg for field city cannot be an empty string"; let pageErrorMessage = ""; page.on("pageerror", (err) => { pageErrorMessage = err.message; }); - await page.evaluate(() => window.localStorage.removeItem("INCORRECT_GETDEFAULT")); - await page.evaluate(() => window.localStorage.removeItem("INCORRECT_ONCHANGE")); - await page.evaluate(() => window.localStorage.setItem("INCORRECT_NON_OPTIONAL_ERROR_MSG", "YES")); + await page.evaluate(() => + window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_NON_OPTIONAL_ERROR_MSG") + ); await page.reload({ waitUntil: "domcontentloaded", }); - if (pageErrorMessage !== "") { - assert( - pageErrorMessage.includes(expectedErrorMessage), - `Expected "${expectedErrorMessage}" to be included in page-error` - ); - } else { - throw "Empty nonOptionalErrorMsg should throw error"; - } + assert.ok(pageErrorMessage !== "", "Empty nonOptionalErrorMsg should throw error"); + assert( + pageErrorMessage.includes(expectedErrorMessage), + `Expected "${expectedErrorMessage}" to be included in page-error` + ); }); }); }); diff --git a/test/end-to-end/thirdpartyemailpassword.test.js b/test/end-to-end/thirdpartyemailpassword.test.js index bff13ae99..fddbc41eb 100644 --- a/test/end-to-end/thirdpartyemailpassword.test.js +++ b/test/end-to-end/thirdpartyemailpassword.test.js @@ -46,6 +46,7 @@ import { backendBeforeEach, setSelectDropdownValue, getInputField, + getLabelsText, } from "../helpers"; import { TEST_CLIENT_BASE_URL, @@ -445,7 +446,7 @@ describe("SuperTokens Third Party Email Password", function () { describe("SignIn default field tests", function () { it("Should contain email and password fields prefilled", async function () { - await page.evaluate(() => window.localStorage.setItem("SHOW_SIGNIN_DEFAULT_FIELDS", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNIN_SETTING_TYPE", "DEFAULT_FIELDS")); await page.reload({ waitUntil: "domcontentloaded", @@ -466,11 +467,9 @@ describe("SuperTokens Third Party Email Password", function () { }); it("Check on blank form submit nonOptionalErrorMsg gets displayed as expected", async function () { - await page.evaluate(() => window.localStorage.removeItem("SHOW_SIGNIN_DEFAULT_FIELDS")); - // set cookie and reload which loads the form with custom field await page.evaluate(() => - window.localStorage.setItem("SHOW_SIGNIN_WITH_NON_OPTIONAL_ERROR_MESSAGE", "YES") + window.localStorage.setItem("SIGNIN_SETTING_TYPE", "FIELDS_WITH_NON_OPTIONAL_ERROR_MESSAGE") ); await page.reload({ waitUntil: "domcontentloaded", @@ -512,7 +511,7 @@ describe("SuperTokens Third Party Email Password", function () { describe("Third Party signup config supports custom fields tests", function () { beforeEach(async function () { - await page.evaluate(() => window.localStorage.setItem("SHOW_CUSTOM_FIELDS", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "CUSTOM_FIELDS")); await page.reload({ waitUntil: "domcontentloaded", @@ -531,6 +530,12 @@ describe("SuperTokens Third Party Email Password", function () { // check if checbox is loaded const checkboxExists = await waitForSTElement(page, 'input[type="checkbox"]'); assert.ok(checkboxExists, "Checkbox exists"); + + // check if labels are loaded correctly + // non-optional field with empty labels should'nt show * sign + const expectedLabels = ["Email *", "Password *", "Select Dropdown", ""]; + const labelNames = await getLabelsText(page); + assert.deepStrictEqual(labelNames, expectedLabels); }); it("Should show error messages, based on optional flag", async function () { @@ -740,7 +745,9 @@ describe("SuperTokens Third Party Email Password", function () { describe("Third Party signup default value for fields test", function () { beforeEach(async function () { // set cookie and reload which loads the form fields with default values - await page.evaluate(() => window.localStorage.setItem("SHOW_CUSTOM_FIELDS_WITH_DEFAULT_VALUES", "YES")); + await page.evaluate(() => + window.localStorage.setItem("SIGNUP_SETTING_TYPE", "CUSTOM_FIELDS_WITH_DEFAULT_VALUES") + ); await page.reload({ waitUntil: "domcontentloaded", @@ -862,7 +869,7 @@ describe("SuperTokens Third Party Email Password", function () { describe("Third Party signup config Incorrect field message test", function () { beforeEach(async function () { // set cookie and reload which loads the form fields with default values - await page.evaluate(() => window.localStorage.setItem("SHOW_INCORRECT_FIELDS", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_FIELDS")); await page.reload({ waitUntil: "domcontentloaded", @@ -870,7 +877,7 @@ describe("SuperTokens Third Party Email Password", function () { }); it("Check if incorrect getDefaultValue throws error", async function () { - await page.evaluate(() => window.localStorage.setItem("INCORRECT_GETDEFAULT", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_GETDEFAULT")); let pageErrorMessage = ""; page.on("pageerror", (err) => { pageErrorMessage = err.message; @@ -889,8 +896,7 @@ describe("SuperTokens Third Party Email Password", function () { }); it("Check if non-string params to onChange throws error", async function () { - await page.evaluate(() => window.localStorage.removeItem("INCORRECT_GETDEFAULT")); - await page.evaluate(() => window.localStorage.setItem("INCORRECT_ONCHANGE", "YES")); + await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_ONCHANGE")); await page.reload({ waitUntil: "domcontentloaded", }); @@ -919,9 +925,9 @@ describe("SuperTokens Third Party Email Password", function () { pageErrorMessage = err.message; }); - await page.evaluate(() => window.localStorage.removeItem("INCORRECT_GETDEFAULT")); - await page.evaluate(() => window.localStorage.removeItem("INCORRECT_ONCHANGE")); - await page.evaluate(() => window.localStorage.setItem("INCORRECT_NON_OPTIONAL_ERROR_MSG", "YES")); + await page.evaluate(() => + window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_NON_OPTIONAL_ERROR_MSG") + ); await page.reload({ waitUntil: "domcontentloaded", }); From ab1f839a966b518ae921cfb9b8d6000543bb5a98 Mon Sep 17 00:00:00 2001 From: amitbadala Date: Mon, 20 Nov 2023 15:45:02 +0530 Subject: [PATCH 09/10] Use page.select for changing dropdown values --- test/helpers.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/helpers.js b/test/helpers.js index 78a93b800..b24ff29ed 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -418,14 +418,8 @@ export async function setInputValues(page, fields) { } export async function setSelectDropdownValue(page, selector, optionValue) { - return await page.evaluate( - ({ selector, ST_ROOT_SELECTOR, optionValue }) => { - const select = document.querySelector(ST_ROOT_SELECTOR).shadowRoot.querySelector(selector); - select.value = optionValue; - select.dispatchEvent(new Event("change", { bubbles: true })); - }, - { selector, ST_ROOT_SELECTOR, optionValue } - ); + const dropdownEle = await waitForSTElement(page, selector); + return dropdownEle.select(selector, optionValue); } export async function clearBrowserCookiesWithoutAffectingConsole(page, console) { From 79b91a87f3cb8b715720ea80acecdbff1735dde0 Mon Sep 17 00:00:00 2001 From: Amit Badala Date: Mon, 20 Nov 2023 23:44:59 +0530 Subject: [PATCH 10/10] Handle react 16 tests for new features (#764) --- test/end-to-end/signin.test.js | 15 ++++++++++ test/end-to-end/signup.test.js | 21 ++++++++++++++ .../thirdpartyemailpassword.test.js | 29 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/test/end-to-end/signin.test.js b/test/end-to-end/signin.test.js index 49f20b9a9..bcbb7cff1 100644 --- a/test/end-to-end/signin.test.js +++ b/test/end-to-end/signin.test.js @@ -54,6 +54,7 @@ import { waitForText, backendBeforeEach, getInputField, + isReact16, } from "../helpers"; import fetch from "isomorphic-fetch"; import { SOMETHING_WENT_WRONG_ERROR } from "../constants"; @@ -672,6 +673,13 @@ describe("SuperTokens SignIn", function () { }); describe("Default fields", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + it("Should contain email and password fields prefilled", async function () { await page.evaluate(() => window.localStorage.setItem("SIGNIN_SETTING_TYPE", "DEFAULT_FIELDS")); @@ -751,6 +759,13 @@ describe("SuperTokens SignIn", function () { }); describe("nonOptionalErrorMsg", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + it("Should be displayed on a blank form submit", async function () { // set cookie and reload which loads the form with custom field await page.evaluate(() => diff --git a/test/end-to-end/signup.test.js b/test/end-to-end/signup.test.js index 81b5b4bd8..c747339ca 100644 --- a/test/end-to-end/signup.test.js +++ b/test/end-to-end/signup.test.js @@ -41,6 +41,7 @@ import { backendBeforeEach, setSelectDropdownValue, getInputField, + isReact16, } from "../helpers"; import { @@ -346,6 +347,12 @@ describe("SuperTokens SignUp", function () { }); describe("Custom fields tests", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); beforeEach(async function () { // set cookie and reload which loads the form with custom field await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "CUSTOM_FIELDS")); @@ -568,6 +575,13 @@ describe("SuperTokens SignUp", function () { // Default values test describe("Default fields tests", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + beforeEach(async function () { // set cookie and reload which loads the form fields with default values await page.evaluate(() => @@ -684,6 +698,13 @@ describe("SuperTokens SignUp", function () { }); describe("Incorrect field config test", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + beforeEach(async function () { // set cookie and reload which loads the form fields with default values await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_FIELDS")); diff --git a/test/end-to-end/thirdpartyemailpassword.test.js b/test/end-to-end/thirdpartyemailpassword.test.js index fddbc41eb..d7a29ddea 100644 --- a/test/end-to-end/thirdpartyemailpassword.test.js +++ b/test/end-to-end/thirdpartyemailpassword.test.js @@ -47,6 +47,7 @@ import { setSelectDropdownValue, getInputField, getLabelsText, + isReact16, } from "../helpers"; import { TEST_CLIENT_BASE_URL, @@ -445,6 +446,13 @@ describe("SuperTokens Third Party Email Password", function () { }); describe("SignIn default field tests", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + it("Should contain email and password fields prefilled", async function () { await page.evaluate(() => window.localStorage.setItem("SIGNIN_SETTING_TYPE", "DEFAULT_FIELDS")); @@ -510,6 +518,13 @@ describe("SuperTokens Third Party Email Password", function () { }); describe("Third Party signup config supports custom fields tests", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + beforeEach(async function () { await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "CUSTOM_FIELDS")); @@ -743,6 +758,13 @@ describe("SuperTokens Third Party Email Password", function () { // Default values test describe("Third Party signup default value for fields test", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + beforeEach(async function () { // set cookie and reload which loads the form fields with default values await page.evaluate(() => @@ -867,6 +889,13 @@ describe("SuperTokens Third Party Email Password", function () { }); describe("Third Party signup config Incorrect field message test", function () { + before(function () { + const isReact16App = isReact16(); + if (isReact16App) { + this.skip(); + } + }); + beforeEach(async function () { // set cookie and reload which loads the form fields with default values await page.evaluate(() => window.localStorage.setItem("SIGNUP_SETTING_TYPE", "INCORRECT_FIELDS"));