diff --git a/src/app/Components/ArtsyWebView.tests.tsx b/src/app/Components/ArtsyWebView.tests.tsx index 788be111e57..aacb1763c09 100644 --- a/src/app/Components/ArtsyWebView.tests.tsx +++ b/src/app/Components/ArtsyWebView.tests.tsx @@ -5,6 +5,7 @@ import { goBack, navigate } from "app/system/navigation/navigate" import { appJson } from "app/utils/jsonFiles" import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" import mockFetch from "jest-fetch-mock" +import { debounce } from "lodash" import { stringify } from "query-string" import Share from "react-native-share" import WebView, { WebViewProps } from "react-native-webview" @@ -24,6 +25,36 @@ jest.mock("app/utils/useWebViewEvent", () => ({ }), })) +// mock implementation of an inner WebView's goBack method that can be observed +const mockGoBack = jest.fn() + +// mocked ref to the inner WebView of an ArtsyWebView whose properties can be observed +const mockRef = { + current: { + goBack: mockGoBack, + stopLoading: jest.fn(), + }, +} + +// mock implementation of the react-native-webview that allows us to observe its properties +jest.mock("react-native-webview", () => { + const React = require("react") + const { View } = require("react-native") + + return { + __esModule: true, + default: React.forwardRef((props: any, ref: any) => { + React.useImperativeHandle(ref, () => mockRef.current) + return + }), + } +}) + +jest.mock("lodash", () => ({ + ...jest.requireActual("lodash"), + debounce: jest.fn(), +})) + const mockOnNavigationStateChange: WebViewNavigation = { navigationType: "click", url: "https://gooooogle.com", @@ -42,7 +73,10 @@ describe("ArtsyWebView", () => { }) describe("ArtsyWebViewPage", () => { - beforeEach(() => jest.clearAllMocks()) + beforeEach(() => { + jest.clearAllMocks() + ;(debounce as jest.Mock).mockImplementation((func) => func) + }) const render = (props: Partial> = {}) => renderWithWrappers() @@ -206,6 +240,63 @@ describe("ArtsyWebViewPage", () => { }) expect(navigate).toHaveBeenCalledWith("https://google.com") }) + + describe("the inner WebView's goBack method", () => { + it("is called when the URL matches a route that is not loaded in a web view", () => { + const tree = render() + + webViewProps(tree).onNavigationStateChange?.({ + ...mockOnNavigationStateChange, + url: "https://staging.artsy.net/artwork/foo", + }) + + expect(mockGoBack).toHaveBeenCalled() + }) + + it("is not called when the URL does not match any route", () => { + const tree = render() + + webViewProps(tree).onNavigationStateChange?.({ + ...mockOnNavigationStateChange, + url: "https://support.artsy.net/", + }) + + expect(mockGoBack).not.toHaveBeenCalled() + }) + + it("is not called when the URL matches a ModalWebView route", () => { + const tree = render() + + webViewProps(tree).onNavigationStateChange?.({ + ...mockOnNavigationStateChange, + url: "https://staging.artsy.net/orders/foo", + }) + + expect(mockGoBack).not.toHaveBeenCalled() + }) + + it("is not called when the URL matches a ReactWebView route", () => { + const tree = render() + + webViewProps(tree).onNavigationStateChange?.({ + ...mockOnNavigationStateChange, + url: "https://staging.artsy.net/meet-the-specialists", + }) + + expect(mockGoBack).not.toHaveBeenCalled() + }) + + it("is not called when the URL matches a VanityURLEntity route", () => { + const tree = render() + + webViewProps(tree).onNavigationStateChange?.({ + ...mockOnNavigationStateChange, + url: "https://staging.artsy.net/foo", + }) + + expect(mockGoBack).not.toHaveBeenCalled() + }) + }) }) }) diff --git a/src/app/Components/ArtsyWebView.tsx b/src/app/Components/ArtsyWebView.tsx index 2624d8db696..28574e8d99e 100644 --- a/src/app/Components/ArtsyWebView.tsx +++ b/src/app/Components/ArtsyWebView.tsx @@ -189,9 +189,12 @@ export const ArtsyWebView = forwardRef< const uri = url.startsWith("/") ? webURL + url : url // Debounce calls just in case multiple stopLoading calls are made in a row - const stopLoading = debounce(() => { + const stopLoading = debounce((needToGoBack = true) => { innerRef.current?.stopLoading() - innerRef.current?.goBack() + + if (needToGoBack) { + innerRef.current?.goBack() + } }, 500) const onNavigationStateChange = (evt: WebViewNavigation) => { @@ -215,13 +218,13 @@ export const ArtsyWebView = forwardRef< // to a different vanityURL that we can handle inapp, such as Fair & Partner. if ( result.type === "match" && - (["ReactWebView", "ModalWebView"].includes(result.module) || - result.module === "VanityURLEntity") + ["ReactWebView", "ModalWebView", "VanityURLEntity"].includes(result.module) ) { innerRef.current!.shareTitleUrl = targetURL return } else { - stopLoading() + const needToGoBack = result.type !== "external_url" + stopLoading(needToGoBack) } // In case of a webview presented modally, if the targetURL is a tab View,