From 7cced1c7d07996b5d85ab684ce1371e1c470fe26 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 11:28:56 +0100 Subject: [PATCH 01/15] add title, date and content --- .../MessageDetail/MessageDetailHeader.tsx | 48 +++++ .../pn/components/LegacyMessageDetails.tsx | 193 +++++++++++++++++ .../LegacyMessageDetailsContent.tsx | 31 +++ ts/features/pn/components/MessageDetails.tsx | 200 ++---------------- .../pn/components/MessageDetailsContent.tsx | 44 ++-- .../__test__/LegacyMessageDetails.test.tsx | 187 ++++++++++++++++ ts/features/pn/navigation/navigator.tsx | 51 +++-- .../pn/screens/LegacyMessageDetailsScreen.tsx | 140 ++++++++++++ .../pn/screens/MessageDetailsScreen.tsx | 35 +-- ts/features/pn/store/types/transformers.ts | 1 + ts/features/pn/store/types/types.ts | 8 +- 11 files changed, 690 insertions(+), 248 deletions(-) create mode 100644 ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx create mode 100644 ts/features/pn/components/LegacyMessageDetails.tsx create mode 100644 ts/features/pn/components/LegacyMessageDetailsContent.tsx create mode 100644 ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx create mode 100644 ts/features/pn/screens/LegacyMessageDetailsScreen.tsx diff --git a/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx new file mode 100644 index 00000000000..849cf89df7c --- /dev/null +++ b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx @@ -0,0 +1,48 @@ +import { Divider, H3, LabelSmall, VSpacer } from "@pagopa/io-app-design-system"; +import React, { PropsWithChildren } from "react"; +import { StyleSheet, View } from "react-native"; +import { localeDateFormat } from "../../../../utils/locale"; +import I18n from "../../../../i18n"; +import { ServicePublic } from "../../../../../definitions/backend/ServicePublic"; + +export type MessageDetailHeaderProps = PropsWithChildren<{ + createdAt: Date; + subject: string; + sender?: string; + service?: ServicePublic; +}>; + +const styles = StyleSheet.create({ + header: { + paddingHorizontal: 24 + } +}); + +const MessageHeaderContent = ({ + subject, + createdAt +}: MessageDetailHeaderProps) => ( + <> +

{subject}

+ + + {localeDateFormat( + createdAt, + I18n.t("global.dateFormats.fullFormatShortMonthLiteralWithTime") + )} + + +); + +export const MessageDetailHeader = ({ + children, + ...rest +}: MessageDetailHeaderProps) => ( + + {children} + + + + {/* TODO: Add MessageHeaderService */} + +); diff --git a/ts/features/pn/components/LegacyMessageDetails.tsx b/ts/features/pn/components/LegacyMessageDetails.tsx new file mode 100644 index 00000000000..78d59087e5e --- /dev/null +++ b/ts/features/pn/components/LegacyMessageDetails.tsx @@ -0,0 +1,193 @@ +import React, { useCallback, createRef, useRef } from "react"; +import { pipe } from "fp-ts/lib/function"; +import * as O from "fp-ts/lib/Option"; +import * as RA from "fp-ts/lib/ReadonlyArray"; +import * as SEP from "fp-ts/lib/Separated"; +import { View } from "react-native"; +import { ScrollView } from "react-native-gesture-handler"; +import { + IOVisualCostants, + ListItemInfoCopy, + VSpacer +} from "@pagopa/io-app-design-system"; +import { ServicePublic } from "../../../../definitions/backend/ServicePublic"; +import { H5 } from "../../../components/core/typography/H5"; +import I18n from "../../../i18n"; +import { useIOSelector } from "../../../store/hooks"; +import { pnFrontendUrlSelector } from "../../../store/reducers/backendStatus"; +import { UIAttachment, UIMessageId } from "../../messages/types"; +import { clipboardSetStringWithFeedback } from "../../../utils/clipboard"; +import { LegacyMessageAttachments } from "../../messages/components/LegacyMessageAttachments"; +import NavigationService from "../../../navigation/NavigationService"; +import PN_ROUTES from "../navigation/routes"; +import { PNMessage } from "../store/types/types"; +import { NotificationPaymentInfo } from "../../../../definitions/pn/NotificationPaymentInfo"; +import { trackPNAttachmentOpening } from "../analytics"; +import { DSFullWidthComponent } from "../../design-system/components/DSFullWidthComponent"; +import StatusContent from "../../../components/SectionStatus/StatusContent"; +import { + getStatusTextColor, + statusColorMap, + statusIconMap +} from "../../../components/SectionStatus"; +import { LevelEnum } from "../../../../definitions/content/SectionStatus"; +import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory"; +import { maxVisiblePaymentCountGenerator } from "../utils"; +import { LegacyMessageDetailsContent } from "./LegacyMessageDetailsContent"; +import { MessageDetailsHeader } from "./MessageDetailsHeader"; +import { MessageDetailsSection } from "./MessageDetailsSection"; +import { MessageTimeline } from "./MessageTimeline"; +import { MessageTimelineCTA } from "./MessageTimelineCTA"; +import { MessageF24 } from "./MessageF24"; +import { MessagePayments } from "./MessagePayments"; +import { MessageFooter } from "./MessageFooter"; +import { MessagePaymentBottomSheet } from "./MessagePaymentBottomSheet"; + +type Props = Readonly<{ + messageId: UIMessageId; + message: PNMessage; + service: ServicePublic | undefined; + payments: ReadonlyArray | undefined; +}>; + +export const LegacyMessageDetails = ({ + message, + messageId, + service, + payments +}: Props) => { + const viewRef = createRef(); + const presentPaymentsBottomSheetRef = useRef<() => void>(); + const frontendUrl = useIOSelector(pnFrontendUrlSelector); + + const partitionedAttachments = pipe( + message.attachments, + O.fromNullable, + O.getOrElse>(() => []), + RA.partition(attachment => attachment.category === ATTACHMENT_CATEGORY.F24) + ); + + const f24List = SEP.right(partitionedAttachments); + const attachmentList = SEP.left(partitionedAttachments); + + const isCancelled = message.isCancelled ?? false; + const completedPaymentNoticeCodes = isCancelled + ? message.completedPayments + : undefined; + + const openAttachment = useCallback( + (attachment: UIAttachment) => { + trackPNAttachmentOpening(attachment.category); + NavigationService.navigate(PN_ROUTES.MESSAGE_ATTACHMENT, { + messageId, + attachmentId: attachment.id, + category: attachment.category + }); + }, + [messageId] + ); + + const maxVisiblePaymentCount = maxVisiblePaymentCountGenerator(); + const scrollViewRef = React.createRef(); + + return ( + <> + + {service && } + + + {isCancelled && ( + <> + + + + {I18n.t("features.pn.details.cancelledMessage.body")} + + + + )} + + {RA.isNonEmpty(attachmentList) && ( + + + + )} + + + {!isCancelled && RA.isNonEmpty(f24List) ? ( + <> + + + + ) : null} + + + clipboardSetStringWithFeedback(message.iun)} + accessibilityLabel={I18n.t("features.pn.details.infoSection.iun")} + label={I18n.t("features.pn.details.infoSection.iun")} + /> +
+ {I18n.t("features.pn.details.timeline.title")} +
+ + { + scrollViewRef.current?.scrollToEnd({ animated: true }); + }} + /> + {frontendUrl.length > 0 && } +
+
+ + {payments && !isCancelled && ( + + )} + + + + ); +}; diff --git a/ts/features/pn/components/LegacyMessageDetailsContent.tsx b/ts/features/pn/components/LegacyMessageDetailsContent.tsx new file mode 100644 index 00000000000..59cb0f34fbe --- /dev/null +++ b/ts/features/pn/components/LegacyMessageDetailsContent.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { View, ViewProps, StyleSheet } from "react-native"; +import { Body } from "../../../components/core/typography/Body"; +import { H1 } from "../../../components/core/typography/H1"; +import { H2 } from "../../../components/core/typography/H2"; +import { PNMessage } from "../store/types/types"; +import customVariables from "../../../theme/variables"; +import { isStringNullyOrEmpty } from "../../../utils/strings"; + +const styles = StyleSheet.create({ + subject: { + marginTop: customVariables.spacerExtrasmallHeight + }, + abstract: { + marginTop: customVariables.spacerExtrasmallHeight + } +}); + +type Props = Readonly<{ message: PNMessage }> & ViewProps; + +export const LegacyMessageDetailsContent = (props: Props) => ( + + {!isStringNullyOrEmpty(props.message.senderDenomination) && ( +

{props.message.senderDenomination}

+ )} +

{props.message.subject}

+ {!isStringNullyOrEmpty(props.message.abstract) && ( + {props.message.abstract} + )} +
+); diff --git a/ts/features/pn/components/MessageDetails.tsx b/ts/features/pn/components/MessageDetails.tsx index a2370ecabd8..703d01793ac 100644 --- a/ts/features/pn/components/MessageDetails.tsx +++ b/ts/features/pn/components/MessageDetails.tsx @@ -1,193 +1,27 @@ -import React, { useCallback, createRef, useRef } from "react"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as RA from "fp-ts/lib/ReadonlyArray"; -import * as SEP from "fp-ts/lib/Separated"; -import { View } from "react-native"; +import React from "react"; import { ScrollView } from "react-native-gesture-handler"; -import { - IOVisualCostants, - ListItemInfoCopy, - VSpacer -} from "@pagopa/io-app-design-system"; import { ServicePublic } from "../../../../definitions/backend/ServicePublic"; -import { H5 } from "../../../components/core/typography/H5"; -import I18n from "../../../i18n"; -import { useIOSelector } from "../../../store/hooks"; -import { pnFrontendUrlSelector } from "../../../store/reducers/backendStatus"; -import { UIAttachment, UIMessageId } from "../../messages/types"; -import { clipboardSetStringWithFeedback } from "../../../utils/clipboard"; -import { LegacyMessageAttachments } from "../../messages/components/LegacyMessageAttachments"; -import NavigationService from "../../../navigation/NavigationService"; -import PN_ROUTES from "../navigation/routes"; +import { UIMessageId } from "../../messages/types"; import { PNMessage } from "../store/types/types"; import { NotificationPaymentInfo } from "../../../../definitions/pn/NotificationPaymentInfo"; -import { trackPNAttachmentOpening } from "../analytics"; -import { DSFullWidthComponent } from "../../design-system/components/DSFullWidthComponent"; -import StatusContent from "../../../components/SectionStatus/StatusContent"; -import { - getStatusTextColor, - statusColorMap, - statusIconMap -} from "../../../components/SectionStatus"; -import { LevelEnum } from "../../../../definitions/content/SectionStatus"; -import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory"; -import { maxVisiblePaymentCountGenerator } from "../utils"; +import { MessageDetailHeader } from "../../messages/components/MessageDetail/MessageDetailHeader"; import { MessageDetailsContent } from "./MessageDetailsContent"; -import { MessageDetailsHeader } from "./MessageDetailsHeader"; -import { MessageDetailsSection } from "./MessageDetailsSection"; -import { MessageTimeline } from "./MessageTimeline"; -import { MessageTimelineCTA } from "./MessageTimelineCTA"; -import { MessageF24 } from "./MessageF24"; -import { MessagePayments } from "./MessagePayments"; -import { MessageFooter } from "./MessageFooter"; -import { MessagePaymentBottomSheet } from "./MessagePaymentBottomSheet"; -type Props = Readonly<{ - messageId: UIMessageId; +type MessageDetailsProps = { message: PNMessage; + messageId: UIMessageId; service: ServicePublic | undefined; payments: ReadonlyArray | undefined; -}>; - -export const MessageDetails = ({ - message, - messageId, - service, - payments -}: Props) => { - const viewRef = createRef(); - const presentPaymentsBottomSheetRef = useRef<() => void>(); - const frontendUrl = useIOSelector(pnFrontendUrlSelector); - - const partitionedAttachments = pipe( - message.attachments, - O.fromNullable, - O.getOrElse>(() => []), - RA.partition(attachment => attachment.category === ATTACHMENT_CATEGORY.F24) - ); - - const f24List = SEP.right(partitionedAttachments); - const attachmentList = SEP.left(partitionedAttachments); - - const isCancelled = message.isCancelled ?? false; - const completedPaymentNoticeCodes = isCancelled - ? message.completedPayments - : undefined; - - const openAttachment = useCallback( - (attachment: UIAttachment) => { - trackPNAttachmentOpening(attachment.category); - NavigationService.navigate(PN_ROUTES.MESSAGE_ATTACHMENT, { - messageId, - attachmentId: attachment.id, - category: attachment.category - }); - }, - [messageId] - ); - - const maxVisiblePaymentCount = maxVisiblePaymentCountGenerator(); - const scrollViewRef = React.createRef(); - - return ( - <> - - {service && } - - - {isCancelled && ( - <> - - - - {I18n.t("features.pn.details.cancelledMessage.body")} - - - - )} - - {RA.isNonEmpty(attachmentList) && ( - - - - )} - - - {!isCancelled && RA.isNonEmpty(f24List) ? ( - <> - - - - ) : null} - - - clipboardSetStringWithFeedback(message.iun)} - accessibilityLabel={I18n.t("features.pn.details.infoSection.iun")} - label={I18n.t("features.pn.details.infoSection.iun")} - /> -
- {I18n.t("features.pn.details.timeline.title")} -
- - { - scrollViewRef.current?.scrollToEnd({ animated: true }); - }} - /> - {frontendUrl.length > 0 && } -
-
- - {payments && !isCancelled && ( - - )} - - - - ); }; + +export const MessageDetails = ({ message, service }: MessageDetailsProps) => ( + + + + +); diff --git a/ts/features/pn/components/MessageDetailsContent.tsx b/ts/features/pn/components/MessageDetailsContent.tsx index 21b33cfef0c..6bae2e6e2e6 100644 --- a/ts/features/pn/components/MessageDetailsContent.tsx +++ b/ts/features/pn/components/MessageDetailsContent.tsx @@ -1,31 +1,21 @@ import React from "react"; -import { View, ViewProps, StyleSheet } from "react-native"; -import { Body } from "../../../components/core/typography/Body"; -import { H1 } from "../../../components/core/typography/H1"; -import { H2 } from "../../../components/core/typography/H2"; -import { PNMessage } from "../store/types/types"; -import customVariables from "../../../theme/variables"; -import { isStringNullyOrEmpty } from "../../../utils/strings"; +import { Body, ContentWrapper, VSpacer } from "@pagopa/io-app-design-system"; -const styles = StyleSheet.create({ - subject: { - marginTop: customVariables.spacerExtrasmallHeight - }, - abstract: { - marginTop: customVariables.spacerExtrasmallHeight - } -}); +type MessageDetailsContentProps = { + abstract: string | undefined; +}; -type Props = Readonly<{ message: PNMessage }> & ViewProps; +export const MessageDetailsContent = ({ + abstract +}: MessageDetailsContentProps) => { + if (abstract === undefined) { + return null; + } -export const MessageDetailsContent = (props: Props) => ( - - {!isStringNullyOrEmpty(props.message.senderDenomination) && ( -

{props.message.senderDenomination}

- )} -

{props.message.subject}

- {!isStringNullyOrEmpty(props.message.abstract) && ( - {props.message.abstract} - )} -
-); + return ( + + + {abstract} + + ); +}; diff --git a/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx new file mode 100644 index 00000000000..d1e7fb14f64 --- /dev/null +++ b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx @@ -0,0 +1,187 @@ +import React from "react"; +import * as pot from "@pagopa/ts-commons/lib/pot"; +import { act, fireEvent } from "@testing-library/react-native"; +import { createStore } from "redux"; +import { applicationChangeState } from "../../../../store/actions/application"; +import { appReducer } from "../../../../store/reducers"; +import { LegacyMessageDetails } from "../LegacyMessageDetails"; +import { GlobalState } from "../../../../store/reducers/types"; +import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; +import PN_ROUTES from "../../navigation/routes"; +import { UIAttachment, UIMessageId } from "../../../messages/types"; +import { PNMessage } from "../../store/types/types"; +import { Download } from "../../../messages/store/reducers/downloads"; +import { NotificationRecipient } from "../../../../../definitions/pn/NotificationRecipient"; +import { ATTACHMENT_CATEGORY } from "../../../messages/types/attachmentCategory"; + +const mockedOnAttachmentSelect = jest.fn(); + +jest.mock("../../../messages/hooks/useAttachmentDownload", () => ({ + useAttachmentDownload: ( + _attachment: UIAttachment, + _openPreview: (attachment: UIAttachment) => void + ) => ({ + onAttachmentSelect: mockedOnAttachmentSelect, + downloadPot: { kind: "PotNone" } as pot.Pot + }) +})); + +describe("LegacyMessageDetails component", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it("should not show the cancelled banner when the PN message is not cancelled", () => { + const { component } = renderComponent( + generateComponentProperties(generatePnMessage()) + ); + expect(component.queryByTestId("PnCancelledMessageBanner")).toBeFalsy(); + }); + + it("every attachment item should have a full opaque style when the PN message is not cancelled", () => { + const { component } = renderComponent( + generateComponentProperties(generatePnMessage()) + ); + const messageAttachmentComponents = component.queryAllByTestId( + "MessageAttachmentTouchable" + ); + expect(messageAttachmentComponents.length).toBe(2); + + messageAttachmentComponents.forEach(messageAttachmentComponent => { + const opacity = messageAttachmentComponent?.props.style.opacity; + expect(opacity).toBe(1.0); + }); + }); + + it("should show the cancelled banner when the PN message is cancelled", () => { + const { component } = renderComponent( + generateComponentProperties({ + ...generatePnMessage(), + isCancelled: true + }) + ); + expect(component.queryByTestId("PnCancelledMessageBanner")).toBeDefined(); + }); + + it("every attachment item should have a semi-transparent style when the PN message is cancelled", () => { + const { component } = renderComponent( + generateComponentProperties({ + ...generatePnMessage(), + isCancelled: true + }) + ); + const messageAttachmentComponents = component.queryAllByTestId( + "MessageAttachmentTouchable" + ); + expect(messageAttachmentComponents.length).toBe(2); + + messageAttachmentComponents.forEach(messageAttachmentComponent => { + const opacity = messageAttachmentComponent?.props.style.opacity; + expect(opacity).toBe(0.5); + }); + }); + + it("every attachment item should handle input when the PN message not is cancelled", async () => { + const { component } = renderComponent( + generateComponentProperties(generatePnMessage()) + ); + const messageAttachmentComponents = component.queryAllByTestId( + "MessageAttachmentTouchable" + ); + expect(messageAttachmentComponents.length).toBe(2); + await act(() => { + messageAttachmentComponents.forEach(messageAttachmentComponent => + fireEvent.press(messageAttachmentComponent) + ); + }); + expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes( + messageAttachmentComponents.length + ); + }); + + it("every attachment item should not handle input when the PN message is cancelled", async () => { + const { component } = renderComponent( + generateComponentProperties({ ...generatePnMessage(), isCancelled: true }) + ); + const messageAttachmentComponents = component.queryAllByTestId( + "MessageAttachmentTouchable" + ); + expect(messageAttachmentComponents.length).toBe(2); + // eslint-disable-next-line sonarjs/no-identical-functions + await act(() => { + messageAttachmentComponents.forEach(messageAttachmentComponent => + fireEvent.press(messageAttachmentComponent) + ); + }); + expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes(0); + }); + + it("should NOT render the F24 section if there are no multiple F24", () => { + const { component } = renderComponent( + generateComponentProperties(generatePnMessage()) + ); + expect(component.queryByTestId("pn-message-f24-section")).toBeFalsy(); + }); +}); + +const generateTestMessageId = () => "00000000000000000000000004" as UIMessageId; +const generateTestFiscalCode = () => "AAABBB00A00A000A"; +const generatePnMessage = (): PNMessage => ({ + iun: "731143-7-0317-8200-0", + subject: "This is the message subject", + senderDenomination: "Sender denomination", + abstract: "Message abstract", + notificationStatusHistory: [], + recipients: [ + { + recipientType: "-", + taxId: generateTestFiscalCode(), + denomination: "AaAaAa BbBbBb", + payment: { + noticeCode: "026773337463073118", + creditorTaxId: "00000000009" + } + } + ] as Array, + attachments: [ + { + messageId: generateTestMessageId(), + id: "1", + displayName: "A First Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment1.pdf" } + }, + { + messageId: generateTestMessageId(), + id: "2", + displayName: "A Second Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment2.pdf" } + } + ] as Array +}); +const generateComponentProperties = (pnMessage: PNMessage) => ({ + payments: undefined, + messageId: generateTestMessageId(), + message: pnMessage, + service: undefined +}); + +const renderComponent = ( + props: React.ComponentProps +) => { + const globalState = appReducer(undefined, applicationChangeState("active")); + const store = createStore(appReducer, globalState as any); + + return { + component: renderScreenWithNavigationStoreContext( + () => , + PN_ROUTES.MESSAGE_DETAILS, + {}, + store + ), + store + }; +}; diff --git a/ts/features/pn/navigation/navigator.tsx b/ts/features/pn/navigation/navigator.tsx index 5b6a533ca60..df23de6cdef 100644 --- a/ts/features/pn/navigation/navigator.tsx +++ b/ts/features/pn/navigation/navigator.tsx @@ -2,30 +2,41 @@ import * as React from "react"; import { createStackNavigator } from "@react-navigation/stack"; import { isGestureEnabled } from "../../../utils/navigation"; import { MessageDetailsScreen } from "../screens/MessageDetailsScreen"; +import { LegacyMessageDetailsScreen } from "../screens/LegacyMessageDetailsScreen"; import { AttachmentPreviewScreen } from "../screens/AttachmentPreviewScreen"; import { PaidPaymentScreen } from "../screens/PaidPaymentScreen"; +import { useIOSelector } from "../../../store/hooks"; +import { isDesignSystemEnabledSelector } from "../../../store/reducers/persistedPreferences"; import { PnParamsList } from "./params"; import PN_ROUTES from "./routes"; const Stack = createStackNavigator(); -export const PnStackNavigator = () => ( - - - - - -); +export const PnStackNavigator = () => { + const isDesignSystemEnabled = useIOSelector(isDesignSystemEnabledSelector); + + return ( + + + + + + ); +}; diff --git a/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx b/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx new file mode 100644 index 00000000000..012f077524f --- /dev/null +++ b/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx @@ -0,0 +1,140 @@ +import * as pot from "@pagopa/ts-commons/lib/pot"; +import { pipe } from "fp-ts/lib/function"; +import * as O from "fp-ts/lib/Option"; +import React from "react"; +import { SafeAreaView } from "react-native"; +import { useFocusEffect, useNavigation } from "@react-navigation/native"; +import { useStore } from "react-redux"; +import { IOStyles } from "../../../components/core/variables/IOStyles"; +import BaseScreenComponent from "../../../components/screens/BaseScreenComponent"; +import { ServiceId } from "../../../../definitions/backend/ServiceId"; +import I18n from "../../../i18n"; +import { IOStackNavigationRouteProps } from "../../../navigation/params/AppParamsList"; +import { useIODispatch, useIOSelector } from "../../../store/hooks"; +import { UIMessageId } from "../../messages/types"; +import { serviceByIdSelector } from "../../../store/reducers/entities/services/servicesById"; +import { emptyContextualHelp } from "../../../utils/emptyContextualHelp"; +import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender"; +import { LegacyMessageDetails } from "../components/LegacyMessageDetails"; +import { PnParamsList } from "../navigation/params"; +import { pnMessageFromIdSelector } from "../store/reducers"; +import { cancelPreviousAttachmentDownload } from "../../messages/store/actions"; +import { profileFiscalCodeSelector } from "../../../store/reducers/profile"; +import { + containsF24FromPNMessagePot, + isCancelledFromPNMessagePot, + paymentsFromPNMessagePot +} from "../utils"; +import { trackPNUxSuccess } from "../analytics"; +import { isStrictSome } from "../../../utils/pot"; +import { + cancelPaymentStatusTracking, + cancelQueuedPaymentUpdates, + clearSelectedPayment, + startPaymentStatusTracking, + updatePaymentForMessage +} from "../store/actions"; +import { GlobalState } from "../../../store/reducers/types"; +import { selectedPaymentIdSelector } from "../store/reducers/payments"; +import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenComponent"; +import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering"; +import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png"; + +export type MessageDetailsScreenNavigationParams = Readonly<{ + messageId: UIMessageId; + serviceId: ServiceId; + firstTimeOpening: boolean; +}>; + +export const LegacyMessageDetailsScreen = ( + props: IOStackNavigationRouteProps +): React.ReactElement => { + const { messageId, serviceId, firstTimeOpening } = props.route.params; + + const dispatch = useIODispatch(); + const navigation = useNavigation(); + + const service = pot.toUndefined( + useIOSelector(state => serviceByIdSelector(state, serviceId)) + ); + + const currentFiscalCode = useIOSelector(profileFiscalCodeSelector); + const messagePot = useIOSelector(state => + pnMessageFromIdSelector(state, messageId) + ); + const payments = paymentsFromPNMessagePot(currentFiscalCode, messagePot); + + const customGoBack = React.useCallback(() => { + dispatch(cancelPreviousAttachmentDownload()); + dispatch(cancelQueuedPaymentUpdates()); + dispatch(cancelPaymentStatusTracking()); + navigation.goBack(); + }, [dispatch, navigation]); + + useOnFirstRender(() => { + dispatch(startPaymentStatusTracking(messageId)); + + if (isStrictSome(messagePot)) { + const paymentCount = payments?.length ?? 0; + const isCancelled = isCancelledFromPNMessagePot(messagePot); + const containsF24 = containsF24FromPNMessagePot(messagePot); + + trackPNUxSuccess( + paymentCount, + firstTimeOpening, + isCancelled, + containsF24 + ); + } + }); + + const store = useStore(); + useFocusEffect( + React.useCallback(() => { + const globalState = store.getState() as GlobalState; + const selectedPaymentId = selectedPaymentIdSelector(globalState); + if (selectedPaymentId) { + dispatch(clearSelectedPayment()); + dispatch( + updatePaymentForMessage.request({ + messageId, + paymentId: selectedPaymentId + }) + ); + } + }, [dispatch, messageId, store]) + ); + + return ( + + + {pipe( + messagePot, + pot.toOption, + O.flatten, + O.fold( + () => ( + + ), + message => ( + + ) + ) + )} + + + ); +}; diff --git a/ts/features/pn/screens/MessageDetailsScreen.tsx b/ts/features/pn/screens/MessageDetailsScreen.tsx index bd84320d4a5..24c6b8a21cf 100644 --- a/ts/features/pn/screens/MessageDetailsScreen.tsx +++ b/ts/features/pn/screens/MessageDetailsScreen.tsx @@ -1,15 +1,19 @@ +import React, { useCallback } from "react"; +import { SafeAreaView } from "react-native"; +import { + RouteProp, + useFocusEffect, + useNavigation, + useRoute +} from "@react-navigation/native"; +import { useStore } from "react-redux"; import * as pot from "@pagopa/ts-commons/lib/pot"; import { pipe } from "fp-ts/lib/function"; import * as O from "fp-ts/lib/Option"; -import React from "react"; -import { SafeAreaView } from "react-native"; -import { useFocusEffect, useNavigation } from "@react-navigation/native"; -import { useStore } from "react-redux"; import { IOStyles } from "../../../components/core/variables/IOStyles"; import BaseScreenComponent from "../../../components/screens/BaseScreenComponent"; import { ServiceId } from "../../../../definitions/backend/ServiceId"; import I18n from "../../../i18n"; -import { IOStackNavigationRouteProps } from "../../../navigation/params/AppParamsList"; import { useIODispatch, useIOSelector } from "../../../store/hooks"; import { UIMessageId } from "../../messages/types"; import { serviceByIdSelector } from "../../../store/reducers/entities/services/servicesById"; @@ -40,31 +44,34 @@ import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenCo import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering"; import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png"; -export type MessageDetailsScreenNavigationParams = Readonly<{ +export type MessageDetailsScreenNavigationParams = { messageId: UIMessageId; serviceId: ServiceId; firstTimeOpening: boolean; -}>; +}; -export const MessageDetailsScreen = ( - props: IOStackNavigationRouteProps -): React.ReactElement => { - const { messageId, serviceId, firstTimeOpening } = props.route.params; +type MessageDetailsRouteProps = RouteProp< + PnParamsList, + "PN_ROUTES_MESSAGE_DETAILS" +>; +export const MessageDetailsScreen = () => { const dispatch = useIODispatch(); const navigation = useNavigation(); + const route = useRoute(); + + const { messageId, serviceId, firstTimeOpening } = route.params; const service = pot.toUndefined( useIOSelector(state => serviceByIdSelector(state, serviceId)) ); - const currentFiscalCode = useIOSelector(profileFiscalCodeSelector); const messagePot = useIOSelector(state => pnMessageFromIdSelector(state, messageId) ); const payments = paymentsFromPNMessagePot(currentFiscalCode, messagePot); - const customGoBack = React.useCallback(() => { + const customGoBack = useCallback(() => { dispatch(cancelPreviousAttachmentDownload()); dispatch(cancelQueuedPaymentUpdates()); dispatch(cancelPaymentStatusTracking()); @@ -90,7 +97,7 @@ export const MessageDetailsScreen = ( const store = useStore(); useFocusEffect( - React.useCallback(() => { + useCallback(() => { const globalState = store.getState() as GlobalState; const selectedPaymentId = selectedPaymentIdSelector(globalState); if (selectedPaymentId) { diff --git a/ts/features/pn/store/types/transformers.ts b/ts/features/pn/store/types/transformers.ts index e89ffc030b4..3b0c5269074 100644 --- a/ts/features/pn/store/types/transformers.ts +++ b/ts/features/pn/store/types/transformers.ts @@ -15,6 +15,7 @@ export const toPNMessage = ( O.chainNullableK(message => message.details), O.map(details => ({ ...details, + created_at: messageFromApi.created_at, attachments: pipe( attachmentsFromThirdPartyMessage(messageFromApi), O.toUndefined diff --git a/ts/features/pn/store/types/types.ts b/ts/features/pn/store/types/types.ts index 525cce459d3..192e7d9652a 100644 --- a/ts/features/pn/store/types/types.ts +++ b/ts/features/pn/store/types/types.ts @@ -1,7 +1,7 @@ import { IOReceivedNotification } from "../../../../../definitions/pn/IOReceivedNotification"; import { UIAttachment } from "../../../messages/types"; -export type PNMessage = IOReceivedNotification & - Readonly<{ - attachments?: ReadonlyArray; - }>; +export type PNMessage = IOReceivedNotification & { + created_at: Date; + attachments?: ReadonlyArray; +}; From 239d8d3f9b11c0a7900a16bb406f1e56afba8a7d Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 11:52:32 +0100 Subject: [PATCH 02/15] add tests --- .../__tests__/MessageDetailHeader.test.tsx | 15 ++ .../MessageDetailHeader.test.tsx.snap | 96 +++++++++ .../__test__/LegacyMessageDetails.test.tsx | 1 + .../__test__/MessageDetails.test.tsx | 187 ------------------ .../__test__/MessageDetailsContent.test.tsx | 10 + .../MessageDetailsContent.test.tsx.snap | 49 +++++ 6 files changed, 171 insertions(+), 187 deletions(-) create mode 100644 ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx create mode 100644 ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap delete mode 100644 ts/features/pn/components/__test__/MessageDetails.test.tsx create mode 100644 ts/features/pn/components/__test__/MessageDetailsContent.test.tsx create mode 100644 ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx new file mode 100644 index 00000000000..dcb7edbcc7e --- /dev/null +++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx @@ -0,0 +1,15 @@ +import React, { ComponentProps } from "react"; +import { render } from "@testing-library/react-native"; +import { MessageDetailHeader } from "../MessageDetailHeader"; + +const defaultProps: ComponentProps = { + subject: "Subject", + createdAt: new Date() +}; + +describe("MessageDetailHeader component", () => { + it("should match the snapshot with default props", () => { + const component = render(); + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap new file mode 100644 index 00000000000..10f67728de5 --- /dev/null +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -0,0 +1,96 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageDetailHeader component should match the snapshot with default props 1`] = ` + + + Subject + + + + 24 Jan 2024, 10:51 + + + + +`; diff --git a/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx index d1e7fb14f64..a7ec40d4d30 100644 --- a/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx +++ b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx @@ -127,6 +127,7 @@ describe("LegacyMessageDetails component", () => { const generateTestMessageId = () => "00000000000000000000000004" as UIMessageId; const generateTestFiscalCode = () => "AAABBB00A00A000A"; const generatePnMessage = (): PNMessage => ({ + created_at: new Date(), iun: "731143-7-0317-8200-0", subject: "This is the message subject", senderDenomination: "Sender denomination", diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx deleted file mode 100644 index 11a7c0e7b65..00000000000 --- a/ts/features/pn/components/__test__/MessageDetails.test.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import React from "react"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { act, fireEvent } from "@testing-library/react-native"; -import { createStore } from "redux"; -import { applicationChangeState } from "../../../../store/actions/application"; -import { appReducer } from "../../../../store/reducers"; -import { MessageDetails } from "../MessageDetails"; -import { GlobalState } from "../../../../store/reducers/types"; -import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; -import PN_ROUTES from "../../navigation/routes"; -import { UIAttachment, UIMessageId } from "../../../messages/types"; -import { PNMessage } from "../../store/types/types"; -import { Download } from "../../../messages/store/reducers/downloads"; -import { NotificationRecipient } from "../../../../../definitions/pn/NotificationRecipient"; -import { ATTACHMENT_CATEGORY } from "../../../messages/types/attachmentCategory"; - -const mockedOnAttachmentSelect = jest.fn(); - -jest.mock("../../../messages/hooks/useAttachmentDownload", () => ({ - useAttachmentDownload: ( - _attachment: UIAttachment, - _openPreview: (attachment: UIAttachment) => void - ) => ({ - onAttachmentSelect: mockedOnAttachmentSelect, - downloadPot: { kind: "PotNone" } as pot.Pot - }) -})); - -describe("MessageDetails component", () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - it("should not show the cancelled banner when the PN message is not cancelled", () => { - const { component } = renderComponent( - generateComponentProperties(generatePnMessage()) - ); - expect(component.queryByTestId("PnCancelledMessageBanner")).toBeFalsy(); - }); - - it("every attachment item should have a full opaque style when the PN message is not cancelled", () => { - const { component } = renderComponent( - generateComponentProperties(generatePnMessage()) - ); - const messageAttachmentComponents = component.queryAllByTestId( - "MessageAttachmentTouchable" - ); - expect(messageAttachmentComponents.length).toBe(2); - - messageAttachmentComponents.forEach(messageAttachmentComponent => { - const opacity = messageAttachmentComponent?.props.style.opacity; - expect(opacity).toBe(1.0); - }); - }); - - it("should show the cancelled banner when the PN message is cancelled", () => { - const { component } = renderComponent( - generateComponentProperties({ - ...generatePnMessage(), - isCancelled: true - }) - ); - expect(component.queryByTestId("PnCancelledMessageBanner")).toBeDefined(); - }); - - it("every attachment item should have a semi-transparent style when the PN message is cancelled", () => { - const { component } = renderComponent( - generateComponentProperties({ - ...generatePnMessage(), - isCancelled: true - }) - ); - const messageAttachmentComponents = component.queryAllByTestId( - "MessageAttachmentTouchable" - ); - expect(messageAttachmentComponents.length).toBe(2); - - messageAttachmentComponents.forEach(messageAttachmentComponent => { - const opacity = messageAttachmentComponent?.props.style.opacity; - expect(opacity).toBe(0.5); - }); - }); - - it("every attachment item should handle input when the PN message not is cancelled", async () => { - const { component } = renderComponent( - generateComponentProperties(generatePnMessage()) - ); - const messageAttachmentComponents = component.queryAllByTestId( - "MessageAttachmentTouchable" - ); - expect(messageAttachmentComponents.length).toBe(2); - await act(() => { - messageAttachmentComponents.forEach(messageAttachmentComponent => - fireEvent.press(messageAttachmentComponent) - ); - }); - expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes( - messageAttachmentComponents.length - ); - }); - - it("every attachment item should not handle input when the PN message is cancelled", async () => { - const { component } = renderComponent( - generateComponentProperties({ ...generatePnMessage(), isCancelled: true }) - ); - const messageAttachmentComponents = component.queryAllByTestId( - "MessageAttachmentTouchable" - ); - expect(messageAttachmentComponents.length).toBe(2); - // eslint-disable-next-line sonarjs/no-identical-functions - await act(() => { - messageAttachmentComponents.forEach(messageAttachmentComponent => - fireEvent.press(messageAttachmentComponent) - ); - }); - expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes(0); - }); - - it("should NOT render the F24 section if there are no multiple F24", () => { - const { component } = renderComponent( - generateComponentProperties(generatePnMessage()) - ); - expect(component.queryByTestId("pn-message-f24-section")).toBeFalsy(); - }); -}); - -const generateTestMessageId = () => "00000000000000000000000004" as UIMessageId; -const generateTestFiscalCode = () => "AAABBB00A00A000A"; -const generatePnMessage = (): PNMessage => ({ - iun: "731143-7-0317-8200-0", - subject: "This is the message subject", - senderDenomination: "Sender denomination", - abstract: "Message abstract", - notificationStatusHistory: [], - recipients: [ - { - recipientType: "-", - taxId: generateTestFiscalCode(), - denomination: "AaAaAa BbBbBb", - payment: { - noticeCode: "026773337463073118", - creditorTaxId: "00000000009" - } - } - ] as Array, - attachments: [ - { - messageId: generateTestMessageId(), - id: "1", - displayName: "A First Attachment", - contentType: "application/pdf", - category: ATTACHMENT_CATEGORY.DOCUMENT, - resourceUrl: { href: "/resource/attachment1.pdf" } - }, - { - messageId: generateTestMessageId(), - id: "2", - displayName: "A Second Attachment", - contentType: "application/pdf", - category: ATTACHMENT_CATEGORY.DOCUMENT, - resourceUrl: { href: "/resource/attachment2.pdf" } - } - ] as Array -}); -const generateComponentProperties = (pnMessage: PNMessage) => ({ - payments: undefined, - messageId: generateTestMessageId(), - message: pnMessage, - service: undefined -}); - -const renderComponent = ( - props: React.ComponentProps -) => { - const globalState = appReducer(undefined, applicationChangeState("active")); - const store = createStore(appReducer, globalState as any); - - return { - component: renderScreenWithNavigationStoreContext( - () => , - PN_ROUTES.MESSAGE_DETAILS, - {}, - store - ), - store - }; -}; diff --git a/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx new file mode 100644 index 00000000000..226c28a2c50 --- /dev/null +++ b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import { render } from "@testing-library/react-native"; +import { MessageDetailsContent } from "../MessageDetailsContent"; + +describe("MessageDetailsContent component", () => { + it("should match the snapshot when abstract is defined", () => { + const component = render(); + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap new file mode 100644 index 00000000000..eae994ed610 --- /dev/null +++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageDetailsContent component should match the snapshot when abstract is defined 1`] = ` + + + + abstract + + +`; From cd149efc3fb3fa065f64a3e5b47441ee7a54becd Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 11:55:37 +0100 Subject: [PATCH 03/15] change text color --- ts/features/pn/components/MessageDetailsContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/pn/components/MessageDetailsContent.tsx b/ts/features/pn/components/MessageDetailsContent.tsx index 6bae2e6e2e6..5b8b8dcd624 100644 --- a/ts/features/pn/components/MessageDetailsContent.tsx +++ b/ts/features/pn/components/MessageDetailsContent.tsx @@ -15,7 +15,7 @@ export const MessageDetailsContent = ({ return ( - {abstract} + {abstract} ); }; From bcfcb8ea4635fd95da3b264208124993e045512b Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 12:04:19 +0100 Subject: [PATCH 04/15] Update message timestamp and color --- .../__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap | 2 +- .../__snapshots__/MessageDetailsContent.test.tsx.snap | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap index 10f67728de5..aa92ea0f50f 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -75,7 +75,7 @@ exports[`MessageDetailHeader component should match the snapshot with default pr } weight="Bold" > - 24 Jan 2024, 10:51 + 24 Jan 2024, 11:01 Date: Wed, 24 Jan 2024 14:53:40 +0100 Subject: [PATCH 05/15] update snap --- .../__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap index aa92ea0f50f..75594282955 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -75,7 +75,7 @@ exports[`MessageDetailHeader component should match the snapshot with default pr } weight="Bold" > - 24 Jan 2024, 11:01 + 24 Jan 2024, 13:52 Date: Wed, 24 Jan 2024 15:16:51 +0100 Subject: [PATCH 06/15] fix test --- .../MessageDetail/__tests__/MessageDetailHeader.test.tsx | 2 +- .../__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx index dcb7edbcc7e..be739559c74 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx +++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx @@ -4,7 +4,7 @@ import { MessageDetailHeader } from "../MessageDetailHeader"; const defaultProps: ComponentProps = { subject: "Subject", - createdAt: new Date() + createdAt: new Date("2021-10-18T16:00:30.541Z") }; describe("MessageDetailHeader component", () => { diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap index 75594282955..c5b0788e14d 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -75,7 +75,7 @@ exports[`MessageDetailHeader component should match the snapshot with default pr } weight="Bold" > - 24 Jan 2024, 13:52 + 18 Oct 2021, 16:00 Date: Fri, 26 Jan 2024 16:32:22 +0100 Subject: [PATCH 07/15] update header mode and options in PnStackNavigator and MessageDetailsScreen --- ts/features/pn/navigation/navigator.tsx | 11 ++- .../pn/screens/MessageDetailsScreen.tsx | 71 +++++++++---------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/ts/features/pn/navigation/navigator.tsx b/ts/features/pn/navigation/navigator.tsx index df23de6cdef..d837d801971 100644 --- a/ts/features/pn/navigation/navigator.tsx +++ b/ts/features/pn/navigation/navigator.tsx @@ -18,7 +18,7 @@ export const PnStackNavigator = () => { return ( { ? MessageDetailsScreen : LegacyMessageDetailsScreen } + options={{ + headerShown: isDesignSystemEnabled + }} /> ); diff --git a/ts/features/pn/screens/MessageDetailsScreen.tsx b/ts/features/pn/screens/MessageDetailsScreen.tsx index 24c6b8a21cf..564c3812898 100644 --- a/ts/features/pn/screens/MessageDetailsScreen.tsx +++ b/ts/features/pn/screens/MessageDetailsScreen.tsx @@ -11,7 +11,6 @@ import * as pot from "@pagopa/ts-commons/lib/pot"; import { pipe } from "fp-ts/lib/function"; import * as O from "fp-ts/lib/Option"; import { IOStyles } from "../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../components/screens/BaseScreenComponent"; import { ServiceId } from "../../../../definitions/backend/ServiceId"; import I18n from "../../../i18n"; import { useIODispatch, useIOSelector } from "../../../store/hooks"; @@ -42,6 +41,7 @@ import { GlobalState } from "../../../store/reducers/types"; import { selectedPaymentIdSelector } from "../store/reducers/payments"; import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenComponent"; import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering"; +import { useHeaderSecondLevel } from "../../../hooks/useHeaderSecondLevel"; import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png"; export type MessageDetailsScreenNavigationParams = { @@ -71,12 +71,17 @@ export const MessageDetailsScreen = () => { ); const payments = paymentsFromPNMessagePot(currentFiscalCode, messagePot); - const customGoBack = useCallback(() => { - dispatch(cancelPreviousAttachmentDownload()); - dispatch(cancelQueuedPaymentUpdates()); - dispatch(cancelPaymentStatusTracking()); - navigation.goBack(); - }, [dispatch, navigation]); + useHeaderSecondLevel({ + title: "", + goBack: () => { + dispatch(cancelPreviousAttachmentDownload()); + dispatch(cancelQueuedPaymentUpdates()); + dispatch(cancelPaymentStatusTracking()); + navigation.goBack(); + }, + supportRequest: true, + contextualHelp: emptyContextualHelp + }); useOnFirstRender(() => { dispatch(startPaymentStatusTracking(messageId)); @@ -113,35 +118,29 @@ export const MessageDetailsScreen = () => { ); return ( - - - {pipe( - messagePot, - pot.toOption, - O.flatten, - O.fold( - () => ( - - ), - message => ( - - ) + + {pipe( + messagePot, + pot.toOption, + O.flatten, + O.fold( + () => ( + + ), + message => ( + ) - )} - - + ) + )} + ); }; From 1bee02ac06a5964aa3d0497341ec972fc7cb1d14 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Fri, 26 Jan 2024 16:38:08 +0100 Subject: [PATCH 08/15] Remove unused type --- ts/features/pn/screens/LegacyMessageDetailsScreen.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx b/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx index 012f077524f..7d2ad7d08a2 100644 --- a/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx +++ b/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx @@ -7,11 +7,9 @@ import { useFocusEffect, useNavigation } from "@react-navigation/native"; import { useStore } from "react-redux"; import { IOStyles } from "../../../components/core/variables/IOStyles"; import BaseScreenComponent from "../../../components/screens/BaseScreenComponent"; -import { ServiceId } from "../../../../definitions/backend/ServiceId"; import I18n from "../../../i18n"; import { IOStackNavigationRouteProps } from "../../../navigation/params/AppParamsList"; import { useIODispatch, useIOSelector } from "../../../store/hooks"; -import { UIMessageId } from "../../messages/types"; import { serviceByIdSelector } from "../../../store/reducers/entities/services/servicesById"; import { emptyContextualHelp } from "../../../utils/emptyContextualHelp"; import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender"; @@ -40,12 +38,6 @@ import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenCo import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering"; import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png"; -export type MessageDetailsScreenNavigationParams = Readonly<{ - messageId: UIMessageId; - serviceId: ServiceId; - firstTimeOpening: boolean; -}>; - export const LegacyMessageDetailsScreen = ( props: IOStackNavigationRouteProps ): React.ReactElement => { From 983145b745415b798a6c98c63d0e592b7bebc34f Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Fri, 26 Jan 2024 16:50:09 +0100 Subject: [PATCH 09/15] replace InfoScreenComponent --- ts/features/pn/screens/MessageDetailsScreen.tsx | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/ts/features/pn/screens/MessageDetailsScreen.tsx b/ts/features/pn/screens/MessageDetailsScreen.tsx index 564c3812898..c1d0c9e85fe 100644 --- a/ts/features/pn/screens/MessageDetailsScreen.tsx +++ b/ts/features/pn/screens/MessageDetailsScreen.tsx @@ -1,5 +1,4 @@ import React, { useCallback } from "react"; -import { SafeAreaView } from "react-native"; import { RouteProp, useFocusEffect, @@ -10,7 +9,6 @@ import { useStore } from "react-redux"; import * as pot from "@pagopa/ts-commons/lib/pot"; import { pipe } from "fp-ts/lib/function"; import * as O from "fp-ts/lib/Option"; -import { IOStyles } from "../../../components/core/variables/IOStyles"; import { ServiceId } from "../../../../definitions/backend/ServiceId"; import I18n from "../../../i18n"; import { useIODispatch, useIOSelector } from "../../../store/hooks"; @@ -39,10 +37,8 @@ import { } from "../store/actions"; import { GlobalState } from "../../../store/reducers/types"; import { selectedPaymentIdSelector } from "../store/reducers/payments"; -import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenComponent"; -import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering"; import { useHeaderSecondLevel } from "../../../hooks/useHeaderSecondLevel"; -import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png"; +import { OperationResultScreenContent } from "../../../components/screens/OperationResultScreenContent"; export type MessageDetailsScreenNavigationParams = { messageId: UIMessageId; @@ -118,17 +114,17 @@ export const MessageDetailsScreen = () => { ); return ( - + <> {pipe( messagePot, pot.toOption, O.flatten, O.fold( () => ( - ), message => ( @@ -141,6 +137,6 @@ export const MessageDetailsScreen = () => { ) ) )} - + ); }; From 8eb6bb8ecaad487899b7ff08094c09e55a552b60 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Fri, 26 Jan 2024 16:50:35 +0100 Subject: [PATCH 10/15] update import for ScrollView --- ts/features/pn/components/MessageDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/pn/components/MessageDetails.tsx b/ts/features/pn/components/MessageDetails.tsx index 703d01793ac..c123ca75edc 100644 --- a/ts/features/pn/components/MessageDetails.tsx +++ b/ts/features/pn/components/MessageDetails.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { ScrollView } from "react-native-gesture-handler"; +import { ScrollView } from "react-native"; import { ServicePublic } from "../../../../definitions/backend/ServicePublic"; import { UIMessageId } from "../../messages/types"; import { PNMessage } from "../store/types/types"; From 868be80cc8b5ddf4e9351a4741820e2530a388f0 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Fri, 26 Jan 2024 17:05:00 +0100 Subject: [PATCH 11/15] add snap --- .../__tests__/MessageDetailHeader.test.tsx | 20 ++++ .../MessageDetailHeader.test.tsx.snap | 95 +++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx index be739559c74..5539b0d3fdc 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx +++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx @@ -1,6 +1,15 @@ import React, { ComponentProps } from "react"; import { render } from "@testing-library/react-native"; import { MessageDetailHeader } from "../MessageDetailHeader"; +import { ServicePublic } from "../../../../../../definitions/backend/ServicePublic"; + +const service = { + service_id: "serviceId", + service_name: "health", + organization_name: "Organization foo", + department_name: "Department one", + organization_fiscal_code: "OFSAAAAAA" +} as ServicePublic; const defaultProps: ComponentProps = { subject: "Subject", @@ -12,4 +21,15 @@ describe("MessageDetailHeader component", () => { const component = render(); expect(component.toJSON()).toMatchSnapshot(); }); + + it("should match the snapshot with all props", () => { + const component = render( + + ); + expect(component.toJSON()).toMatchSnapshot(); + }); }); diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap index c5b0788e14d..d1597d22480 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -1,5 +1,100 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`MessageDetailHeader component should match the snapshot with all props 1`] = ` + + + Subject + + + + 18 Oct 2021, 16:00 + + + + +`; + exports[`MessageDetailHeader component should match the snapshot with default props 1`] = ` Date: Fri, 26 Jan 2024 17:07:50 +0100 Subject: [PATCH 12/15] add snap --- ts/features/pn/components/MessageDetailsContent.tsx | 2 +- .../pn/components/__test__/MessageDetailsContent.test.tsx | 5 +++++ .../__snapshots__/MessageDetailsContent.test.tsx.snap | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ts/features/pn/components/MessageDetailsContent.tsx b/ts/features/pn/components/MessageDetailsContent.tsx index 5b8b8dcd624..96cbdbbe3ad 100644 --- a/ts/features/pn/components/MessageDetailsContent.tsx +++ b/ts/features/pn/components/MessageDetailsContent.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Body, ContentWrapper, VSpacer } from "@pagopa/io-app-design-system"; type MessageDetailsContentProps = { - abstract: string | undefined; + abstract?: string; }; export const MessageDetailsContent = ({ diff --git a/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx index 226c28a2c50..31e5a7c3603 100644 --- a/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx +++ b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx @@ -7,4 +7,9 @@ describe("MessageDetailsContent component", () => { const component = render(); expect(component.toJSON()).toMatchSnapshot(); }); + + it("should match the snapshot when abstract is not defined", () => { + const component = render(); + expect(component.toJSON()).toMatchSnapshot(); + }); }); diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap index ae770e59d3a..19f593ad32a 100644 --- a/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap +++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap @@ -47,3 +47,5 @@ exports[`MessageDetailsContent component should match the snapshot when abstract `; + +exports[`MessageDetailsContent component should match the snapshot when abstract is not defined 1`] = `null`; From c1099cbe1a2e3009587212477a81dddfd225d157 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Fri, 26 Jan 2024 19:15:08 +0100 Subject: [PATCH 13/15] add snap --- ts/features/pn/__mocks__/message.ts | 44 ++ ts/features/pn/components/MessageDetails.tsx | 4 +- .../__test__/MessageDetails.test.tsx | 43 ++ .../MessageDetails.test.tsx.snap | 449 ++++++++++++++++++ 4 files changed, 538 insertions(+), 2 deletions(-) create mode 100644 ts/features/pn/__mocks__/message.ts create mode 100644 ts/features/pn/components/__test__/MessageDetails.test.tsx create mode 100644 ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap diff --git a/ts/features/pn/__mocks__/message.ts b/ts/features/pn/__mocks__/message.ts new file mode 100644 index 00000000000..3019e1df815 --- /dev/null +++ b/ts/features/pn/__mocks__/message.ts @@ -0,0 +1,44 @@ +import { UIAttachment, UIMessageId } from "../../messages/types"; +import { PNMessage } from "../store/types/types"; +import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory"; +import { NotificationRecipient } from "../../../../definitions/pn/NotificationRecipient"; + +export const messageId = "00000000000000000000000004" as UIMessageId; + +export const pnMessage: PNMessage = { + created_at: new Date("2020-01-01T00:00:00.000Z"), + iun: "731143-7-0317-8200-0", + subject: "This is the message subject", + senderDenomination: "Sender denomination", + abstract: "Message abstract", + notificationStatusHistory: [], + recipients: [ + { + recipientType: "-", + taxId: "AAABBB00A00A000A", + denomination: "AaAaAa BbBbBb", + payment: { + noticeCode: "026773337463073118", + creditorTaxId: "00000000009" + } + } + ] as Array, + attachments: [ + { + messageId, + id: "1", + displayName: "A First Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment1.pdf" } + }, + { + messageId, + id: "2", + displayName: "A Second Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment2.pdf" } + } + ] as Array +}; diff --git a/ts/features/pn/components/MessageDetails.tsx b/ts/features/pn/components/MessageDetails.tsx index c123ca75edc..d6f25513ce4 100644 --- a/ts/features/pn/components/MessageDetails.tsx +++ b/ts/features/pn/components/MessageDetails.tsx @@ -10,8 +10,8 @@ import { MessageDetailsContent } from "./MessageDetailsContent"; type MessageDetailsProps = { message: PNMessage; messageId: UIMessageId; - service: ServicePublic | undefined; - payments: ReadonlyArray | undefined; + service?: ServicePublic; + payments?: ReadonlyArray; }; export const MessageDetails = ({ message, service }: MessageDetailsProps) => ( diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx new file mode 100644 index 00000000000..879b8ec686e --- /dev/null +++ b/ts/features/pn/components/__test__/MessageDetails.test.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import configureMockStore from "redux-mock-store"; +import { applicationChangeState } from "../../../../store/actions/application"; +import { appReducer } from "../../../../store/reducers"; +import { MessageDetails } from "../MessageDetails"; +import { GlobalState } from "../../../../store/reducers/types"; +import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; +import { PNMessage } from "../../store/types/types"; +import { messageId, pnMessage } from "../../__mocks__/message"; + +describe("MessageDetails component", () => { + it("should match the snapshot", () => { + const { component } = renderComponent( + generateComponentProperties(pnMessage) + ); + expect(component).toMatchSnapshot(); + }); +}); + +const generateComponentProperties = (message: PNMessage) => ({ + messageId, + message, + payments: undefined, + service: undefined +}); + +const renderComponent = ( + props: React.ComponentProps +) => { + const globalState = appReducer(undefined, applicationChangeState("active")); + const mockStore = configureMockStore(); + const store: ReturnType = mockStore(globalState); + + return { + component: renderScreenWithNavigationStoreContext( + () => , + "DUMMY_ROUTE", + {}, + store + ), + store + }; +}; diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap new file mode 100644 index 00000000000..76c4c33377b --- /dev/null +++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap @@ -0,0 +1,449 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageDetails component should match the snapshot 1`] = ` + + + + + + + + + + + + + DUMMY_ROUTE + + + + + + + + + + + + + + + + + + + + + This is the message subject + + + + 01 Jan 2020, 00:00 + + + + + + + + Message abstract + + + + + + + + + + + + + + +`; From 186b15e8e21544196a3825bec548c59dd175fa53 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Mon, 29 Jan 2024 15:15:55 +0100 Subject: [PATCH 14/15] refactor goBack function --- .../pn/screens/MessageDetailsScreen.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ts/features/pn/screens/MessageDetailsScreen.tsx b/ts/features/pn/screens/MessageDetailsScreen.tsx index c1d0c9e85fe..cc4ff8e8dbe 100644 --- a/ts/features/pn/screens/MessageDetailsScreen.tsx +++ b/ts/features/pn/screens/MessageDetailsScreen.tsx @@ -14,7 +14,6 @@ import I18n from "../../../i18n"; import { useIODispatch, useIOSelector } from "../../../store/hooks"; import { UIMessageId } from "../../messages/types"; import { serviceByIdSelector } from "../../../store/reducers/entities/services/servicesById"; -import { emptyContextualHelp } from "../../../utils/emptyContextualHelp"; import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender"; import { MessageDetails } from "../components/MessageDetails"; import { PnParamsList } from "../navigation/params"; @@ -67,16 +66,17 @@ export const MessageDetailsScreen = () => { ); const payments = paymentsFromPNMessagePot(currentFiscalCode, messagePot); + const goBack = useCallback(() => { + dispatch(cancelPreviousAttachmentDownload()); + dispatch(cancelQueuedPaymentUpdates()); + dispatch(cancelPaymentStatusTracking()); + navigation.goBack(); + }, []); + useHeaderSecondLevel({ title: "", - goBack: () => { - dispatch(cancelPreviousAttachmentDownload()); - dispatch(cancelQueuedPaymentUpdates()); - dispatch(cancelPaymentStatusTracking()); - navigation.goBack(); - }, - supportRequest: true, - contextualHelp: emptyContextualHelp + goBack, + supportRequest: true }); useOnFirstRender(() => { From 48773e3de4ac99a9b1c00e43580c2f6681361838 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Mon, 29 Jan 2024 15:16:42 +0100 Subject: [PATCH 15/15] update tests --- ts/features/pn/__mocks__/message.ts | 80 +- .../__test__/MessageDetails.test.tsx | 18 +- .../MessageDetails.test.tsx.snap | 4 +- .../__test__/MessageDetailsScreen.test.tsx | 83 + .../MessageDetailsScreen.test.tsx.snap | 1372 +++++++++++++++++ 5 files changed, 1513 insertions(+), 44 deletions(-) create mode 100644 ts/features/pn/screens/__test__/MessageDetailsScreen.test.tsx create mode 100644 ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap diff --git a/ts/features/pn/__mocks__/message.ts b/ts/features/pn/__mocks__/message.ts index 3019e1df815..6bfc8eeaccb 100644 --- a/ts/features/pn/__mocks__/message.ts +++ b/ts/features/pn/__mocks__/message.ts @@ -1,44 +1,46 @@ -import { UIAttachment, UIMessageId } from "../../messages/types"; -import { PNMessage } from "../store/types/types"; +import { ThirdPartyMessageWithContent } from "../../../../definitions/backend/ThirdPartyMessageWithContent"; +import { message_1 } from "../../messages/__mocks__/message"; import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory"; -import { NotificationRecipient } from "../../../../definitions/pn/NotificationRecipient"; -export const messageId = "00000000000000000000000004" as UIMessageId; - -export const pnMessage: PNMessage = { +export const thirdPartyMessage: ThirdPartyMessageWithContent = { + ...message_1, created_at: new Date("2020-01-01T00:00:00.000Z"), - iun: "731143-7-0317-8200-0", - subject: "This is the message subject", - senderDenomination: "Sender denomination", - abstract: "Message abstract", - notificationStatusHistory: [], - recipients: [ - { - recipientType: "-", - taxId: "AAABBB00A00A000A", - denomination: "AaAaAa BbBbBb", - payment: { - noticeCode: "026773337463073118", - creditorTaxId: "00000000009" - } - } - ] as Array, - attachments: [ - { - messageId, - id: "1", - displayName: "A First Attachment", - contentType: "application/pdf", - category: ATTACHMENT_CATEGORY.DOCUMENT, - resourceUrl: { href: "/resource/attachment1.pdf" } - }, - { - messageId, - id: "2", - displayName: "A Second Attachment", - contentType: "application/pdf", - category: ATTACHMENT_CATEGORY.DOCUMENT, - resourceUrl: { href: "/resource/attachment2.pdf" } + third_party_message: { + details: { + abstract: "######## abstract ########", + attachments: [ + { + messageId: message_1.id, + id: "1", + displayName: "A First Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment1.pdf" } + }, + { + messageId: message_1.id, + id: "2", + displayName: "A Second Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment2.pdf" } + } + ], + iun: "731143-7-0317-8200-0", + subject: "######## subject ########", + recipients: [ + { + recipientType: "-", + taxId: "AAABBB00A00A000A", + denomination: "AaAaAa BbBbBb", + payment: { + noticeCode: "026773337463073118", + creditorTaxId: "00000000009" + } + } + ], + notificationStatusHistory: [], + senderDenomination: "Sender denomination" } - ] as Array + } }; diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx index 879b8ec686e..b6c8687b84c 100644 --- a/ts/features/pn/components/__test__/MessageDetails.test.tsx +++ b/ts/features/pn/components/__test__/MessageDetails.test.tsx @@ -1,23 +1,35 @@ import React from "react"; import configureMockStore from "redux-mock-store"; +import { pipe } from "fp-ts/lib/function"; +import * as O from "fp-ts/lib/Option"; import { applicationChangeState } from "../../../../store/actions/application"; import { appReducer } from "../../../../store/reducers"; import { MessageDetails } from "../MessageDetails"; import { GlobalState } from "../../../../store/reducers/types"; import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; import { PNMessage } from "../../store/types/types"; -import { messageId, pnMessage } from "../../__mocks__/message"; +import { thirdPartyMessage } from "../../__mocks__/message"; +import { toPNMessage } from "../../store/types/transformers"; +import { UIMessageId } from "../../../messages/types"; + +const pnMessage = pipe(thirdPartyMessage, toPNMessage, O.toUndefined); describe("MessageDetails component", () => { it("should match the snapshot", () => { const { component } = renderComponent( - generateComponentProperties(pnMessage) + generateComponentProperties( + thirdPartyMessage.id as UIMessageId, + pnMessage! + ) ); expect(component).toMatchSnapshot(); }); }); -const generateComponentProperties = (message: PNMessage) => ({ +const generateComponentProperties = ( + messageId: UIMessageId, + message: PNMessage +) => ({ messageId, message, payments: undefined, diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap index 76c4c33377b..45785dc4e5f 100644 --- a/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap +++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap @@ -333,7 +333,7 @@ exports[`MessageDetails component should match the snapshot 1`] = ` testID="message-header-subject" weight="SemiBold" > - This is the message subject + ######## subject ######## - Message abstract + ######## abstract ######## diff --git a/ts/features/pn/screens/__test__/MessageDetailsScreen.test.tsx b/ts/features/pn/screens/__test__/MessageDetailsScreen.test.tsx new file mode 100644 index 00000000000..0aa4268dca0 --- /dev/null +++ b/ts/features/pn/screens/__test__/MessageDetailsScreen.test.tsx @@ -0,0 +1,83 @@ +import configureMockStore from "redux-mock-store"; +import { Action, Store } from "redux"; +import PN_ROUTES from "../../navigation/routes"; +import { GlobalState } from "../../../../store/reducers/types"; +import { appReducer } from "../../../../store/reducers"; +import { MessageDetailsScreen } from "../MessageDetailsScreen"; +import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; +import { reproduceSequence } from "../../../../utils/tests"; +import { + loadMessageById, + loadMessageDetails, + loadThirdPartyMessage +} from "../../../messages/store/actions"; +import { + toUIMessage, + toUIMessageDetails +} from "../../../messages/store/reducers/transformers"; +import { message_1 } from "../../../messages/__mocks__/message"; +import { loadServiceDetail } from "../../../../store/actions/services"; +import { service_1 } from "../../../messages/__mocks__/messages"; +import { UIMessageId } from "../../../messages/types"; +import { applicationChangeState } from "../../../../store/actions/application"; +import { thirdPartyMessage } from "../../__mocks__/message"; + +describe("MessageDetailsScreen", () => { + it("should match the snapshot when there is an error", () => { + const sequenceOfActions: ReadonlyArray = [ + applicationChangeState("active") + ]; + + const state: GlobalState = reproduceSequence( + {} as GlobalState, + appReducer, + sequenceOfActions + ); + const mockStore = configureMockStore(); + const store: Store = mockStore(state); + + const { component } = renderComponent(store); + expect(component).toMatchSnapshot(); + }); + + it("should match the snapshot when everything went fine", () => { + const sequenceOfActions: ReadonlyArray = [ + applicationChangeState("active"), + loadMessageById.success(toUIMessage(message_1)), + loadServiceDetail.success(service_1), + loadMessageDetails.success(toUIMessageDetails(message_1)), + loadThirdPartyMessage.success({ + id: message_1.id as UIMessageId, + content: thirdPartyMessage + }) + ]; + + const state: GlobalState = reproduceSequence( + {} as GlobalState, + appReducer, + sequenceOfActions + ); + const mockStore = configureMockStore(); + const store: Store = mockStore(state); + + const { component } = renderComponent(store); + expect(component).toMatchSnapshot(); + }); +}); + +const renderComponent = (store: Store) => { + const { id, sender_service_id } = message_1; + + return { + component: renderScreenWithNavigationStoreContext( + MessageDetailsScreen, + PN_ROUTES.MESSAGE_DETAILS, + { + firstTimeOpening: false, + messageId: id, + serviceId: sender_service_id + }, + store + ) + }; +}; diff --git a/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap new file mode 100644 index 00000000000..48edcca56d3 --- /dev/null +++ b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap @@ -0,0 +1,1372 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageDetailsScreen should match the snapshot when everything went fine 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ######## subject ######## + + + + 01 Jan 2020, 00:00 + + + + + + + + ######## abstract ######## + + + + + + + + + + + + + + +`; + +exports[`MessageDetailsScreen should match the snapshot when there is an error 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Qualcosa è andato storto + + + + Non è stato possibile recuperare i dettagli del tuo messaggio. Riprova per favore + + + + + + + + + + + + + + +`;