diff --git a/README.md b/README.md index 4689e5c..8518656 100644 --- a/README.md +++ b/README.md @@ -37,14 +37,14 @@ $ yarn add @mutualmobile/react-native-barricade **1. Create and start Barricade** -Create an instance of Barricade with the help of the `createBarricade` function along with an array of `RequestConfig` as its function argument. +Create an instance of Barricade with the help of the `createBarricade` function. While calling this function, you can pass an array of `RequestConfig`(optional) to register the request configs. You can also register a request config later by making use of `registerRequest` method on the barricade instance. **:warning: Make sure to do this in index.js so that you can start Barricade before hitting any API.** ```tsx import { createBarricade } from '@mutualmobile/react-native-barricade'; -const requestConfig = []; // Array of RequestConfigs for all the APIs that needs to be mocked +const requestConfig = []; // Optional: Array of RequestConfigs for all the APIs that needs to be mocked const barricade = createBarricade(requestConfig); barricade.start(); // Start the Barricade @@ -76,7 +76,25 @@ const App = () => { **3. Create RequestConfigs** -Create a `RequestConfig` for each API you want to mock. Then, add these to the list of request configs shown in Step 1. +Create a `RequestConfig` for each API you want to mock. Then, add these to the list of request configs shown in Step 1 or register them individually by calling the `registerRequest` method as shown below. + +```tsx +import { getBarricadeInstance } from '@mutualmobile/react-native-barricade'; + +const apiRequestConfig = {} // RequestConfig for a particular API that you wish to mock. +getBarricadeInstance()?.registerRequest(apiRequestConfig); +``` + +**:warning: Make sure to call the `registerRequest` method only after barricade instance is created.** + +In case you want to unregister a config programmatically, you can do this by calling the `unregisterRequest` method similar to the registerRequest method. + +```tsx +import { getBarricadeInstance } from '@mutualmobile/react-native-barricade'; + +const apiRequestConfig = {} // RequestConfig object that was previously used for registering +getBarricadeInstance()?.unregisterRequest(apiRequestConfig); +``` **RequestConfig:** | Property | Description | Type | @@ -86,6 +104,7 @@ Create a `RequestConfig` for each API you want to mock. Then, add these to the l | **`pathEvaluation`** | Data used to identify the current API triggered from the list of RequestConfigs. | `PathEvaluation` | | **`responseHandler`** | List of mocked responses the current API can return with. By default, the first response from the list is selected. | `ResponseHandler[]` | | **`delay`** | The time (in milliseconds) Barricade needs to wait before responding with the mocked response. This is optional and by default it's `400`. | `number` / `undefined` | +| **`disabled`** | Boolean used to enable/disable mocking of current API. This is optional and by default it's `undefined`. | `boolean` / `undefined` | **PathEvaluation:** | Property | Description | Type | @@ -159,9 +178,9 @@ const successResponseHandler = (request: Request) => { Barricade comes packaged with an in-app interface that allows you to select the network responses at runtime. For this to be visible, you need to add the `BarricadeView` mentioned in Step 2 under **Usage**. <p align="center"> -<img src="https://github.com/mutualmobile/react-native-barricade/blob/main/docs/screenshots/menu.png?raw=true" alt="Developer Menu" width="231" height="500"/> -<img src="https://github.com/mutualmobile/react-native-barricade/blob/main/docs/screenshots/list.png?raw=true" alt="List View" width="231" height="500"/> -<img src="https://github.com/mutualmobile/react-native-barricade/blob/main/docs/screenshots/detail.png?raw=true" alt="Detail View" width="231" height="500"/> +<img src="https://github.com/mutualmobile/react-native-barricade/blob/main/docs/screenshots/developer-menu.png?raw=true" alt="Developer Menu" width="231" height="500"/> +<img src="https://github.com/mutualmobile/react-native-barricade/blob/main/docs/screenshots/request-list.png?raw=true" alt="List View" width="231" height="500"/> +<img src="https://github.com/mutualmobile/react-native-barricade/blob/main/docs/screenshots/response-list.png?raw=true" alt="Detail View" width="231" height="500"/> </p> With this in place and the device shaken, you'll be able to see an option for `Barricade` in React Native's developer menu. On tapping the `Barricade` option, you’ll be redirected to a screen with the list of mocked APIs. @@ -186,7 +205,8 @@ const App = () => { ``` **Note:** In BarricadeView, apart from changing the selected response for any of the listed APIs, we can also: -- Disable/Enable Barricade. This will stop/start mocking API calls and lets you check the app with the actual/mocked API responses at runtime. +- Disable/Enable Barricade. This will stop/start mocking all the registered API calls and lets you check the app with the actual/mocked API responses at runtime. +- Disable/Enable API Mock. This will stop/start mocking the current API calls and lets you check the app with the actual/mocked API response at runtime. - Reset all the changes done to the list of selected responses. ## Store Submission diff --git a/docs/media/demo.gif b/docs/media/demo.gif index 940ab78..4c51a74 100644 Binary files a/docs/media/demo.gif and b/docs/media/demo.gif differ diff --git a/docs/screenshots/detail.png b/docs/screenshots/detail.png deleted file mode 100644 index 43c2d50..0000000 Binary files a/docs/screenshots/detail.png and /dev/null differ diff --git a/docs/screenshots/developer-menu.png b/docs/screenshots/developer-menu.png new file mode 100644 index 0000000..6771b1a Binary files /dev/null and b/docs/screenshots/developer-menu.png differ diff --git a/docs/screenshots/list.png b/docs/screenshots/list.png deleted file mode 100644 index 099b451..0000000 Binary files a/docs/screenshots/list.png and /dev/null differ diff --git a/docs/screenshots/menu.png b/docs/screenshots/menu.png deleted file mode 100644 index bfcf465..0000000 Binary files a/docs/screenshots/menu.png and /dev/null differ diff --git a/docs/screenshots/request-list.png b/docs/screenshots/request-list.png new file mode 100644 index 0000000..9c6d92a Binary files /dev/null and b/docs/screenshots/request-list.png differ diff --git a/docs/screenshots/response-list.png b/docs/screenshots/response-list.png new file mode 100644 index 0000000..25b56e5 Binary files /dev/null and b/docs/screenshots/response-list.png differ diff --git a/example/mock-server/index.ts b/example/mock-server/index.ts index 7844144..e58e717 100644 --- a/example/mock-server/index.ts +++ b/example/mock-server/index.ts @@ -1,12 +1,13 @@ -import { createBarricade } from '@mutualmobile/react-native-barricade'; +import { + createBarricade, + getBarricadeInstance, +} from '@mutualmobile/react-native-barricade'; import { RecentApiRequestConfig } from './api/recent.api.mock'; import { SearchApiRequestConfig } from './api/search.api.mock'; export const mockServer = () => { - const barricade = createBarricade([ - RecentApiRequestConfig, - SearchApiRequestConfig, - ]); - barricade.start(); // We can all call this like - getBarricadeInstance()?.start(); + const barricade = createBarricade([RecentApiRequestConfig]); + barricade.start(); // We can also call this like - getBarricadeInstance()?.start(); + getBarricadeInstance()?.registerRequest(SearchApiRequestConfig); }; diff --git a/src/__tests__/components/Footer.test.tsx b/src/__tests__/components/Footer.test.tsx index 528caaf..10dd650 100644 --- a/src/__tests__/components/Footer.test.tsx +++ b/src/__tests__/components/Footer.test.tsx @@ -3,62 +3,33 @@ import { TouchableOpacity } from 'react-native'; import renderer, { act } from 'react-test-renderer'; import { Footer } from '../../components/Footer'; -import { Barricade } from '../../network/barricade'; -import { - firstApiConfig, - secondApiConfig, - thirdApiConfig, -} from '../data/barricade-test-data'; - -let barricade: Barricade; -beforeEach(() => { - barricade = new Barricade([firstApiConfig, secondApiConfig, thirdApiConfig]); - barricade.start(); -}); - -afterEach(() => { - jest.clearAllMocks(); - barricade.shutdown(); -}); - -describe('given that Barricade is enabled,', () => { - test('should match Footer snapshot', () => { - const tree = renderer.create(<Footer barricade={barricade} />); - expect(tree).toMatchSnapshot(); - expect.assertions(1); - - barricade.shutdown(); - }); - - test('when "Disable barricade" button is pressed, should disable barricade', async () => { - barricade.shutdown = jest.fn(); - const tree = renderer.create(<Footer barricade={barricade} />); - const instance = tree.root; - await act(() => instance.findByType(TouchableOpacity).props.onPress()); - - expect(barricade.shutdown).toHaveBeenCalled(); - expect.assertions(1); - }); +import { Strings } from '../../constants'; +import { LightThemeColors } from '../../theme/colors'; + +test('should match Footer snapshot', () => { + const tree = renderer.create( + <Footer + title={Strings.DisableBarricade} + titleStyle={{ color: LightThemeColors.error }} + onPress={jest.fn} + />, + ); + expect(tree).toMatchSnapshot(); + expect.assertions(1); }); -describe('given that Barricade is disabled,', () => { - test('given that Barricade is disabled, should match Footer snapshot', () => { - barricade.shutdown(); - - const tree = renderer.create(<Footer barricade={barricade} />); - expect(tree).toMatchSnapshot(); - - expect.assertions(1); - }); - - test('when "Enable barricade" button is pressed, should enable barricade', async () => { - barricade.shutdown(); - barricade.start = jest.fn(); - const tree = renderer.create(<Footer barricade={barricade} />); - const instance = tree.root; - await act(() => instance.findByType(TouchableOpacity).props.onPress()); - - expect(barricade.start).toHaveBeenCalledTimes(1); - expect.assertions(1); - }); +test('given that onPress is passed to Footer, when its pressed should call the onPress prop', async () => { + const onPress = jest.fn(); + const tree = renderer.create( + <Footer + onPress={onPress} + title={Strings.DisableBarricade} + titleStyle={{ color: LightThemeColors.error }} + />, + ); + const instance = tree.root; + await act(() => instance.findByType(TouchableOpacity).props.onPress()); + + expect(onPress).toHaveBeenCalled(); + expect.assertions(1); }); diff --git a/src/__tests__/components/RequestDetail.test.tsx b/src/__tests__/components/RequestDetail.test.tsx index 87ce305..6c5d2dc 100644 --- a/src/__tests__/components/RequestDetail.test.tsx +++ b/src/__tests__/components/RequestDetail.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { TouchableOpacity } from 'react-native'; import renderer, { act } from 'react-test-renderer'; +import { Footer } from '../../components/Footer'; import { RequestDetail } from '../../components/RequestDetail'; import { Barricade } from '../../network/barricade'; import { @@ -21,16 +21,80 @@ afterEach(() => { barricade.shutdown(); }); -test('should match RequestDetail snapshot', () => { - const tree = renderer.create( - <RequestDetail - barricade={barricade} - selectedListItemIndex={0} - onBackPressed={jest.fn} - />, - ); - expect(tree).toMatchSnapshot(); - expect.assertions(1); +describe('given that API is enabled,', () => { + test('should match snapshot', () => { + const selectedIndex = 0; + barricade.requestConfig[selectedIndex].disabled = false; + + const tree = renderer.create( + <RequestDetail + barricade={barricade} + selectedListItemIndex={selectedIndex} + onBackPressed={jest.fn} + />, + ); + + expect(tree).toMatchSnapshot(); + expect.assertions(1); + }); + + test('when "Disable API Mock" button is pressed, should disable API for mocking', async () => { + const selectedIndex = 0; + barricade.requestConfig[selectedIndex].disabled = false; + + const tree = renderer.create( + <RequestDetail + barricade={barricade} + selectedListItemIndex={selectedIndex} + onBackPressed={jest.fn} + />, + ); + const instance = tree.root; + await act(async () => { + await instance.findByType(Footer).props.onPress(); + }); + + expect(barricade.requestConfig[selectedIndex].disabled).toBe(true); + expect.assertions(1); + }); +}); + +describe('given that API is disabled,', () => { + test('should match snapshot', () => { + const selectedIndex = 0; + barricade.requestConfig[selectedIndex].disabled = true; + + const tree = renderer.create( + <RequestDetail + barricade={barricade} + selectedListItemIndex={selectedIndex} + onBackPressed={jest.fn} + />, + ); + + expect(tree).toMatchSnapshot(); + expect.assertions(1); + }); + + test('when "Enable API Mock" button is pressed, should enable API for mocking', async () => { + const selectedIndex = 0; + barricade.requestConfig[selectedIndex].disabled = true; + + const tree = renderer.create( + <RequestDetail + barricade={barricade} + selectedListItemIndex={selectedIndex} + onBackPressed={jest.fn} + />, + ); + const instance = tree.root; + await act(async () => { + await instance.findByType(Footer).props.onPress(); + }); + + expect(barricade.requestConfig[selectedIndex].disabled).toBe(false); + expect.assertions(1); + }); }); test('when an selected list item is pressed, should select list item', async () => { @@ -42,7 +106,13 @@ test('when an selected list item is pressed, should select list item', async () />, ); const instance = tree.root; - await act(() => instance.findAllByType(TouchableOpacity)[1].props.onPress()); + await act(() => { + const findAllByTestID = instance.findAll( + el => el.props.testID === 'responseListItem0', + ); + + findAllByTestID[0].props.onPress(); + }); expect(barricade.requestConfig[0].responseHandler[0].isSelected).toBe(true); expect.assertions(1); @@ -57,7 +127,13 @@ test('when an unselected list item is pressed, should select list item', async ( />, ); const instance = tree.root; - await act(() => instance.findAllByType(TouchableOpacity)[2].props.onPress()); + await act(() => { + const findAllByTestID = instance.findAll( + el => el.props.testID === 'responseListItem1', + ); + + findAllByTestID[0].props.onPress(); + }); expect(barricade.requestConfig[0].responseHandler[1].isSelected).toBe(true); expect.assertions(1); diff --git a/src/__tests__/components/RequestList.test.tsx b/src/__tests__/components/RequestList.test.tsx index feaab44..9be17f0 100644 --- a/src/__tests__/components/RequestList.test.tsx +++ b/src/__tests__/components/RequestList.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import renderer, { act } from 'react-test-renderer'; +import { Footer } from '../../components/Footer'; import { Header } from '../../components/Header'; import { RequestList } from '../../components/RequestList'; import { Barricade } from '../../network/barricade'; @@ -20,7 +21,20 @@ afterEach(() => { barricade.shutdown(); }); -test('should match RequestList snapshot', () => { +test('given that all APIs are enabled, should match RequestList snapshot', () => { + const tree = renderer.create( + <RequestList + barricade={barricade} + onDonePressed={jest.fn} + onListItemPressed={jest.fn} + />, + ); + expect(tree).toMatchSnapshot(); + expect.assertions(1); +}); + +test('given that one API is disabled, should match RequestList snapshot', () => { + barricade.requestConfig[0].disabled = true; const tree = renderer.create( <RequestList barricade={barricade} @@ -80,7 +94,7 @@ test('when a list item is pressed, should call onListItemPressed', async () => { const instance = tree.root; await act(() => { const findAllByTestID = instance.findAll( - el => el.props.testID === 'listItem0', + el => el.props.testID === 'requestListItem0', ); findAllByTestID[0].props.onPress(); @@ -89,3 +103,70 @@ test('when a list item is pressed, should call onListItemPressed', async () => { expect(onListItemPressed).toHaveBeenCalled(); expect.assertions(1); }); + +describe('given that Barricade is enabled,', () => { + test('should match snapshot', () => { + const tree = renderer.create( + <RequestList + barricade={barricade} + onDonePressed={jest.fn} + onListItemPressed={jest.fn} + />, + ); + expect(tree).toMatchSnapshot(); + expect.assertions(1); + }); + + test('when "Disable barricade" button is pressed, should disable barricade', async () => { + barricade.shutdown = jest.fn(); + const tree = renderer.create( + <RequestList + barricade={barricade} + onDonePressed={jest.fn} + onListItemPressed={jest.fn} + />, + ); + const instance = tree.root; + await act(async () => { + await instance.findByType(Footer).props.onPress(); + }); + + expect(barricade.shutdown).toHaveBeenCalled(); + expect.assertions(1); + }); +}); + +describe('given that Barricade is disabled,', () => { + test('should match snapshot', () => { + barricade.shutdown(); + const tree = renderer.create( + <RequestList + barricade={barricade} + onDonePressed={jest.fn} + onListItemPressed={jest.fn} + />, + ); + + expect(tree).toMatchSnapshot(); + expect.assertions(1); + }); + + test('when "Enable barricade" button is pressed, should enable barricade', async () => { + barricade.shutdown(); + barricade.start = jest.fn(); + const tree = renderer.create( + <RequestList + barricade={barricade} + onDonePressed={jest.fn} + onListItemPressed={jest.fn} + />, + ); + const instance = tree.root; + await act(async () => { + await instance.findByType(Footer).props.onPress(); + }); + + expect(barricade.start).toHaveBeenCalledTimes(1); + expect.assertions(1); + }); +}); diff --git a/src/__tests__/components/__snapshots__/BarricadeView.test.tsx.snap b/src/__tests__/components/__snapshots__/BarricadeView.test.tsx.snap index 1ee2e2d..59206bf 100644 --- a/src/__tests__/components/__snapshots__/BarricadeView.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/BarricadeView.test.tsx.snap @@ -221,6 +221,7 @@ exports[`given that theme is dark and detail view is selected, should match Barr "paddingVertical": 19.714285714285715, } } + testID="responseListItem0" > <Text style={ @@ -282,6 +283,7 @@ exports[`given that theme is dark and detail view is selected, should match Barr "paddingVertical": 19.714285714285715, } } + testID="responseListItem1" > <Text style={ @@ -304,6 +306,61 @@ exports[`given that theme is dark and detail view is selected, should match Barr </View> </View> </RCTScrollView> + <View + style={ + Array [ + Object { + "borderTopWidth": 1, + "height": 50, + }, + Object { + "backgroundColor": "#181818", + }, + Object { + "borderColor": "#69696D", + }, + ] + } + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "alignItems": "center", + "flex": 1, + "justifyContent": "center", + "marginHorizontal": 24, + "opacity": 1, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 36, + "textAlign": "center", + }, + Object { + "color": "#EE0003", + }, + ] + } + > + Disable API Mock + </Text> + </View> + </View> </View> </RCTSafeAreaView> </Modal> @@ -597,7 +654,7 @@ exports[`given that theme is dark and list view is selected, should match Barric "paddingVertical": 19.714285714285715, } } - testID="listItem0" + testID="requestListItem0" > <Text style={ @@ -632,8 +689,21 @@ exports[`given that theme is dark and list view is selected, should match Barric ] } > - Success - + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -646,7 +716,7 @@ exports[`given that theme is dark and list view is selected, should match Barric ] } > - › + › </Text> </Text> </View> @@ -678,7 +748,7 @@ exports[`given that theme is dark and list view is selected, should match Barric "paddingVertical": 19.714285714285715, } } - testID="listItem1" + testID="requestListItem1" > <Text style={ @@ -713,8 +783,21 @@ exports[`given that theme is dark and list view is selected, should match Barric ] } > - Error - + Error + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -727,7 +810,7 @@ exports[`given that theme is dark and list view is selected, should match Barric ] } > - › + › </Text> </Text> </View> @@ -759,7 +842,7 @@ exports[`given that theme is dark and list view is selected, should match Barric "paddingVertical": 19.714285714285715, } } - testID="listItem2" + testID="requestListItem2" > <Text style={ @@ -794,8 +877,21 @@ exports[`given that theme is dark and list view is selected, should match Barric ] } > - Success - + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -808,7 +904,7 @@ exports[`given that theme is dark and list view is selected, should match Barric ] } > - › + › </Text> </Text> </View> @@ -1072,6 +1168,7 @@ exports[`given that theme is light and detail view is selected, should match Bar "paddingVertical": 19.714285714285715, } } + testID="responseListItem0" > <Text style={ @@ -1133,6 +1230,7 @@ exports[`given that theme is light and detail view is selected, should match Bar "paddingVertical": 19.714285714285715, } } + testID="responseListItem1" > <Text style={ @@ -1155,6 +1253,61 @@ exports[`given that theme is light and detail view is selected, should match Bar </View> </View> </RCTScrollView> + <View + style={ + Array [ + Object { + "borderTopWidth": 1, + "height": 50, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "alignItems": "center", + "flex": 1, + "justifyContent": "center", + "marginHorizontal": 24, + "opacity": 1, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 36, + "textAlign": "center", + }, + Object { + "color": "#EE0003", + }, + ] + } + > + Disable API Mock + </Text> + </View> + </View> </View> </RCTSafeAreaView> </Modal> @@ -1448,7 +1601,7 @@ exports[`given that theme is light and list view is selected, should match Barri "paddingVertical": 19.714285714285715, } } - testID="listItem0" + testID="requestListItem0" > <Text style={ @@ -1483,8 +1636,21 @@ exports[`given that theme is light and list view is selected, should match Barri ] } > - Success - + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -1497,7 +1663,7 @@ exports[`given that theme is light and list view is selected, should match Barri ] } > - › + › </Text> </Text> </View> @@ -1529,7 +1695,7 @@ exports[`given that theme is light and list view is selected, should match Barri "paddingVertical": 19.714285714285715, } } - testID="listItem1" + testID="requestListItem1" > <Text style={ @@ -1564,8 +1730,21 @@ exports[`given that theme is light and list view is selected, should match Barri ] } > - Error - + Error + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -1578,7 +1757,7 @@ exports[`given that theme is light and list view is selected, should match Barri ] } > - › + › </Text> </Text> </View> @@ -1610,7 +1789,7 @@ exports[`given that theme is light and list view is selected, should match Barri "paddingVertical": 19.714285714285715, } } - testID="listItem2" + testID="requestListItem2" > <Text style={ @@ -1645,8 +1824,21 @@ exports[`given that theme is light and list view is selected, should match Barri ] } > - Success - + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -1659,7 +1851,7 @@ exports[`given that theme is light and list view is selected, should match Barri ] } > - › + › </Text> </Text> </View> diff --git a/src/__tests__/components/__snapshots__/Footer.test.tsx.snap b/src/__tests__/components/__snapshots__/Footer.test.tsx.snap index 1e06f26..1709ce6 100644 --- a/src/__tests__/components/__snapshots__/Footer.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/Footer.test.tsx.snap @@ -1,64 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`given that Barricade is disabled, given that Barricade is disabled, should match Footer snapshot 1`] = ` -<View - style={ - Array [ - Object { - "borderTopWidth": 1, - "height": 50, - }, - Object { - "backgroundColor": "#EBEBEB", - }, - Object { - "borderColor": "#DEDEDE", - }, - ] - } -> - <View - accessible={true} - collapsable={false} - focusable={true} - onClick={[Function]} - onResponderGrant={[Function]} - onResponderMove={[Function]} - onResponderRelease={[Function]} - onResponderTerminate={[Function]} - onResponderTerminationRequest={[Function]} - onStartShouldSetResponder={[Function]} - style={ - Object { - "alignItems": "center", - "flex": 1, - "justifyContent": "center", - "marginHorizontal": 24, - "opacity": 1, - } - } - > - <Text - numberOfLines={1} - style={ - Array [ - Object { - "fontSize": 36, - "textAlign": "center", - }, - Object { - "color": "#007aa3", - }, - ] - } - > - Enable Barricade - </Text> - </View> -</View> -`; - -exports[`given that Barricade is enabled, should match Footer snapshot 1`] = ` +exports[`should match Footer snapshot 1`] = ` <View style={ Array [ diff --git a/src/__tests__/components/__snapshots__/RequestDetail.test.tsx.snap b/src/__tests__/components/__snapshots__/RequestDetail.test.tsx.snap index e665e23..0a4ce6b 100644 --- a/src/__tests__/components/__snapshots__/RequestDetail.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/RequestDetail.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should match RequestDetail snapshot 1`] = ` +exports[`given that API is disabled, should match snapshot 1`] = ` <View style={ Array [ @@ -177,6 +177,7 @@ exports[`should match RequestDetail snapshot 1`] = ` "paddingVertical": 19.714285714285715, } } + testID="responseListItem0" > <Text style={ @@ -238,6 +239,7 @@ exports[`should match RequestDetail snapshot 1`] = ` "paddingVertical": 19.714285714285715, } } + testID="responseListItem1" > <Text style={ @@ -260,5 +262,380 @@ exports[`should match RequestDetail snapshot 1`] = ` </View> </View> </RCTScrollView> + <View + style={ + Array [ + Object { + "borderTopWidth": 1, + "height": 50, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "alignItems": "center", + "flex": 1, + "justifyContent": "center", + "marginHorizontal": 24, + "opacity": 1, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 36, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Enable API Mock + </Text> + </View> + </View> +</View> +`; + +exports[`given that API is enabled, should match snapshot 1`] = ` +<View + style={ + Array [ + Object { + "flex": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } +> + <View + style={ + Array [ + Object { + "borderBottomWidth": 1, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + style={ + Object { + "alignItems": "center", + "flexDirection": "row", + "height": 82.14285714285714, + "marginHorizontal": 24, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 40, + "fontWeight": "bold", + "left": 0, + "marginHorizontal": 140, + "position": "absolute", + "right": 0, + "textAlign": "center", + }, + Object { + "color": "#181818", + }, + ] + } + > + First API + </Text> + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "marginRight": 20, + "opacity": 1, + } + } + > + <Text + style={ + Array [ + Object { + "fontSize": 36, + "margin": 16, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + ‹ Back + </Text> + </View> + <View + style={ + Object { + "flex": 1, + } + } + /> + </View> + </View> + <RCTScrollView + contentContainerStyle={ + Array [ + Object { + "flexGrow": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } + data={ + Array [ + Object { + "handler": [Function], + "isSelected": true, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": false, + "label": "Error", + }, + ] + } + getItem={[Function]} + getItemCount={[Function]} + keyExtractor={[Function]} + onContentSizeChange={[Function]} + onLayout={[Function]} + onMomentumScrollBegin={[Function]} + onMomentumScrollEnd={[Function]} + onScroll={[Function]} + onScrollBeginDrag={[Function]} + onScrollEndDrag={[Function]} + removeClippedSubviews={false} + renderItem={[Function]} + scrollEventThrottle={50} + stickyHeaderIndices={Array []} + style={ + Object { + "flexGrow": 1, + } + } + viewabilityConfigCallbackPairs={Array []} + > + <View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="responseListItem0" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "fontSize": 36, + "fontWeight": "400", + "marginEnd": 20, + }, + Object { + "color": "#181818", + }, + ] + } + > + Success + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#007aa3", + }, + ] + } + > + ✓ + </Text> + </View> + </View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="responseListItem1" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "fontSize": 36, + "fontWeight": "400", + "marginEnd": 20, + }, + Object { + "color": "#181818", + }, + ] + } + > + Error + </Text> + </View> + </View> + </View> + </RCTScrollView> + <View + style={ + Array [ + Object { + "borderTopWidth": 1, + "height": 50, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "alignItems": "center", + "flex": 1, + "justifyContent": "center", + "marginHorizontal": 24, + "opacity": 1, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 36, + "textAlign": "center", + }, + Object { + "color": "#EE0003", + }, + ] + } + > + Disable API Mock + </Text> + </View> + </View> </View> `; diff --git a/src/__tests__/components/__snapshots__/RequestList.test.tsx.snap b/src/__tests__/components/__snapshots__/RequestList.test.tsx.snap index d04c05e..04456cc 100644 --- a/src/__tests__/components/__snapshots__/RequestList.test.tsx.snap +++ b/src/__tests__/components/__snapshots__/RequestList.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should match RequestList snapshot 1`] = ` +exports[`given that Barricade is disabled, should match snapshot 1`] = ` <View style={ Array [ @@ -153,6 +153,7 @@ exports[`should match RequestList snapshot 1`] = ` Array [ Object { "delay": 2000, + "disabled": true, "label": "First API", "method": "GET", "pathEvaluation": Object { @@ -268,7 +269,7 @@ exports[`should match RequestList snapshot 1`] = ` "paddingVertical": 19.714285714285715, } } - testID="listItem0" + testID="requestListItem0" > <Text style={ @@ -303,8 +304,1772 @@ exports[`should match RequestList snapshot 1`] = ` ] } > - Success - + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#EE0003", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem1" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + Second API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Error + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem2" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + Third API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + </View> + </RCTScrollView> + <View + style={ + Array [ + Object { + "borderTopWidth": 1, + "height": 50, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "alignItems": "center", + "flex": 1, + "justifyContent": "center", + "marginHorizontal": 24, + "opacity": 1, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 36, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Enable Barricade + </Text> + </View> + </View> +</View> +`; + +exports[`given that Barricade is enabled, should match snapshot 1`] = ` +<View + style={ + Array [ + Object { + "flex": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } +> + <View + style={ + Array [ + Object { + "borderBottomWidth": 1, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + style={ + Object { + "alignItems": "center", + "flexDirection": "row", + "height": 82.14285714285714, + "marginHorizontal": 24, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 40, + "fontWeight": "bold", + "left": 0, + "marginHorizontal": 140, + "position": "absolute", + "right": 0, + "textAlign": "center", + }, + Object { + "color": "#181818", + }, + ] + } + > + Barricade + </Text> + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "marginRight": 20, + "opacity": 1, + } + } + > + <Text + style={ + Array [ + Object { + "fontSize": 36, + "margin": 16, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Reset + </Text> + </View> + <View + style={ + Object { + "flex": 1, + } + } + /> + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "marginLeft": 20, + "opacity": 1, + } + } + > + <Text + style={ + Array [ + Object { + "fontSize": 36, + "margin": 16, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Done + </Text> + </View> + </View> + </View> + <RCTScrollView + contentContainerStyle={ + Array [ + Object { + "flexGrow": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } + data={ + Array [ + Object { + "delay": 2000, + "disabled": true, + "label": "First API", + "method": "GET", + "pathEvaluation": Object { + "path": "/first/api", + "type": 2, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": true, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": false, + "label": "Error", + }, + ], + "selectedResponseLabel": "Success", + }, + Object { + "label": "Second API", + "method": "POST", + "pathEvaluation": Object { + "path": "/second/api", + "type": 1, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": false, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": true, + "label": "Error", + }, + ], + "selectedResponseLabel": "Error", + }, + Object { + "label": "Third API", + "method": "POST", + "pathEvaluation": Object { + "callback": [Function], + "path": "/third/api", + "type": 0, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": true, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": false, + "label": "Error", + }, + ], + "selectedResponseLabel": "Success", + }, + ] + } + extraData={0} + getItem={[Function]} + getItemCount={[Function]} + keyExtractor={[Function]} + onContentSizeChange={[Function]} + onLayout={[Function]} + onMomentumScrollBegin={[Function]} + onMomentumScrollEnd={[Function]} + onScroll={[Function]} + onScrollBeginDrag={[Function]} + onScrollEndDrag={[Function]} + removeClippedSubviews={false} + renderItem={[Function]} + scrollEventThrottle={50} + stickyHeaderIndices={Array []} + style={ + Object { + "flexGrow": 1, + } + } + viewabilityConfigCallbackPairs={Array []} + > + <View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem0" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + First API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#EE0003", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem1" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + Second API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Error + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem2" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + Third API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + </View> + </RCTScrollView> + <View + style={ + Array [ + Object { + "borderTopWidth": 1, + "height": 50, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "alignItems": "center", + "flex": 1, + "justifyContent": "center", + "marginHorizontal": 24, + "opacity": 1, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 36, + "textAlign": "center", + }, + Object { + "color": "#EE0003", + }, + ] + } + > + Disable Barricade + </Text> + </View> + </View> +</View> +`; + +exports[`given that all APIs are enabled, should match RequestList snapshot 1`] = ` +<View + style={ + Array [ + Object { + "flex": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } +> + <View + style={ + Array [ + Object { + "borderBottomWidth": 1, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + style={ + Object { + "alignItems": "center", + "flexDirection": "row", + "height": 82.14285714285714, + "marginHorizontal": 24, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 40, + "fontWeight": "bold", + "left": 0, + "marginHorizontal": 140, + "position": "absolute", + "right": 0, + "textAlign": "center", + }, + Object { + "color": "#181818", + }, + ] + } + > + Barricade + </Text> + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "marginRight": 20, + "opacity": 1, + } + } + > + <Text + style={ + Array [ + Object { + "fontSize": 36, + "margin": 16, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Reset + </Text> + </View> + <View + style={ + Object { + "flex": 1, + } + } + /> + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "marginLeft": 20, + "opacity": 1, + } + } + > + <Text + style={ + Array [ + Object { + "fontSize": 36, + "margin": 16, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Done + </Text> + </View> + </View> + </View> + <RCTScrollView + contentContainerStyle={ + Array [ + Object { + "flexGrow": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } + data={ + Array [ + Object { + "delay": 2000, + "label": "First API", + "method": "GET", + "pathEvaluation": Object { + "path": "/first/api", + "type": 2, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": true, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": false, + "label": "Error", + }, + ], + "selectedResponseLabel": "Success", + }, + Object { + "label": "Second API", + "method": "POST", + "pathEvaluation": Object { + "path": "/second/api", + "type": 1, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": false, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": true, + "label": "Error", + }, + ], + "selectedResponseLabel": "Error", + }, + Object { + "label": "Third API", + "method": "POST", + "pathEvaluation": Object { + "callback": [Function], + "path": "/third/api", + "type": 0, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": true, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": false, + "label": "Error", + }, + ], + "selectedResponseLabel": "Success", + }, + ] + } + extraData={0} + getItem={[Function]} + getItemCount={[Function]} + keyExtractor={[Function]} + onContentSizeChange={[Function]} + onLayout={[Function]} + onMomentumScrollBegin={[Function]} + onMomentumScrollEnd={[Function]} + onScroll={[Function]} + onScrollBeginDrag={[Function]} + onScrollEndDrag={[Function]} + removeClippedSubviews={false} + renderItem={[Function]} + scrollEventThrottle={50} + stickyHeaderIndices={Array []} + style={ + Object { + "flexGrow": 1, + } + } + viewabilityConfigCallbackPairs={Array []} + > + <View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem0" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + First API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem1" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + Second API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Error + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem2" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + Third API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> + <Text + style={ + Array [ + Object { + "fontSize": 36, + }, + Object { + "color": "#69696D", + }, + ] + } + > + › + </Text> + </Text> + </View> + </View> + </View> + </RCTScrollView> + <View + style={ + Array [ + Object { + "borderTopWidth": 1, + "height": 50, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "alignItems": "center", + "flex": 1, + "justifyContent": "center", + "marginHorizontal": 24, + "opacity": 1, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 36, + "textAlign": "center", + }, + Object { + "color": "#EE0003", + }, + ] + } + > + Disable Barricade + </Text> + </View> + </View> +</View> +`; + +exports[`given that one API is disabled, should match RequestList snapshot 1`] = ` +<View + style={ + Array [ + Object { + "flex": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } +> + <View + style={ + Array [ + Object { + "borderBottomWidth": 1, + }, + Object { + "backgroundColor": "#EBEBEB", + }, + Object { + "borderColor": "#DEDEDE", + }, + ] + } + > + <View + style={ + Object { + "alignItems": "center", + "flexDirection": "row", + "height": 82.14285714285714, + "marginHorizontal": 24, + } + } + > + <Text + numberOfLines={1} + style={ + Array [ + Object { + "fontSize": 40, + "fontWeight": "bold", + "left": 0, + "marginHorizontal": 140, + "position": "absolute", + "right": 0, + "textAlign": "center", + }, + Object { + "color": "#181818", + }, + ] + } + > + Barricade + </Text> + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "marginRight": 20, + "opacity": 1, + } + } + > + <Text + style={ + Array [ + Object { + "fontSize": 36, + "margin": 16, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Reset + </Text> + </View> + <View + style={ + Object { + "flex": 1, + } + } + /> + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "marginLeft": 20, + "opacity": 1, + } + } + > + <Text + style={ + Array [ + Object { + "fontSize": 36, + "margin": 16, + "textAlign": "center", + }, + Object { + "color": "#007aa3", + }, + ] + } + > + Done + </Text> + </View> + </View> + </View> + <RCTScrollView + contentContainerStyle={ + Array [ + Object { + "flexGrow": 1, + }, + Object { + "backgroundColor": "#ffffff", + }, + ] + } + data={ + Array [ + Object { + "delay": 2000, + "disabled": true, + "label": "First API", + "method": "GET", + "pathEvaluation": Object { + "path": "/first/api", + "type": 2, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": true, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": false, + "label": "Error", + }, + ], + "selectedResponseLabel": "Success", + }, + Object { + "label": "Second API", + "method": "POST", + "pathEvaluation": Object { + "path": "/second/api", + "type": 1, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": false, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": true, + "label": "Error", + }, + ], + "selectedResponseLabel": "Error", + }, + Object { + "label": "Third API", + "method": "POST", + "pathEvaluation": Object { + "callback": [Function], + "path": "/third/api", + "type": 0, + }, + "responseHandler": Array [ + Object { + "handler": [Function], + "isSelected": true, + "label": "Success", + }, + Object { + "handler": [Function], + "isSelected": false, + "label": "Error", + }, + ], + "selectedResponseLabel": "Success", + }, + ] + } + extraData={0} + getItem={[Function]} + getItemCount={[Function]} + keyExtractor={[Function]} + onContentSizeChange={[Function]} + onLayout={[Function]} + onMomentumScrollBegin={[Function]} + onMomentumScrollEnd={[Function]} + onScroll={[Function]} + onScrollBeginDrag={[Function]} + onScrollEndDrag={[Function]} + removeClippedSubviews={false} + renderItem={[Function]} + scrollEventThrottle={50} + stickyHeaderIndices={Array []} + style={ + Object { + "flexGrow": 1, + } + } + viewabilityConfigCallbackPairs={Array []} + > + <View> + <View + onLayout={[Function]} + style={null} + > + <View + accessible={true} + collapsable={false} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Object { + "borderBottomWidth": 1, + "borderColor": "#DEDEDE", + "flex": 1, + "flexDirection": "row", + "justifyContent": "space-between", + "opacity": 1, + "paddingHorizontal": 40, + "paddingVertical": 19.714285714285715, + } + } + testID="requestListItem0" + > + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 36, + "fontWeight": "400", + "textAlign": "left", + }, + Object { + "color": "#181818", + }, + ] + } + > + First API + </Text> + <Text + style={ + Array [ + Object { + "flex": 1, + "flexWrap": "wrap", + "fontSize": 32, + "textAlign": "right", + }, + Object { + "color": "#69696D", + }, + ] + } + > + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#EE0003", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -317,7 +2082,7 @@ exports[`should match RequestList snapshot 1`] = ` ] } > - › + › </Text> </Text> </View> @@ -349,7 +2114,7 @@ exports[`should match RequestList snapshot 1`] = ` "paddingVertical": 19.714285714285715, } } - testID="listItem1" + testID="requestListItem1" > <Text style={ @@ -384,8 +2149,21 @@ exports[`should match RequestList snapshot 1`] = ` ] } > - Error - + Error + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -398,7 +2176,7 @@ exports[`should match RequestList snapshot 1`] = ` ] } > - › + › </Text> </Text> </View> @@ -430,7 +2208,7 @@ exports[`should match RequestList snapshot 1`] = ` "paddingVertical": 19.714285714285715, } } - testID="listItem2" + testID="requestListItem2" > <Text style={ @@ -465,8 +2243,21 @@ exports[`should match RequestList snapshot 1`] = ` ] } > - Success - + Success + <Text + style={ + Array [ + Object { + "fontSize": 24, + }, + Object { + "color": "#00EA6E", + }, + ] + } + > + ● + </Text> <Text style={ Array [ @@ -479,7 +2270,7 @@ exports[`should match RequestList snapshot 1`] = ` ] } > - › + › </Text> </Text> </View> diff --git a/src/__tests__/network/barricade.test.ts b/src/__tests__/network/barricade.test.ts index 105ffe7..0433bbe 100644 --- a/src/__tests__/network/barricade.test.ts +++ b/src/__tests__/network/barricade.test.ts @@ -1,5 +1,6 @@ jest.useFakeTimers(); +import { Method } from '../../network/barricade.types'; import { HttpStatusCode } from '../../network/http-codes'; import { asyncResponseApiConfig, @@ -15,7 +16,7 @@ import { successResponse, } from '../data/request-test-data'; -test('given that an instance of Barricade is created, should format and set requestConfig', () => { +test('given that an instance of Barricade is created with requestConfig, should format and set requestConfig', () => { const firstConfig = { ...firstApiConfig, responseHandler: [ @@ -38,7 +39,7 @@ test('given that an instance of Barricade is created, should format and set requ { label: 'Error', isSelected: false, handler: jest.fn() }, ], }; - const formattedSeconfConfig = { + const formattedSecondConfig = { ...secondApiConfig, selectedResponseLabel: 'Error', responseHandler: [ @@ -51,12 +52,104 @@ test('given that an instance of Barricade is created, should format and set requ const barricade = new Barricade.Barricade([firstConfig, secondConfig]); expect(JSON.stringify(barricade.requestConfig)).toEqual( - JSON.stringify([formattedFirstConfig, formattedSeconfConfig]), + JSON.stringify([formattedFirstConfig, formattedSecondConfig]), ); expect.assertions(1); }); +test('given that an instance of Barricade is created without requestConfig, requestConfig should be empty', () => { + const Barricade = require('../../network/barricade'); + const barricade = new Barricade.Barricade(); + + expect(barricade.requestConfig).toEqual([]); + + expect.assertions(1); +}); + +test('given that an instance of Barricade is created without requestConfig, when registerRequest is called should format and set requestConfig', () => { + const firstConfig = { + ...firstApiConfig, + responseHandler: [ + { label: 'Success', handler: jest.fn() }, + { label: 'Error', handler: jest.fn() }, + ], + }; + const formattedFirstConfig = { + ...firstApiConfig, + selectedResponseLabel: 'Success', + responseHandler: [ + { label: 'Success', isSelected: true, handler: jest.fn() }, + { label: 'Error', isSelected: false, handler: jest.fn() }, + ], + }; + + const Barricade = require('../../network/barricade'); + const barricade = new Barricade.Barricade(); + barricade.registerRequest(firstConfig); + + expect(JSON.stringify(barricade.requestConfig)).toEqual( + JSON.stringify([formattedFirstConfig]), + ); + expect.assertions(1); +}); + +describe('given that Barricade is created and requestConfig is set,', () => { + const firstConfig = { + ...firstApiConfig, + responseHandler: [ + { label: 'Success', handler: jest.fn() }, + { label: 'Error', handler: jest.fn() }, + ], + }; + const secondConfig = { + ...secondApiConfig, + responseHandler: [ + { label: 'Success', handler: jest.fn() }, + { label: 'Error', isSelected: true, handler: jest.fn() }, + ], + }; + const formattedFirstConfig = { + ...firstApiConfig, + selectedResponseLabel: 'Success', + responseHandler: [ + { label: 'Success', isSelected: true, handler: jest.fn() }, + { label: 'Error', isSelected: false, handler: jest.fn() }, + ], + }; + const formattedSecondConfig = { + ...secondApiConfig, + selectedResponseLabel: 'Error', + responseHandler: [ + { label: 'Success', isSelected: false, handler: jest.fn() }, + { label: 'Error', isSelected: true, handler: jest.fn() }, + ], + }; + let barricade; + beforeEach(() => { + const Barricade = require('../../network/barricade'); + barricade = new Barricade.Barricade([firstConfig, secondConfig]); + }); + + test('when unregisterRequest is called with a config in requestConfig, should remove config from requestConfig', () => { + barricade.unregisterRequest(secondConfig); + + expect(JSON.stringify(barricade.requestConfig)).toEqual( + JSON.stringify([formattedFirstConfig]), + ); + expect.assertions(1); + }); + + test('when unregisterRequest is called with a config not in requestConfig, should not remove config from requestConfig', () => { + barricade.unregisterRequest({ ...secondConfig, method: Method.Put }); + + expect(JSON.stringify(barricade.requestConfig)).toEqual( + JSON.stringify([formattedFirstConfig, formattedSecondConfig]), + ); + expect.assertions(1); + }); +}); + describe('given that start function is called,', () => { let barricade; afterEach(() => { @@ -201,7 +294,7 @@ describe('given that shutdown function is called,', () => { }); }); -test('given that inital requestConfig was updated, should reset requestConfig, when resetRequestConfig is called', () => { +test('given that initial requestConfig was updated, should reset requestConfig, when resetRequestConfig is called', () => { const firstConfig = { ...firstApiConfig, responseHandler: [ @@ -224,7 +317,7 @@ test('given that inital requestConfig was updated, should reset requestConfig, w { label: 'Error', isSelected: false, handler: jest.fn() }, ], }; - const formattedSeconfConfig = { + const formattedSecondConfig = { ...secondApiConfig, selectedResponseLabel: 'Error', responseHandler: [ @@ -244,23 +337,23 @@ test('given that inital requestConfig was updated, should reset requestConfig, w barricade.resetRequestConfig(); expect(JSON.stringify(barricade.requestConfig)).toEqual( - JSON.stringify([formattedFirstConfig, formattedSeconfConfig]), + JSON.stringify([formattedFirstConfig, formattedSecondConfig]), ); expect.assertions(1); }); describe('given that barricade was started and handleRequest is called,', () => { let barricade; - const mockCreateNativeXMLHttpRequest = jest.fn(); + const mockHandleNativeXMLHttpRequest = jest.fn(); beforeEach(() => { jest.resetModules(); const Barricade = require('../../network/barricade'); barricade = new Barricade.Barricade([ - firstApiConfig, - secondApiConfig, - thirdApiConfig, + { ...firstApiConfig }, + { ...secondApiConfig }, + { ...thirdApiConfig }, ]); - barricade.handleNativeXMLHttpRequest = mockCreateNativeXMLHttpRequest; + barricade.handleNativeXMLHttpRequest = mockHandleNativeXMLHttpRequest; barricade.start(); }); @@ -268,12 +361,26 @@ describe('given that barricade was started and handleRequest is called,', () => afterEach(() => { jest.clearAllMocks(); barricade.shutdown(); + barricade = undefined; }); test('when an API which is not in requestConfig is triggered, should trigger API with nativeXMLHttpRequest', () => { barricade.handleRequest(mockApiRequest); - expect(mockCreateNativeXMLHttpRequest).toHaveBeenCalledWith(mockApiRequest); + expect(mockHandleNativeXMLHttpRequest).toHaveBeenCalledWith(mockApiRequest); + expect.assertions(1); + }); + + test('when an API which is in requestConfig which is disabled is triggered, should trigger API with nativeXMLHttpRequest', () => { + const request = getCustomMockApiRequest({ + _method: firstApiConfig.method, + _url: firstApiConfig.pathEvaluation.path, + }); + barricade.requestConfig[0].disabled = true; + + barricade.handleRequest(request); + + expect(mockHandleNativeXMLHttpRequest).toHaveBeenCalledWith(request); expect.assertions(1); }); @@ -286,7 +393,7 @@ describe('given that barricade was started and handleRequest is called,', () => barricade.resolveRequest = jest.fn(); barricade.handleRequest(request); - expect(mockCreateNativeXMLHttpRequest).not.toHaveBeenCalled(); + expect(mockHandleNativeXMLHttpRequest).not.toHaveBeenCalled(); expect(barricade.resolveRequest).toHaveBeenCalledWith( request, successResponse, @@ -303,7 +410,7 @@ describe('given that barricade was started and handleRequest is called,', () => barricade.resolveRequest = jest.fn(); barricade.handleRequest(request); - expect(mockCreateNativeXMLHttpRequest).toHaveBeenCalledWith(request); + expect(mockHandleNativeXMLHttpRequest).toHaveBeenCalledWith(request); expect(barricade.resolveRequest).not.toHaveBeenCalled(); expect.assertions(2); }); @@ -318,7 +425,7 @@ describe('given that barricade was started and handleRequest is called,', () => barricade.resolveRequest = jest.fn(); barricade.handleRequest(request); - expect(mockCreateNativeXMLHttpRequest).not.toHaveBeenCalled(); + expect(mockHandleNativeXMLHttpRequest).not.toHaveBeenCalled(); expect(barricade.resolveRequest).toHaveBeenCalledWith( request, errorResponse, @@ -335,7 +442,7 @@ describe('given that barricade was started and handleRequest is called,', () => barricade.resolveRequest = jest.fn(); barricade.handleRequest(request); - expect(mockCreateNativeXMLHttpRequest).toHaveBeenCalledWith(request); + expect(mockHandleNativeXMLHttpRequest).toHaveBeenCalledWith(request); expect(barricade.resolveRequest).not.toHaveBeenCalled(); expect.assertions(2); }); @@ -354,7 +461,7 @@ describe('given that barricade was started and handleRequest is called,', () => barricade.resolveRequest = jest.fn(); barricade.handleRequest(request); - expect(mockCreateNativeXMLHttpRequest).not.toHaveBeenCalled(); + expect(mockHandleNativeXMLHttpRequest).not.toHaveBeenCalled(); expect(barricade.resolveRequest).toHaveBeenCalledWith( request, successResponse, @@ -375,7 +482,7 @@ describe('given that barricade was started and handleRequest is called,', () => barricade.resolveRequest = jest.fn(); barricade.handleRequest(request); - expect(mockCreateNativeXMLHttpRequest).toHaveBeenCalledWith(request); + expect(mockHandleNativeXMLHttpRequest).toHaveBeenCalledWith(request); expect(barricade.resolveRequest).not.toHaveBeenCalled(); expect.assertions(2); }); @@ -394,7 +501,7 @@ describe('given that barricade was started and handleRequest is called,', () => barricade.resolveRequest = jest.fn(); barricade.handleRequest(request); - expect(mockCreateNativeXMLHttpRequest).not.toHaveBeenCalled(); + expect(mockHandleNativeXMLHttpRequest).not.toHaveBeenCalled(); expect(barricade.resolveRequest).toHaveBeenCalledWith( request, successResponse, @@ -418,7 +525,7 @@ describe('given that barricade was started and handleRequest is called,', () => await jest.advanceTimersByTime(3000); - expect(mockCreateNativeXMLHttpRequest).not.toHaveBeenCalled(); + expect(mockHandleNativeXMLHttpRequest).not.toHaveBeenCalled(); expect(barricade.resolveRequest).toHaveBeenCalledWith( request, successResponse, @@ -441,7 +548,7 @@ describe('given that barricade was started and handleRequest is called,', () => await jest.advanceTimersByTime(3000); - expect(mockCreateNativeXMLHttpRequest).not.toHaveBeenCalled(); + expect(mockHandleNativeXMLHttpRequest).not.toHaveBeenCalled(); expect(barricade.handleMockedXMLHttpRequest).toThrow( new Error( `Barricade intercepted undefined(undefined) API and threw an error - Cannot read properties of undefined (reading 'responseHandler').`, diff --git a/src/components/BarricadeView.tsx b/src/components/BarricadeView.tsx index df198f7..cb0cdae 100644 --- a/src/components/BarricadeView.tsx +++ b/src/components/BarricadeView.tsx @@ -42,7 +42,10 @@ export const BarricadeView = (props: BarricadeViewProps) => { const renderContent = () => { if (!barricade) { return; - } else if (viewType === ViewType.List) { + } else if ( + viewType === ViewType.List || + typeof selectedListItemIndex === 'undefined' + ) { return ( <RequestList barricade={barricade} diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 9e814f8..59c2f67 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,25 +1,25 @@ -import React, { useState } from 'react'; -import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import React from 'react'; +import { + NativeSyntheticEvent, + StyleSheet, + Text, + TextStyle, + TouchableOpacity, + View, +} from 'react-native'; -import { Strings } from '../constants'; -import { Barricade } from '../network'; import { useThemedColor } from '../theme'; import { hScale } from '../utils'; export type FooterProps = { - barricade: Barricade; + onPress?: (event: NativeSyntheticEvent<any>) => void; + title: string; + titleStyle: TextStyle; }; const Footer = (props: FooterProps): JSX.Element => { - const { barricade } = props; + const { onPress, title, titleStyle } = props; const { themeColorStyle } = useThemedColor(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [refreshList, setRefreshList] = useState(0); - - const onButtonPressed = () => { - barricade.running ? barricade.shutdown() : barricade.start(); - setRefreshList(Math.random()); - }; return ( <View @@ -28,16 +28,9 @@ const Footer = (props: FooterProps): JSX.Element => { themeColorStyle.background, themeColorStyle.border, ]}> - <TouchableOpacity onPress={onButtonPressed} style={styles.footer}> - <Text - numberOfLines={1} - style={[ - styles.title, - barricade.running ? themeColorStyle.error : themeColorStyle.primary, - ]}> - {barricade.running - ? Strings.DisableBarricade - : Strings.EnableBarricade} + <TouchableOpacity onPress={onPress} style={styles.footer}> + <Text numberOfLines={1} style={[styles.title, titleStyle]}> + {title} </Text> </TouchableOpacity> </View> diff --git a/src/components/RequestDetail.tsx b/src/components/RequestDetail.tsx index 1e5a38e..457fd9c 100644 --- a/src/components/RequestDetail.tsx +++ b/src/components/RequestDetail.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { FlatList, ListRenderItemInfo, @@ -12,26 +12,37 @@ import { Strings, Unicode } from '../constants'; import { Barricade, ResponseHandler } from '../network'; import { useThemedColor } from '../theme'; import { hScale, vScale } from '../utils'; +import { Footer } from './Footer'; import { Header } from './Header'; type RequestDetailProps = { barricade: Barricade; - selectedListItemIndex?: number; + selectedListItemIndex: number; onBackPressed: () => void; }; const RequestDetail = (props: RequestDetailProps): JSX.Element => { const { barricade, selectedListItemIndex, onBackPressed } = props; const { themeColorStyle } = useThemedColor(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [refreshList, setRefreshList] = useState(0); + + const onFooterPressed = () => { + barricade.requestConfig[selectedListItemIndex].disabled = + !barricade.requestConfig[selectedListItemIndex].disabled; + setRefreshList(Math.random()); + onBackPressed(); + }; const onDetailItemPressed = (item: ResponseHandler, index: number) => { if (!item.isSelected) { - const requestConfig = barricade.requestConfig[selectedListItemIndex!]; + const requestConfig = barricade.requestConfig[selectedListItemIndex]; requestConfig.responseHandler.map((responseHandler, i) => { responseHandler.isSelected = index === i; return responseHandler; }); requestConfig.selectedResponseLabel = item.label; + requestConfig.disabled = false; onBackPressed(); } }; @@ -42,6 +53,7 @@ const RequestDetail = (props: RequestDetailProps): JSX.Element => { }: ListRenderItemInfo<ResponseHandler>) => { return ( <TouchableOpacity + testID={`responseListItem${index}`} style={[styles.listItemContainer, themeColorStyle.border]} onPress={() => onDetailItemPressed(item, index)}> <Text style={[styles.label, themeColorStyle.textDark]}> @@ -63,14 +75,27 @@ const RequestDetail = (props: RequestDetailProps): JSX.Element => { title: `${Unicode.ChevronLeft} ${Strings.Back}`, onPress: onBackPressed, }} - title={barricade?.requestConfig[selectedListItemIndex!]?.label ?? ''} + title={barricade.requestConfig[selectedListItemIndex]?.label} /> <FlatList style={styles.listContainer} contentContainerStyle={[styles.listContainer, themeColorStyle.surface]} - data={barricade?.requestConfig[selectedListItemIndex!]?.responseHandler} + data={barricade.requestConfig[selectedListItemIndex]?.responseHandler} renderItem={renderDetailItem} /> + <Footer + onPress={onFooterPressed} + title={ + barricade.requestConfig[selectedListItemIndex]?.disabled + ? Strings.EnableAPI + : Strings.DisableAPI + } + titleStyle={ + barricade.requestConfig[selectedListItemIndex]?.disabled + ? themeColorStyle.primary + : themeColorStyle.error + } + /> </View> ); }; diff --git a/src/components/RequestList.tsx b/src/components/RequestList.tsx index 8f2ef3b..77e5501 100644 --- a/src/components/RequestList.tsx +++ b/src/components/RequestList.tsx @@ -28,28 +28,46 @@ const RequestList = (props: RequestListProps): JSX.Element => { const [refreshList, setRefreshList] = useState(0); const onResetPressed = () => { - barricade?.resetRequestConfig(); + barricade.resetRequestConfig(); setRefreshList(Math.random()); }; + const onFooterPressed = () => { + barricade.running ? barricade.shutdown() : barricade.start(); + setRefreshList(Math.random()); + }; + + const renderSelectedResponse = (item: RequestConfigForLib) => { + return ( + <Text style={[styles.value, themeColorStyle.textLight]}> + {`${item.selectedResponseLabel} `} + <Text + style={[ + styles.circleIcon, + item.disabled ? themeColorStyle.error : themeColorStyle.success, + ]}> + {Unicode.Circle} + </Text> + <Text style={[styles.rightIcon, themeColorStyle.textLight]}> + {` ${Unicode.ChevronRight}`} + </Text> + </Text> + ); + }; + const renderListItem = ({ item, index, }: ListRenderItemInfo<RequestConfigForLib>) => { return ( <TouchableOpacity - testID={`listItem${index}`} + testID={`requestListItem${index}`} style={[styles.listItemContainer, themeColorStyle.border]} onPress={() => onListItemPressed(index)}> <Text style={[styles.label, themeColorStyle.textDark]}> {item.label} </Text> - <Text style={[styles.value, themeColorStyle.textLight]}> - {item.selectedResponseLabel}{' '} - <Text style={[styles.icon, themeColorStyle.textLight]}> - {Unicode.ChevronRight} - </Text> - </Text> + {renderSelectedResponse(item)} </TouchableOpacity> ); }; @@ -64,11 +82,19 @@ const RequestList = (props: RequestListProps): JSX.Element => { <FlatList style={styles.listContainer} contentContainerStyle={[styles.listContainer, themeColorStyle.surface]} - data={barricade?.requestConfig} + data={barricade.requestConfig} renderItem={renderListItem} extraData={refreshList} /> - <Footer barricade={barricade} /> + <Footer + onPress={onFooterPressed} + title={ + barricade.running ? Strings.DisableBarricade : Strings.EnableBarricade + } + titleStyle={ + barricade.running ? themeColorStyle.error : themeColorStyle.primary + } + /> </View> ); }; @@ -99,7 +125,10 @@ const styles = StyleSheet.create({ textAlign: 'right', fontSize: hScale(16), }, - icon: { + circleIcon: { + fontSize: hScale(12), + }, + rightIcon: { fontSize: hScale(18), }, }); diff --git a/src/constants/strings.ts b/src/constants/strings.ts index 5e39cb8..df0f5d7 100644 --- a/src/constants/strings.ts +++ b/src/constants/strings.ts @@ -1,8 +1,11 @@ export const Strings = { Back: 'Back', Barricade: 'Barricade', + DisableAPI: 'Disable API Mock', DisableBarricade: 'Disable Barricade', + Disabled: 'Disabled', Done: 'Done', + EnableAPI: 'Enable API Mock', EnableBarricade: 'Enable Barricade', Reset: 'Reset', }; diff --git a/src/constants/unicode.ts b/src/constants/unicode.ts index 392aafb..c2cdac2 100644 --- a/src/constants/unicode.ts +++ b/src/constants/unicode.ts @@ -2,4 +2,5 @@ export const Unicode = { ChevronLeft: '\u2039', CheckMark: '\u2713', ChevronRight: '\u203A', + Circle: '\u25CF', }; diff --git a/src/index.tsx b/src/index.tsx index 8a4e99d..8dee9b6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -21,10 +21,10 @@ let barricade: Barricade | undefined; /** * Creates an instance of Barricade and initializes the array of RequestConfig passed to the function. - * @param requests : Array of RequestConfig. Each item in this array should contain configuration of each API that you want to mock. + * @param requests : Optional Array of RequestConfig. Each item in this array should contain configuration of each API that you want to mock. * @returns Instance of Barricade that was created. */ -const createBarricade = (requests: RequestConfig[]) => { +const createBarricade = (requests?: RequestConfig[]) => { barricade = new Barricade(requests); if (__DEV__) { DevSettings.addMenuItem(Strings.Barricade, () => { diff --git a/src/network/barricade.ts b/src/network/barricade.ts index 3299a5c..653f4ea 100644 --- a/src/network/barricade.ts +++ b/src/network/barricade.ts @@ -18,7 +18,7 @@ import { interceptor } from './interceptor'; export class Barricade { /** Boolean that indicates whether the Barricade is enabled/disabled. */ running = false; - private readonly _originalRequestConfig: RequestConfigForLib[] = []; + private _originalRequestConfig: RequestConfigForLib[] = []; private _requestConfig: RequestConfigForLib[] = []; private _nativeXMLHttpRequest: typeof XMLHttpRequest; private _nativeFetch: RNFetch.fetch; @@ -26,9 +26,9 @@ export class Barricade { private _nativeRequest: RNFetch.Request; private _nativeResponse: RNFetch.Response; - constructor(requestConfig: RequestConfig[]) { + constructor(requestConfig?: RequestConfig[]) { this.initRequestConfig(requestConfig); - this._originalRequestConfig = ObjectUtils.cloneDeep(this.requestConfig); + this._nativeXMLHttpRequest = global.XMLHttpRequest; this._nativeFetch = global.fetch; this._nativeHeaders = global.Headers; @@ -99,14 +99,17 @@ export class Barricade { /** * Formats the RequestConfig[] to RequestConfigForLib[] and sets it to _requestConfig - * @param requests Array of RequestConfigs passed to be registered while creating an instance of Barricade. + * @param requests Optional Array of RequestConfigs passed to be registered while creating an instance of Barricade. */ - private initRequestConfig(requests: RequestConfig[]) { - const updatedRequestConfig = requests.map<RequestConfig>( - this.getRequestConfigForLib, - ); + private initRequestConfig(requests?: RequestConfig[]) { + if (requests?.length) { + const updatedRequestConfig = requests.map<RequestConfig>( + this.getRequestConfigForLib, + ); - this._requestConfig = updatedRequestConfig; + this._requestConfig = updatedRequestConfig; + this._originalRequestConfig = ObjectUtils.cloneDeep(this.requestConfig); + } } /** @@ -127,7 +130,7 @@ export class Barricade { requestUrl, ); - if (requestConfig) { + if (requestConfig && !requestConfig.disabled) { this.handleMockedXMLHttpRequest( request, requestConfig, @@ -243,6 +246,19 @@ export class Barricade { xhr.send(request._requestBody); } + /** + * Registers an API to be mocked by Barricade. + * @param request RequestConfig data of an API call that needs to be registered for mocking. + */ + registerRequest(request: RequestConfig) { + const updatedRequestConfig = this.getRequestConfigForLib(request); + + this.requestConfig.push(updatedRequestConfig); + this._originalRequestConfig.push( + ObjectUtils.cloneDeep(updatedRequestConfig), + ); + } + /** * Resets selected responses for all the registered request config to its default value. */ @@ -298,4 +314,19 @@ export class Barricade { global.Response = this._nativeResponse; this.running = false; } + + /** + * Unregisters an API from being mocked by Barricade. + * @param request RequestConfig data of an API call that needs to be registered for mocking. + */ + unregisterRequest(request: RequestConfig) { + const index = this.requestConfig.findIndex( + item => item.label === request.label && item.method === request.method, + ); + + if (index > -1) { + this.requestConfig.splice(index, 1); + this._originalRequestConfig.splice(index, 1); + } + } } diff --git a/src/network/barricade.types.ts b/src/network/barricade.types.ts index 2cba902..a6345b3 100644 --- a/src/network/barricade.types.ts +++ b/src/network/barricade.types.ts @@ -35,6 +35,7 @@ export interface RequestConfig { ); responseHandler: ResponseHandler[]; delay?: number; + disabled?: boolean; } export interface RequestConfigForLib extends RequestConfig {