From 0cf8caa53b6e45c234a39b2e846dcc48b2cd0717 Mon Sep 17 00:00:00 2001 From: Nima Shkouhfar Date: Sat, 19 Oct 2024 17:24:29 -0400 Subject: [PATCH 1/2] storybooks second release --- .../pages/LoginIdpLinkConfirm.stories.tsx | 39 ++++- .../login/pages/LoginIdpLinkEmail.stories.tsx | 65 ++++++-- ...oginOauth2DeviceVerifyUserCode.stories.tsx | 44 ++++++ .../login/pages/LoginOauthGrant.stories.tsx | 70 ++++++++- stories/login/pages/LoginOtp.stories.tsx | 110 ++++++++++++++ .../login/pages/LoginPageExpired.stories.tsx | 23 +++ stories/login/pages/LoginPassword.stories.tsx | 51 +++++++ .../LoginRecoveryAuthnCodeConfig.stories.tsx | 22 +++ stories/login/pages/LoginResetOtp.stories.tsx | 79 ++++++++++ .../pages/LoginResetPassword.stories.tsx | 30 ++++ .../pages/LoginUpdatePassword.stories.tsx | 46 ++++++ .../pages/LoginUpdateProfile.stories.tsx | 23 +++ .../login/pages/LoginVerifyEmail.stories.tsx | 75 ++++++++++ stories/login/pages/LoginX509Info.stories.tsx | 25 ++++ stories/login/pages/LogoutConfirm.stories.tsx | 29 ++++ .../pages/SelectAuthenticator.stories.tsx | 21 +++ stories/login/pages/UpdateEmail.stories.tsx | 22 +++ .../pages/WebauthnAuthenticate.stories.tsx | 141 ++++++++++++++++++ stories/login/pages/WebauthnError.stories.tsx | 69 +++++++++ .../login/pages/WebauthnRegister.stories.tsx | 44 ++++++ 20 files changed, 1015 insertions(+), 13 deletions(-) diff --git a/stories/login/pages/LoginIdpLinkConfirm.stories.tsx b/stories/login/pages/LoginIdpLinkConfirm.stories.tsx index 6642bf2b..ca7ca2ac 100644 --- a/stories/login/pages/LoginIdpLinkConfirm.stories.tsx +++ b/stories/login/pages/LoginIdpLinkConfirm.stories.tsx @@ -2,6 +2,14 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { createKcPageStory } from "../KcPageStory"; +// Mock kcContext to avoid the TS2304 error +const mockKcContext = { + url: { + loginAction: "/login-action" + }, + idpAlias: "mockIdpAlias" +}; + const { KcPageStory } = createKcPageStory({ pageId: "login-idp-link-confirm.ftl" }); const meta = { @@ -13,6 +21,35 @@ export default meta; type Story = StoryObj; +/** + * Default: + * - Purpose: Tests standard behavior with mock data. + * - Scenario: The component renders with a mocked identity provider alias (`mockIdpAlias`) and a login action URL (`/login-action`). + * - Key Aspect: Ensures the default behavior of the component with standard values for kcContext. + */ export const Default: Story = { - render: () => + render: () => +}; + +/** + * WithFormSubmissionError: + * - Purpose: Tests how the component handles form submission errors. + * - Scenario: Simulates a form submission error by setting the login action URL to `/error` and displays an error message. + * - Key Aspect: Verifies that the component can display error messages during form submission failure, ensuring proper error handling. + */ +export const WithFormSubmissionError: Story = { + render: () => ( + + ) }; diff --git a/stories/login/pages/LoginIdpLinkEmail.stories.tsx b/stories/login/pages/LoginIdpLinkEmail.stories.tsx index e855eed5..014829b9 100644 --- a/stories/login/pages/LoginIdpLinkEmail.stories.tsx +++ b/stories/login/pages/LoginIdpLinkEmail.stories.tsx @@ -2,6 +2,20 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { createKcPageStory } from "../KcPageStory"; +// Mock kcContext to avoid TS2304 error and to simulate the real environment +const mockKcContext = { + url: { + loginAction: "/login-action" + }, + idpAlias: "mockIdpAlias", + brokerContext: { + username: "mockUser" + }, + realm: { + displayName: "MockRealm" + } +}; + const { KcPageStory } = createKcPageStory({ pageId: "login-idp-link-email.ftl" }); const meta = { @@ -13,13 +27,27 @@ export default meta; type Story = StoryObj; +/** + * Default: + * - Purpose: Tests the default behavior with mock data. + * - Scenario: The component renders with a mocked identity provider alias (`mockIdpAlias`), a default broker username (`mockUser`), and a default realm name (`MockRealm`). + * - Key Aspect: Ensures the default behavior of the component with typical kcContext values. + */ export const Default: Story = { - render: () => + render: () => }; + +/** + * WithIdpAlias: + * - Purpose: Tests behavior when the idpAlias is set to "Google". + * - Scenario: Simulates the component being used with a Google identity provider, showing the username "john.doe" and realm "MyRealm". + * - Key Aspect: Ensures the correct identity provider alias ("Google") and broker context (user info) are displayed in the email linking instructions. + */ export const WithIdpAlias: Story = { render: () => ( ) }; -export const WithoutIdpAlias: Story = { + +/** + * WithCustomRealmDisplayName: + * - Purpose: Tests behavior when the realm display name is customized. + * - Scenario: Simulates the component with a Facebook identity provider, a broker username "jane.doe", and a custom realm name "CustomRealm". + * - Key Aspect: Ensures that custom realm display names are rendered correctly alongside the idpAlias and broker context. + */ +export const WithCustomRealmDisplayName: Story = { render: () => ( ) }; -export const WithCustomRealmDisplayName: Story = { +/** + * WithFormSubmissionError: + * - Purpose: Tests how the component handles form submission errors. + * - Scenario: Simulates a form submission error by setting the login action URL to `/error` and displays an error message. + * - Key Aspect: Verifies that the component can display error messages during form submission failure, ensuring proper error handling. + */ +export const WithFormSubmissionError: Story = { render: () => ( diff --git a/stories/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx b/stories/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx index 0999ba0f..7c004142 100644 --- a/stories/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx +++ b/stories/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx @@ -16,3 +16,47 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithErrorMessage: + * - Purpose: Tests when there is an error with the OAuth2 device user code entry. + * - Scenario: The component renders with an error message displayed to the user. + * - Key Aspect: Ensures the error message is properly shown when the user enters an invalid code. + */ +export const WithErrorMessage: Story = { + render: () => ( + + ) +}; + +/** + * WithEmptyInputField: + * - Purpose: Tests when the user code field is left empty. + * - Scenario: The component renders the form, and the user tries to submit without entering any code. + * - Key Aspect: Ensures the form displays validation errors when the field is left empty. + */ +export const WithEmptyInputField: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/LoginOauthGrant.stories.tsx b/stories/login/pages/LoginOauthGrant.stories.tsx index 53569903..5aa69b11 100644 --- a/stories/login/pages/LoginOauthGrant.stories.tsx +++ b/stories/login/pages/LoginOauthGrant.stories.tsx @@ -2,6 +2,25 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { createKcPageStory } from "../KcPageStory"; +// Mock kcContext to simulate real environment +const mockKcContext = { + url: { + oauthAction: "/oauth-action" + }, + oauth: { + clientScopesRequested: [{ consentScreenText: "Scope1", dynamicScopeParameter: "dynamicScope1" }, { consentScreenText: "Scope2" }], + code: "mockCode" + }, + client: { + attributes: { + policyUri: "https://twitter.com/en/tos", + tosUri: "https://twitter.com/en/privacy" + }, + name: "Twitter", + clientId: "twitter-client-id" + } +}; + const { KcPageStory } = createKcPageStory({ pageId: "login-oauth-grant.ftl" }); const meta = { @@ -13,6 +32,55 @@ export default meta; type Story = StoryObj; +/** + * Default: + * - Purpose: Tests the default behavior with meaningful logo (Twitter). + * - Scenario: The component renders with Twitter as the client, displaying its logo, policy, and terms of service links. + * - Key Aspect: Ensures the component works with a realistic `logoUri` and client name. + */ export const Default: Story = { - render: () => + render: () => +}; + +/** + * WithoutScopes: + * - Purpose: Tests the component when no OAuth scopes are requested. + * - Scenario: The component renders with no scopes listed under the consent screen. + * - Key Aspect: Ensures the component renders correctly when there are no requested scopes. + */ +export const WithoutScopes: Story = { + render: () => ( + + ) +}; + +/** + * WithFormSubmissionError: + * - Purpose: Tests how the component handles form submission errors. + * - Scenario: The `oauthAction` URL is set to an error route and an error message is displayed. + * - Key Aspect: Ensures that the component can display error messages when form submission fails. + */ +export const WithFormSubmissionError: Story = { + render: () => ( + + ) }; diff --git a/stories/login/pages/LoginOtp.stories.tsx b/stories/login/pages/LoginOtp.stories.tsx index 84b1061a..a02f4712 100644 --- a/stories/login/pages/LoginOtp.stories.tsx +++ b/stories/login/pages/LoginOtp.stories.tsx @@ -16,3 +16,113 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * MultipleOtpCredentials: + * - Purpose: Tests the behavior when the user has multiple OTP credentials to choose from. + * - Scenario: Simulates the scenario where the user is presented with multiple OTP credentials and must select one to proceed. + * - Key Aspect: Ensures that multiple OTP credentials are listed and selectable, and the correct credential is selected by default. + */ +export const MultipleOtpCredentials: Story = { + render: () => ( + false + } + }} + /> + ) +}; + +/** + * WithOtpError: + * - Purpose: Tests the behavior when an error occurs with the OTP field (e.g., invalid OTP code). + * - Scenario: Simulates an invalid OTP code scenario where an error message is displayed. + * - Key Aspect: Ensures that the OTP input displays error messages correctly and the error is visible. + */ +export const WithOtpError: Story = { + render: () => ( + field === "totp", + get: () => "Invalid OTP code" + } + }} + /> + ) +}; + +/** + * NoOtpCredentials: + * - Purpose: Tests the behavior when no OTP credentials are provided for the user. + * - Scenario: Simulates the scenario where the user is not presented with any OTP credentials, and only the OTP input is displayed. + * - Key Aspect: Ensures that the component handles cases where there are no user OTP credentials, and the user is only prompted for the OTP code. + */ +export const NoOtpCredentials: Story = { + render: () => ( + false + } + }} + /> + ) +}; + +/** + * WithErrorAndMultipleOtpCredentials: + * - Purpose: Tests behavior when there is both an error in the OTP field and multiple OTP credentials. + * - Scenario: Simulates the case where the user has multiple OTP credentials and encounters an error with the OTP input. + * - Key Aspect: Ensures that the component can handle both multiple OTP credentials and display an error message simultaneously. + */ +export const WithErrorAndMultipleOtpCredentials: Story = { + render: () => ( + field === "totp", + get: () => "Invalid OTP code" + } + }} + /> + ) +}; diff --git a/stories/login/pages/LoginPageExpired.stories.tsx b/stories/login/pages/LoginPageExpired.stories.tsx index 22e43072..911053bc 100644 --- a/stories/login/pages/LoginPageExpired.stories.tsx +++ b/stories/login/pages/LoginPageExpired.stories.tsx @@ -16,3 +16,26 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithErrorMessage: + * - Purpose: Tests behavior when an error message is displayed along with the page expiration message. + * - Scenario: Simulates a case where the session expired due to an error, and an error message is displayed alongside the expiration message. + * - Key Aspect: Ensures that error messages are displayed correctly in addition to the page expiration notice. + */ +export const WithErrorMessage: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/LoginPassword.stories.tsx b/stories/login/pages/LoginPassword.stories.tsx index 247ec786..bdff68c7 100644 --- a/stories/login/pages/LoginPassword.stories.tsx +++ b/stories/login/pages/LoginPassword.stories.tsx @@ -16,3 +16,54 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithPasswordError: + * - Purpose: Tests the behavior when an error occurs in the password field (e.g., incorrect password). + * - Scenario: Simulates a scenario where an invalid password is entered, and an error message is displayed. + * - Key Aspect: Ensures that the password input field displays error messages correctly. + */ +export const WithPasswordError: Story = { + render: () => ( + field === "password", + get: () => "Invalid password" + } + }} + /> + ) +}; + +/** + * WithoutResetPasswordOption: + * - Purpose: Tests the behavior when the reset password option is disabled. + * - Scenario: Simulates a scenario where the `resetPasswordAllowed` is set to `false`, and the "Forgot Password" link is not rendered. + * - Key Aspect: Ensures that the component handles cases where resetting the password is not allowed. + */ +export const WithoutResetPasswordOption: Story = { + render: () => ( + false + } + }} + /> + ) +}; diff --git a/stories/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx b/stories/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx index 26c181fa..7230cfb4 100644 --- a/stories/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx +++ b/stories/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx @@ -16,3 +16,25 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithErrorDuringCodeGeneration: + * - Purpose: Tests when an error occurs while generating recovery authentication codes. + * - Scenario: The component renders an error message to inform the user of the failure during code generation. + * - Key Aspect: Ensures that error messages are properly displayed when recovery code generation fails. + */ +export const WithErrorDuringCodeGeneration: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/LoginResetOtp.stories.tsx b/stories/login/pages/LoginResetOtp.stories.tsx index 59db7637..c91ec53c 100644 --- a/stories/login/pages/LoginResetOtp.stories.tsx +++ b/stories/login/pages/LoginResetOtp.stories.tsx @@ -16,3 +16,82 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithoutOtpCredentials: + * - Purpose: Tests the behavior when no OTP credentials are available. + * - Scenario: The component renders without any OTP credentials, showing only the submit button. + * - Key Aspect: Ensures that the component handles the absence of OTP credentials correctly. + */ +export const WithoutOtpCredentials: Story = { + render: () => ( + false + } + }} + /> + ) +}; + +/** + * WithOtpError: + * - Purpose: Tests the behavior when an error occurs with the OTP selection. + * - Scenario: Simulates a scenario where an error occurs (e.g., no OTP selected), and an error message is displayed. + * - Key Aspect: Ensures that error messages are displayed correctly for OTP-related errors. + */ +export const WithOtpError: Story = { + render: () => ( + field === "totp", + get: () => "Invalid OTP selection" + } + }} + /> + ) +}; + +/** + * WithOnlyOneOtpCredential: + * - Purpose: Tests the behavior when there is only one OTP credential available. + * - Scenario: Simulates the case where the user has only one OTP credential, and it is pre-selected by default. + * - Key Aspect: Ensures that the component renders correctly with only one OTP credential pre-selected. + */ +export const WithOnlyOneOtpCredential: Story = { + render: () => ( + false + } + }} + /> + ) +}; diff --git a/stories/login/pages/LoginResetPassword.stories.tsx b/stories/login/pages/LoginResetPassword.stories.tsx index 12412f31..598b2602 100644 --- a/stories/login/pages/LoginResetPassword.stories.tsx +++ b/stories/login/pages/LoginResetPassword.stories.tsx @@ -29,3 +29,33 @@ export const WithEmailAsUsername: Story = { /> ) }; +/** + * WithUsernameError: + * - Purpose: Tests behavior when an error occurs with the username input (e.g., invalid username). + * - Scenario: The component displays an error message next to the username input field. + * - Key Aspect: Ensures the username input shows error messages when validation fails. + */ +export const WithUsernameError: Story = { + render: () => ( + field === "username", + get: () => "Invalid username" + }, + auth: { + attemptedUsername: "invalid_user" + } + }} + /> + ) +}; diff --git a/stories/login/pages/LoginUpdatePassword.stories.tsx b/stories/login/pages/LoginUpdatePassword.stories.tsx index 904e3983..3e84a4a4 100644 --- a/stories/login/pages/LoginUpdatePassword.stories.tsx +++ b/stories/login/pages/LoginUpdatePassword.stories.tsx @@ -16,3 +16,49 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithPasswordError: + * - Purpose: Tests when there is an error in the password input (e.g., invalid password). + * - Scenario: Simulates the case where the user enters an invalid password, and an error message is displayed. + * - Key Aspect: Ensures the password input field shows an error message when validation fails. + */ +export const WithPasswordError: Story = { + render: () => ( + field === "password", + get: () => "Password must be at least 8 characters long." + }, + isAppInitiatedAction: false + }} + /> + ) +}; + +/** + * WithPasswordConfirmError: + * - Purpose: Tests when there is an error in the password confirmation input (e.g., passwords do not match). + * - Scenario: Simulates the case where the user enters mismatching passwords, and an error message is displayed in the confirmation field. + * - Key Aspect: Ensures that the password confirmation field shows an error when passwords do not match. + */ +export const WithPasswordConfirmError: Story = { + render: () => ( + field === "password-confirm", + get: () => "Passwords do not match." + }, + isAppInitiatedAction: false + }} + /> + ) +}; diff --git a/stories/login/pages/LoginUpdateProfile.stories.tsx b/stories/login/pages/LoginUpdateProfile.stories.tsx index bb5a6e04..e6fdeab8 100644 --- a/stories/login/pages/LoginUpdateProfile.stories.tsx +++ b/stories/login/pages/LoginUpdateProfile.stories.tsx @@ -16,3 +16,26 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithProfileError: + * - Purpose: Tests when an error occurs in one or more profile fields (e.g., invalid email format). + * - Scenario: The component displays error messages next to the affected fields. + * - Key Aspect: Ensures the profile fields show error messages when validation fails. + */ +export const WithProfileError: Story = { + render: () => ( + field === "email", + get: () => "Invalid email format" + }, + isAppInitiatedAction: false + }} + /> + ) +}; diff --git a/stories/login/pages/LoginVerifyEmail.stories.tsx b/stories/login/pages/LoginVerifyEmail.stories.tsx index 6da554d8..cc5b3d25 100644 --- a/stories/login/pages/LoginVerifyEmail.stories.tsx +++ b/stories/login/pages/LoginVerifyEmail.stories.tsx @@ -28,3 +28,78 @@ export const Default: Story = { /> ) }; + +/** + * WithSuccessMessage: + * - Purpose: Tests when the email verification is successful, and the user receives a confirmation message. + * - Scenario: The component renders a success message instead of a warning or error. + * - Key Aspect: Ensures the success message is displayed correctly when the email is successfully verified. + */ +export const WithSuccessMessage: Story = { + render: () => ( + + ) +}; + +/** + * WithErrorMessage: + * - Purpose: Tests when there is an error during the email verification process. + * - Scenario: The component renders an error message indicating the email verification failed. + * - Key Aspect: Ensures the error message is shown correctly when the verification process encounters an issue. + */ +export const WithErrorMessage: Story = { + render: () => ( + + ) +}; + +/** + * WithInfoMessage: + * - Purpose: Tests when the user is prompted to verify their email without any urgency. + * - Scenario: The component renders with an informational message for email verification. + * - Key Aspect: Ensures the informational message is displayed properly. + */ +export const WithInfoMessage: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/LoginX509Info.stories.tsx b/stories/login/pages/LoginX509Info.stories.tsx index f3c6be75..b0203837 100644 --- a/stories/login/pages/LoginX509Info.stories.tsx +++ b/stories/login/pages/LoginX509Info.stories.tsx @@ -16,3 +16,28 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithoutUserEnabled: + * - Purpose: Tests when the user is not enabled to log in via x509. + * - Scenario: The component renders the certificate details but does not provide the option to log in or cancel. + * - Key Aspect: Ensures that the login buttons are not displayed when the user is not enabled. + */ +export const WithoutUserEnabled: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/LogoutConfirm.stories.tsx b/stories/login/pages/LogoutConfirm.stories.tsx index 20ae5c94..014e1296 100644 --- a/stories/login/pages/LogoutConfirm.stories.tsx +++ b/stories/login/pages/LogoutConfirm.stories.tsx @@ -16,3 +16,32 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithCustomLogoutMessage: + * - Purpose: Tests when a custom message is displayed for the logout confirmation. + * - Scenario: The component renders with a custom logout confirmation message instead of the default one. + * - Key Aspect: Ensures the custom logout message is displayed correctly. + */ +export const WithCustomLogoutMessage: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/SelectAuthenticator.stories.tsx b/stories/login/pages/SelectAuthenticator.stories.tsx index f5ddcf11..333050ee 100644 --- a/stories/login/pages/SelectAuthenticator.stories.tsx +++ b/stories/login/pages/SelectAuthenticator.stories.tsx @@ -82,3 +82,24 @@ export const WithRealmTranslations: Story = { /> ) }; + +/** + * WithoutAuthenticationSelections: + * - Purpose: Tests when no authentication methods are available for selection. + * - Scenario: The component renders without any authentication options, providing a default message or fallback. + * - Key Aspect: Ensures that the component gracefully handles the absence of available authentication methods. + */ +export const WithoutAuthenticationSelections: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/UpdateEmail.stories.tsx b/stories/login/pages/UpdateEmail.stories.tsx index bcb56556..7881eaf1 100644 --- a/stories/login/pages/UpdateEmail.stories.tsx +++ b/stories/login/pages/UpdateEmail.stories.tsx @@ -16,3 +16,25 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithAppInitiatedAction: + * - Purpose: Tests when the form is displayed as part of an application-initiated action. + * - Scenario: The component renders the form with additional buttons like "Cancel." + * - Key Aspect: Ensures the "Cancel" button is visible and functional during app-initiated actions. + */ +export const WithAppInitiatedAction: Story = { + render: () => ( + false + }, + isAppInitiatedAction: true + }} + /> + ) +}; diff --git a/stories/login/pages/WebauthnAuthenticate.stories.tsx b/stories/login/pages/WebauthnAuthenticate.stories.tsx index fc68e97b..111e9086 100644 --- a/stories/login/pages/WebauthnAuthenticate.stories.tsx +++ b/stories/login/pages/WebauthnAuthenticate.stories.tsx @@ -16,3 +16,144 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithMultipleAuthenticators: + * - Purpose: Tests when multiple WebAuthn authenticators are available for selection. + * - Scenario: The component renders multiple authenticators, allowing the user to choose between them. + * - Key Aspect: Ensures that the available authenticators are displayed, and the user can select one. + */ +export const WithMultipleAuthenticators: Story = { + render: () => ( + + ) +}; + +/** + * WithSingleAuthenticator: + * - Purpose: Tests when only one WebAuthn authenticator is available. + * - Scenario: The component renders the WebAuthn form with a single available authenticator. + * - Key Aspect: Ensures the form renders correctly when there is only one authenticator available. + */ +export const WithSingleAuthenticator: Story = { + render: () => ( + + ) +}; + +/** + * WithErrorDuringAuthentication: + * - Purpose: Tests the behavior when an error occurs during WebAuthn authentication. + * - Scenario: The component renders with an error message displayed to the user. + * - Key Aspect: Ensures the form handles authentication errors and displays a relevant message. + */ +export const WithErrorDuringAuthentication: Story = { + render: () => ( + + ) +}; + +/** + * WithJavaScriptDisabled: + * - Purpose: Tests the behavior when JavaScript is disabled or not functioning. + * - Scenario: The component renders a fallback message prompting the user to enable JavaScript for WebAuthn authentication. + * - Key Aspect: Ensures the form provides a clear message when JavaScript is required but unavailable. + */ +export const WithJavaScriptDisabled: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/WebauthnError.stories.tsx b/stories/login/pages/WebauthnError.stories.tsx index db76221b..8543eb78 100644 --- a/stories/login/pages/WebauthnError.stories.tsx +++ b/stories/login/pages/WebauthnError.stories.tsx @@ -16,3 +16,72 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithRetryAvailable: + * - Purpose: Tests when the user can retry the WebAuthn authentication after an error. + * - Scenario: The component renders with a "Try Again" button to allow retrying the authentication process. + * - Key Aspect: Ensures the retry button functionality is visible and the user can retry authentication. + */ +export const WithRetryAvailable: Story = { + render: () => ( + + ) +}; + +/** + * WithAppInitiatedAction: + * - Purpose: Tests when the WebAuthn error form is part of an application-initiated action. + * - Scenario: The component renders with both a "Try Again" button and a "Cancel" button for app-initiated actions. + * - Key Aspect: Ensures the form renders correctly with both "Try Again" and "Cancel" options. + */ +export const WithAppInitiatedAction: Story = { + render: () => ( + + ) +}; + +/** + * WithJavaScriptDisabled: + * - Purpose: Tests the behavior when JavaScript is disabled or not functioning. + * - Scenario: The component renders with a message prompting the user to retry manually without JavaScript. + * - Key Aspect: Ensures the retry mechanism works properly when JavaScript is disabled or unavailable. + */ +export const WithJavaScriptDisabled: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/WebauthnRegister.stories.tsx b/stories/login/pages/WebauthnRegister.stories.tsx index ff8b70bb..c5fcbcbc 100644 --- a/stories/login/pages/WebauthnRegister.stories.tsx +++ b/stories/login/pages/WebauthnRegister.stories.tsx @@ -16,3 +16,47 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * WithRetryAvailable: + * - Purpose: Tests when the user is allowed to retry WebAuthn registration after a failure. + * - Scenario: The component renders the form with a retry option. + * - Key Aspect: Ensures the retry functionality is available and the form allows the user to retry. + */ +export const WithRetryAvailable: Story = { + render: () => ( + + ) +}; + +/** + * WithErrorDuringRegistration: + * - Purpose: Tests when an error occurs during WebAuthn registration. + * - Scenario: The component displays an error message related to WebAuthn registration failure. + * - Key Aspect: Ensures the error message is displayed correctly, informing the user of the registration failure. + */ +export const WithErrorDuringRegistration: Story = { + render: () => ( + + ) +}; From 3ff01d186d3eda6de5c7033d16d454ed75d05768 Mon Sep 17 00:00:00 2001 From: Nima Shkouhfar Date: Sat, 19 Oct 2024 19:10:32 -0400 Subject: [PATCH 2/2] account page test coverage improved --- stories/account/pages/Account.stories.tsx | 102 ++++++++++++ .../account/pages/Applications.stories.tsx | 148 ++++++++++++++++++ .../pages/FederatedIdentity.stories.tsx | 58 +++++++ stories/account/pages/Log.stories.tsx | 104 ++++++++++++ stories/account/pages/Password.stories.tsx | 76 +++++++++ stories/account/pages/Sessions.stories.tsx | 94 +++++++++++ stories/account/pages/Totp.stories.tsx | 61 ++++++++ 7 files changed, 643 insertions(+) diff --git a/stories/account/pages/Account.stories.tsx b/stories/account/pages/Account.stories.tsx index 04e654d2..4eac998c 100644 --- a/stories/account/pages/Account.stories.tsx +++ b/stories/account/pages/Account.stories.tsx @@ -16,3 +16,105 @@ type Story = StoryObj; export const Default: Story = { render: () => }; + +/** + * UsernameNotEditable: + * - Purpose: Test the scenario where the username field is not editable. + * - Scenario: The component renders, but the username field is disabled. + * - Key Aspect: Ensures that the `editUsernameAllowed` condition is respected and the username field is read-only. + */ +export const UsernameNotEditable: Story = { + render: () => ( + "" + }, + stateChecker: "state-checker" + }} + /> + ) +}; + +/** + * WithValidationErrors: + * - Purpose: Test the form when there are validation errors. + * - Scenario: The component renders with error messages for invalid input in the fields. + * - Key Aspect: Ensures that error messages are properly displayed and the user can correct their inputs. + */ +export const WithValidationErrors: Story = { + render: () => ( + (field === "email" || field === "firstName" ? "has-error" : "") + }, + stateChecker: "state-checker" + }} + /> + ) +}; +/** + * EmailAsUsername: + * - Purpose: Test the form where email is used as the username. + * - Scenario: The component renders without a separate username field, and the email field is treated as the username. + * - Key Aspect: Ensures the form functions correctly when `registrationEmailAsUsername` is enabled. + */ +export const EmailAsUsername: Story = { + render: () => ( + "" + }, + stateChecker: "state-checker" + }} + /> + ) +}; diff --git a/stories/account/pages/Applications.stories.tsx b/stories/account/pages/Applications.stories.tsx index c531c03d..e0b85893 100644 --- a/stories/account/pages/Applications.stories.tsx +++ b/stories/account/pages/Applications.stories.tsx @@ -78,3 +78,151 @@ export const Default: Story = { /> ) }; + +// No Available Roles or Grants Scenario +export const NoAvailableRolesOrGrants: Story = { + render: () => ( + + ) +}; + +// Consent Not Required Scenario +export const ConsentNotRequired: Story = { + render: () => ( + + ) +}; + +// No Roles Available but Consent Required Scenario +export const NoRolesButConsentRequired: Story = { + render: () => ( + + ) +}; + +// Only Resource Roles Available Scenario +export const OnlyResourceRolesAvailable: Story = { + render: () => ( + + ) +}; + +// No Additional Grants Scenario +export const NoAdditionalGrants: Story = { + render: () => ( + + ) +}; diff --git a/stories/account/pages/FederatedIdentity.stories.tsx b/stories/account/pages/FederatedIdentity.stories.tsx index ccb29405..86c556fc 100644 --- a/stories/account/pages/FederatedIdentity.stories.tsx +++ b/stories/account/pages/FederatedIdentity.stories.tsx @@ -36,3 +36,61 @@ export const NotConnected: Story = { /> ) }; + +/** + * RemoveLinkNotPossible: + * - Federated identities are connected, but the user cannot remove them due to restrictions. + */ +export const RemoveLinkNotPossible: Story = { + render: () => ( + + ) +}; + +/** + * AddLinkForUnconnectedIdentity: + * - The user has an identity that is not connected and can add it. + */ +export const AddLinkForUnconnectedIdentity: Story = { + render: () => ( + + ) +}; diff --git a/stories/account/pages/Log.stories.tsx b/stories/account/pages/Log.stories.tsx index 6b9ba1b4..754f19fc 100644 --- a/stories/account/pages/Log.stories.tsx +++ b/stories/account/pages/Log.stories.tsx @@ -355,3 +355,107 @@ export const Default: Story = { /> ) }; +export const LogsMissingDetails: Story = { + render: () => ( + + ) +}; +export const SingleLogEntry: Story = { + render: () => ( + + ) +}; +export const LogsWithLongDetails: Story = { + render: () => ( + + ) +}; +export const EmptyClientField: Story = { + render: () => ( + + ) +}; +export const NoLogsAvailable: Story = { + render: () => ( + + ) +}; diff --git a/stories/account/pages/Password.stories.tsx b/stories/account/pages/Password.stories.tsx index b9632bd9..7cd7efbf 100644 --- a/stories/account/pages/Password.stories.tsx +++ b/stories/account/pages/Password.stories.tsx @@ -26,3 +26,79 @@ export const WithMessage: Story = { /> ) }; +/** + * FirstTimePasswordSetup: + * - Purpose: Tests the page when no password is set (e.g., first login). + * - Scenario: This renders the form without the current password field. + * - Key Aspect: Ensures the page only displays fields for setting a new password. + */ +export const FirstTimePasswordSetup: Story = { + render: () => ( + + ) +}; + +/** + * IncorrectCurrentPassword: + * - Purpose: Simulates validation error when the current password is incorrect. + * - Scenario: This renders the page with an error message indicating the current password is incorrect. + * - Key Aspect: Validates that an error message is correctly displayed for the current password input. + */ +export const IncorrectCurrentPassword: Story = { + render: () => ( + + ) +}; + +/** + * SubmissionSuccessWithRedirect: + * - Purpose: Simulates a successful form submission with a redirect or success message. + * - Scenario: After successfully changing the password, a success message and redirect behavior are triggered. + * - Key Aspect: Verifies the handling of successful submissions. + */ +export const SubmissionSuccessWithRedirect: Story = { + render: () => ( + + ) +}; diff --git a/stories/account/pages/Sessions.stories.tsx b/stories/account/pages/Sessions.stories.tsx index 434622f5..8e86b344 100644 --- a/stories/account/pages/Sessions.stories.tsx +++ b/stories/account/pages/Sessions.stories.tsx @@ -57,3 +57,97 @@ export const WithError: Story = { /> ) }; +/** + * No active sessions scenario: + * - Simulates the scenario where no sessions are active for the user. + */ +export const NoActiveSessions: Story = { + render: () => ( + + ) +}; + +/** + * Single session scenario: + * - Displays only one active session with session details. + */ +export const SingleSession: Story = { + render: () => ( + + ) +}; + +/** + * Multiple clients per session scenario: + * - Displays sessions where each session has multiple associated clients. + */ +export const MultipleClientsSession: Story = { + render: () => ( + + ) +}; + +/** + * Session without client details scenario: + * - Simulates a session where no client information is provided. + */ +export const SessionWithoutClients: Story = { + render: () => ( + + ) +}; diff --git a/stories/account/pages/Totp.stories.tsx b/stories/account/pages/Totp.stories.tsx index 1ad1f027..84e060f9 100644 --- a/stories/account/pages/Totp.stories.tsx +++ b/stories/account/pages/Totp.stories.tsx @@ -180,3 +180,64 @@ export const MoreThanOneTotpProviders: Story = { /> ) }; + +// TOTP Enabled but No Existing OTP Credentials +export const TotpEnabledNoOtpCredentials: Story = { + render: () => ( + + ) +}; + +// Manual Mode TOTP without Scanning +export const ManualModeTotp: Story = { + render: () => ( + + ) +}; + +// Multiple OTP Devices Scenario +export const MultipleOtpDevices: Story = { + render: () => ( + + ) +};