Skip to content

Commit

Permalink
review
Browse files Browse the repository at this point in the history
  • Loading branch information
rav3n11 committed Jan 29, 2025
1 parent 91af8c3 commit 6efaeb7
Show file tree
Hide file tree
Showing 30 changed files with 712 additions and 512 deletions.
3 changes: 2 additions & 1 deletion frontend-new/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const config: StorybookConfig = {
"FIREBASE_AUTH_DOMAIN": btoa("some-domain"),
"BACKEND_URL": btoa("http://foo.bar.com/api"),
"SENSITIVE_PERSONAL_DATA_RSA_ENCRYPTION_KEY": btoa(\`${key}\`),
"SENSITIVE_PERSONAL_DATA_RSA_ENCRYPTION_KEY_ID": btoa("1")
"SENSITIVE_PERSONAL_DATA_RSA_ENCRYPTION_KEY_ID": btoa("1"),
"SENTRY_FRONTEND_DSN": btoa("https://[email protected]/baz")
};
//used for chat components
sessionStorage.setItem("ChatSessionID", "1234")
Expand Down
40 changes: 39 additions & 1 deletion frontend-new/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useEffect } from "react";
import { CssBaseline, ThemeProvider } from "@mui/material";
import { HashRouter } from "react-router-dom";
import { applicationTheme, ThemeMode } from "../src/theme/applicationTheme/applicationTheme";
import * as Sentry from "@sentry/react";
// Load fonts
// The application font are typically loaded in the index.html, index.css or index.tsx file
// The fonts for the storybook are loaded here
Expand All @@ -19,6 +21,7 @@ import "@fontsource/roboto/700.css";
import type { Preview, StoryFn, StoryObj } from "@storybook/react";
import SnackbarProvider from "../src/theme/SnackbarProvider/SnackbarProvider";
import { IsOnlineContext } from "../src/app/isOnlineProvider/IsOnlineProvider";
import { initSentry } from "../src/sentryInit";

const preview: Preview = {
parameters: {
Expand Down Expand Up @@ -58,6 +61,17 @@ const preview: Preview = {
dynamicTitle: true,
},
},
sentryEnabled: {
name: 'Sentry Enabled',
description: 'Enable/disable Sentry error reporting',
toolbar: {
icon: 'alert',
items: [
{value: true, title: 'Enabled'},
{value: false, title: 'Disabled'},
],
},
},
},
args: {
online: true,
Expand All @@ -66,9 +80,33 @@ const preview: Preview = {

export default preview;

// Store the original SENTRY_FRONTEND_DSN value
// @ts-ignore
const ORIGINAL_SENTRY_DSN = window.tabiyaConfig.SENTRY_FRONTEND_DSN;
let isSentryInitialized = true;

export const decorators = [
( Story: StoryFn, context: { globals: { online: any; }; }) => {
( Story: StoryFn, context: { globals: { online: any; sentryEnabled: boolean; }; }) => {
const isOnline = context.globals.online;
const sentryEnabled = context.globals.sentryEnabled;
const prevSentryEnabled = React.useRef(sentryEnabled);

useEffect(() => {
if (prevSentryEnabled.current !== sentryEnabled) {
if (sentryEnabled) {
// @ts-ignore
window.tabiyaConfig.SENTRY_FRONTEND_DSN = ORIGINAL_SENTRY_DSN;
initSentry();
isSentryInitialized = true;
} else {
// @ts-ignore
window.tabiyaConfig.SENTRY_FRONTEND_DSN = undefined;
isSentryInitialized = false;
window.location.reload();
}
prevSentryEnabled.current = sentryEnabled;
}
}, [sentryEnabled]);

return (
<HashRouter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import RegistrationCodeFormModal, {
DATA_TEST_ID,
RegistrationCodeFormModalState,
} from "src/auth/components/registrationCodeFormModal/RegistrationCodeFormModal";
import RequestInvitationCodeFormModal from "src/auth/components/requestInvitationCodeFormModal/RequestInvitationCodeFormModal";
import RequestInvitationCodeFormModal from "src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal";

// Mock InvitationsService
jest.mock("src/auth/services/invitationsService/invitations.service", () => ({
Expand All @@ -31,9 +31,9 @@ jest.mock("src/theme/SnackbarProvider/SnackbarProvider", () => {
});

// mock the RequestInvitationFormModal component
jest.mock("src/auth/components/requestInvitationCodeFormModal/RequestInvitationCodeFormModal", () => {
jest.mock("src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal", () => {
const actual = jest.requireActual(
"src/auth/components/requestInvitationCodeFormModal/RequestInvitationCodeFormModal"
"src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal"
);
return {
...actual,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useCallback, useState } from "react";
import Modal from "@mui/material/Modal";

import { Box, CircularProgress, Link, styled, TextField, Typography, useTheme } from "@mui/material";
import { Box, CircularProgress, TextField, Typography, useTheme } from "@mui/material";
import PrimaryButton from "src/theme/PrimaryButton/PrimaryButton";
import PrimaryIconButton from "src/theme/PrimaryIconButton/PrimaryIconButton";
import CloseIcon from "@mui/icons-material/Close";
import RequestInvitationCodeFormModal from "src/auth/components/requestInvitationCodeFormModal/RequestInvitationCodeFormModal";
import RequestInvitationCodeFormModal from "src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal";
import CustomLink from "src/theme/CustomLink/CustomLink";

export enum RegistrationCodeFormModalState {
SHOW,
Expand Down Expand Up @@ -60,16 +61,6 @@ const style = {
flexDirection: "column",
};

const StyledLink = styled(Link)(({ theme }) => ({
color: theme.palette.text.textAccent,
cursor: "pointer",
fontStyle: "italic",
textDecoration: "none",
"&:hover": {
textDecoration: "underline",
},
}));

const RegistrationCodeFormModal: React.FC<InvitationCodeFormModalProps> = ({ modalState, onSuccess, onClose }) => {
const theme = useTheme();
const [registrationCode, setRegistrationCode] = useState("");
Expand Down Expand Up @@ -150,9 +141,9 @@ const RegistrationCodeFormModal: React.FC<InvitationCodeFormModalProps> = ({ mod
}}
>
Don't have a registration code?{" "}
<StyledLink onClick={handleOpenRequestModal} data-testid={DATA_TEST_ID.REQUEST_REGISTRATION_CODE_LINK}>
<CustomLink onClick={handleOpenRequestModal} data-testid={DATA_TEST_ID.REQUEST_REGISTRATION_CODE_LINK}>
Reach out
</StyledLink>
</CustomLink>
</Typography>
</Box>
</Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ exports[`RegistrationCodeFormModal closes the modal when the close icon is click
Don't have a registration code?
<a
class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-19xsbs9-MuiTypography-root-MuiLink-root"
class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1vjgeqq-MuiTypography-root-MuiLink-root"
data-testid="invitation-code-request-registration-code-link-f782907a-6904-482c-b148-3c6682bf7b54"
>
Reach out
Expand Down Expand Up @@ -248,7 +248,7 @@ exports[`RegistrationCodeFormModal renders correctly when modal is shown and cal
Don't have a registration code?
<a
class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-19xsbs9-MuiTypography-root-MuiLink-root"
class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-1vjgeqq-MuiTypography-root-MuiLink-root"
data-testid="invitation-code-request-registration-code-link-f782907a-6904-482c-b148-3c6682bf7b54"
>
Reach out
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";
import { render, screen } from "src/_test_utilities/test-utils";
import userEvent from "@testing-library/user-event";
import * as Sentry from "@sentry/react";

import RequestInvitationCode, { DATA_TEST_ID as REQUEST_INVITATION_CODE_DATA_TEST_ID } from "./RequestInvitationCode";
import { InvitationType } from "src/auth/services/invitationsService/invitations.types";
import { DATA_TEST_ID as REQUEST_INVITATION_CODE_FORM_MODAL_DATA_TEST_ID } from "src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal";
jest.mock("@sentry/react");
jest.mock("src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal", () => {
const actual = jest.requireActual("src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal");
return {
...actual,
__esModule: true,
default: jest.fn().mockImplementation(() =>
<div data-testid={actual.DATA_TEST_ID.CONTAINER} />
)
};
});

describe("RequestInvitationCode", () => {
test("renders null when Sentry is not initialized", () => {
// GIVEN sentry is not initialized
jest.spyOn(Sentry, "isInitialized").mockReturnValue(false);

// WHEN the component is rendered
render(<RequestInvitationCode invitationCodeType={InvitationType.AUTO_REGISTER} />);

// THEN the link should not render
expect(screen.queryByTestId(REQUEST_INVITATION_CODE_DATA_TEST_ID.REQUEST_INVITATION_CODE_LINK)).not.toBeInTheDocument();
// AND the modal should not render
expect(screen.queryByTestId(REQUEST_INVITATION_CODE_FORM_MODAL_DATA_TEST_ID.CONTAINER)).not.toBeInTheDocument();
});

test("renders the correct text and link when Sentry is initialized", () => {
// GIVEN sentry is initialized
jest.spyOn(Sentry, "isInitialized").mockReturnValue(true);

// WHEN the component is rendered
render(<RequestInvitationCode invitationCodeType={InvitationType.AUTO_REGISTER} />);

// THEN the component should render the correct text and link
expect(screen.getByTestId(`${REQUEST_INVITATION_CODE_DATA_TEST_ID.REQUEST_INVITATION_CODE_LINK}-${InvitationType.AUTO_REGISTER}`)).toBeInTheDocument();
});

test("opens the modal when the link is clicked", async () => {
// GIVEN sentry is initialized
jest.spyOn(Sentry, "isInitialized").mockReturnValue(true);

// WHEN the component is rendered
render(<RequestInvitationCode invitationCodeType={InvitationType.AUTO_REGISTER} />);

// THEN the modal should be opened when the link is clicked
await userEvent.click(screen.getByTestId(`${REQUEST_INVITATION_CODE_DATA_TEST_ID.REQUEST_INVITATION_CODE_LINK}-${InvitationType.AUTO_REGISTER}`));
expect(screen.getByTestId(REQUEST_INVITATION_CODE_FORM_MODAL_DATA_TEST_ID.CONTAINER)).toBeInTheDocument();
});

test("renders the correct text for registration code type", () => {
// GIVEN sentry is initialized
jest.spyOn(Sentry, "isInitialized").mockReturnValue(true);

// WHEN the component is rendered
render(<RequestInvitationCode invitationCodeType={InvitationType.REGISTER} />);

// THEN the component should render the correct text
expect(screen.getByTestId(`${REQUEST_INVITATION_CODE_DATA_TEST_ID.REQUEST_INVITATION_CODE_LINK}-${InvitationType.REGISTER}`)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState, useContext } from 'react'
import Typography from '@mui/material/Typography'
import * as Sentry from '@sentry/react'
import CustomLink from 'src/theme/CustomLink/CustomLink'
import RequestInvitationCodeFormModal from "./requestInvitationCodeFormModal/RequestInvitationCodeFormModal";
import { InvitationType } from "src/auth/services/invitationsService/invitations.types";
import { IsOnlineContext } from "src/app/isOnlineProvider/IsOnlineProvider";


interface Props {
invitationCodeType: InvitationType;
}

const uniqueId = "7ce9ba1f-bde0-48e2-88df-e4f697945cc4";

export const DATA_TEST_ID = {
REQUEST_INVITATION_CODE_LINK: `request-invitation-code-link-${uniqueId}`,
}

const RequestInvitationCode = ({ invitationCodeType }: Props) => {
const [isModalOpen, setIsModalOpen] = useState(false)
const isOnline = useContext(IsOnlineContext);

if (!Sentry.isInitialized()) {
return null
}

const invitationCodeText = invitationCodeType === InvitationType.AUTO_REGISTER ? 'login code' : 'registration code';

return (
<>
<Typography variant="caption" gutterBottom>
Don't have a {invitationCodeText}?{" "}
<CustomLink
disabled={!isOnline}
onClick={() => setIsModalOpen(true)}
data-testid={`${DATA_TEST_ID.REQUEST_INVITATION_CODE_LINK}-${invitationCodeType}`}
>
Reach out
</CustomLink>
</Typography>

<RequestInvitationCodeFormModal open={isModalOpen} onClose={() => setIsModalOpen(false)} />
</>
)
}

export default RequestInvitationCode
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface InvitationCodeRequestData {
name: string;
email: string;
message: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as Sentry from "@sentry/react";
import { requestInvitationCode } from "./SentryInvitationCodeRequest.service";
import { InvitationError } from "../../../../error/commonErrors";

jest.mock("@sentry/react");

describe("requestInvitationCode", () => {
beforeEach(() => {
// GIVEN a fresh mock state
jest.clearAllMocks();
});

it("should send feedback to Sentry with the correct data", async () => {
// GIVEN the request data
const requestData = {
name: "John Doe",
email: "[email protected]",
message: "I want to try Compass",
};

// WHEN requesting an invitation code
await requestInvitationCode(requestData);

// THEN expect Sentry.captureFeedback to be called with the correct data
expect(Sentry.captureFeedback).toHaveBeenCalledWith({
name: requestData.name,
email: requestData.email,
message: `A user has requested an invitation code. Additional information: ${requestData.message}`,
});
});

it("should throw an error if Sentry.captureFeedback fails", async () => {
// GIVEN Sentry will throw an error
const error = new Error("Sentry error");
(Sentry.captureFeedback as jest.Mock).mockImplementation(() => {
throw error;
});

// GIVEN the request data
const requestData = {
name: "John Doe",
email: "[email protected]",
message: "I want to try Compass",
};

// WHEN requesting an invitation code AND it fails
// THEN expect the error to be thrown
expect(() => requestInvitationCode(requestData)).toThrow(new InvitationError(`Something went wrong while attempting to request new invitation for user with email ${requestData.email}`));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as Sentry from "@sentry/react";
import { InvitationCodeRequestData } from "./InvitationCodeRequest.types";
import { InvitationError } from "../../../../error/commonErrors";

export function requestInvitationCode(data: InvitationCodeRequestData) {
// we currently use sentry to capture user feedback
try {
Sentry.captureFeedback({
name: data.name,
email: data.email,
message: `A user has requested an invitation code. Additional information: ${data.message}`,
});
} catch (e) {
throw new InvitationError(`Something went wrong while attempting to request new invitation for user with email ${data.email}`, e as Error);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Meta, StoryObj } from "@storybook/react";
import RequestInvitationCodeFormModal from "src/auth/components/requestInvitationCodeFormModal/RequestInvitationCodeFormModal";
import RequestInvitationCodeFormModal from "src/auth/components/requestInvitationCode/requestInvitationCodeFormModal/RequestInvitationCodeFormModal";

const meta: Meta<typeof RequestInvitationCodeFormModal> = {
title: "Invitations/RequestInvitationCodeFormModal",
Expand Down
Loading

0 comments on commit 6efaeb7

Please sign in to comment.