Skip to content

Commit

Permalink
Added register & unregister methods to Barricade
Browse files Browse the repository at this point in the history
Added logic to be able to disable individual APIs in Barricade
  • Loading branch information
prajnab-mm committed Jan 25, 2023
1 parent a874b83 commit 279a126
Show file tree
Hide file tree
Showing 26 changed files with 2,898 additions and 254 deletions.
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 |
Expand All @@ -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 |
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
Binary file modified docs/media/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/screenshots/detail.png
Binary file not shown.
Binary file added docs/screenshots/developer-menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/screenshots/list.png
Binary file not shown.
Binary file removed docs/screenshots/menu.png
Binary file not shown.
Binary file added docs/screenshots/request-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/response-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 7 additions & 6 deletions example/mock-server/index.ts
Original file line number Diff line number Diff line change
@@ -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);
};
83 changes: 27 additions & 56 deletions src/__tests__/components/Footer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
102 changes: 89 additions & 13 deletions src/__tests__/components/RequestDetail.test.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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 () => {
Expand All @@ -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);
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 279a126

Please sign in to comment.