Skip to content

Commit

Permalink
refactor(app): Rework client state for /networking/status endpoint (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous authored and IanLondon committed Oct 16, 2018
1 parent 608d5df commit 51c0aa2
Show file tree
Hide file tree
Showing 6 changed files with 319 additions and 917 deletions.
31 changes: 18 additions & 13 deletions app/src/components/RobotSettings/ConnectivityCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import {connect} from 'react-redux'

import {CONNECTABLE, startDiscovery} from '../../discovery'
import {
fetchNetworkingStatus,
fetchWifiList,
configureWifi,
clearConfigureWifiResponse,
makeGetRobotWifiList,
makeGetRobotWifiConfigure,
type RobotWifiList,
type RobotWifiConfigure,
} from '../../http-api-client'

import {RefreshCard, LabeledValue} from '@opentrons/components'
Expand All @@ -21,16 +20,17 @@ import WifiConnectModal from './WifiConnectModal'

import type {State, Dispatch} from '../../types'
import type {ViewableRobot} from '../../discovery'
import type {FetchWifiListCall, ConfigureWifiCall} from '../../http-api-client'

type OP = {robot: ViewableRobot}

type SP = {|
listRequest: RobotWifiList,
configureRequest: RobotWifiConfigure,
listRequest: FetchWifiListCall,
configureRequest: ConfigureWifiCall,
|}

type DP = {|
fetchList: () => mixed,
refresh: () => mixed,
configure: (?string, ?string) => mixed,
clearSuccessfulConfigure: () => mixed,
clearFailedConfigure: () => mixed,
Expand All @@ -48,12 +48,12 @@ export default connect(

function ConnectivityCard (props: Props) {
const {
fetchList,
refresh,
clearSuccessfulConfigure,
clearFailedConfigure,
configure,
robot: {ip, local, name, status},
listRequest: {inProgress: listInProgress, response: listResponse},
listRequest: {response: listResponse},
configureRequest: {
inProgress: configInProgress,
response: configResponse,
Expand All @@ -76,8 +76,8 @@ function ConnectivityCard (props: Props) {
<React.Fragment>
<RefreshCard
watch={name}
refresh={fetchList}
refreshing={listInProgress || configInProgress}
refresh={refresh}
refreshing={configInProgress}
title={TITLE}
disabled={disabled}
column
Expand Down Expand Up @@ -119,20 +119,25 @@ function makeMapStateToProps () {

function mapDispatchToProps (dispatch: Dispatch, ownProps: OP): DP {
const {robot} = ownProps
const fetchList = () => dispatch(fetchWifiList(robot))
const configure = (ssid, psk) => dispatch(configureWifi(robot, ssid, psk))
const configure = (ssid, psk) => dispatch(configureWifi(robot, {ssid, psk}))
const refresh = () => {
// send these requests simultaneously
dispatch(fetchNetworkingStatus(robot))
dispatch(fetchWifiList(robot))
}

// TODO(mc, 2018-02-26): handle refreshing the list and kicking off dispatch
// more elegantly and closer to the configure response
const clearConfigureAction = clearConfigureWifiResponse(robot)
const clearFailedConfigure = () => dispatch(clearConfigureAction)
const clearSuccessfulConfigure = () =>
fetchList()
Promise.resolve()
.then(refresh)
.then(() => dispatch(startDiscovery()))
.then(() => dispatch(clearConfigureAction))

return {
fetchList,
refresh,
configure,
clearSuccessfulConfigure,
clearFailedConfigure,
Expand Down
177 changes: 177 additions & 0 deletions app/src/http-api-client/__tests__/networking.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// networking api tests
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'

import client from '../client'
import * as networking from '../networking'

jest.mock('../client')

const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)

describe('networking', () => {
beforeEach(() => jest.clearAllMocks())

describe('selector creators', () => {
const SPECS = [
{
name: 'makeGetRobotNetworkingStatus',
selector: networking.makeGetRobotNetworkingStatus,
state: {
api: {
api: {
someName: {'networking/status': {response: {status: 'full'}}},
},
},
},
props: {name: 'someName'},
expected: {response: {status: 'full'}},
},
{
name: 'makeGetRobotWifiList',
selector: networking.makeGetRobotWifiList,
state: {
api: {
api: {
someName: {
'wifi/list': {
inProgress: false,
response: {
list: [
{ssid: 'foo', active: false, signal: 100},
{ssid: 'foo', active: true, signal: 42},
{ssid: 'baz', active: false, signal: 50},
{ssid: 'baz', active: false, signal: 65},
{ssid: 'bar', active: false, signal: 42},
],
},
},
},
},
},
},
props: {name: 'someName'},
expected: {
inProgress: false,
response: {
list: [
{ssid: 'foo', active: true, signal: 42},
{ssid: 'bar', active: false, signal: 42},
{ssid: 'baz', active: false, signal: 65},
],
},
},
},
{
name: 'makeGetRobotWifiConfigure',
selector: networking.makeGetRobotWifiConfigure,
state: {
api: {
api: {
someName: {'wifi/configure': {response: {ssid: 'some-ssid'}}},
},
},
},
props: {name: 'someName'},
expected: {response: {ssid: 'some-ssid'}},
},
]

SPECS.forEach(spec => {
const {name, selector, state, props, expected} = spec

test(`${name} with known robot`, () =>
expect(selector()(state, props)).toEqual(expected))

test(`${name} with unknown robot`, () =>
expect(selector()(state, {name: 'foo'})).toEqual({inProgress: false}))
})
})

test('clearConfigureWifiResponse action creator', () => {
expect(networking.clearConfigureWifiResponse({name: 'foo'})).toEqual({
type: 'api:CLEAR_RESPONSE',
payload: {robot: {name: 'foo'}, path: 'wifi/configure'},
})
})

describe('HTTP request action creators', () => {
let store
let robot

beforeEach(() => {
store = mockStore({})
robot = {name: 'opentrons', ip: '1.2.3.4', port: '1234'}
})

const SPECS = [
{
name: 'fetchNetworkingStatus',
action: networking.fetchNetworkingStatus,
method: 'GET',
path: 'networking/status',
request: null,
success: {status: 'full', interfaces: {}},
failure: {name: 'ResponseError', status: '400', message: 'oh no'},
},
{
name: 'fetchWifiList',
action: networking.fetchWifiList,
method: 'GET',
path: 'wifi/list',
request: null,
success: {list: []},
failure: {name: 'ResponseError', status: '400', message: 'oh no'},
},
{
name: 'configureWifi',
action: networking.configureWifi,
method: 'POST',
path: 'wifi/configure',
request: {ssid: 'some-ssid', psk: 'some-psk'},
success: {ssid: 'some-ssid', message: 'success!'},
failure: {name: 'ResponseError', status: '400', message: 'oh no'},
},
]

SPECS.forEach(spec => {
const {name, action, method, path, request, success, failure} = spec

test(`${name} makes HTTP call`, () => {
client.__setMockResponse(success)
return store
.dispatch(action(robot, request))
.then(() =>
expect(client).toHaveBeenCalledWith(robot, method, path, request)
)
})

test(`${name} handles success`, () => {
const expectedActions = [
{type: 'api:REQUEST', payload: {robot, path, request}},
{type: 'api:SUCCESS', payload: {robot, path, response: success}},
]

client.__setMockResponse(success)

return store
.dispatch(action(robot, request))
.then(() => expect(store.getActions()).toEqual(expectedActions))
})

test(`${name} handles failure`, () => {
const expectedActions = [
{type: 'api:REQUEST', payload: {robot, path, request}},
{type: 'api:FAILURE', payload: {robot, path, error: failure}},
]

client.__setMockError(failure)

return store
.dispatch(action(robot, request))
.then(() => expect(store.getActions()).toEqual(expectedActions))
})
})
})
})
Loading

0 comments on commit 51c0aa2

Please sign in to comment.