Skip to content

Commit 07f2631

Browse files
authored
Merge pull request #15 from opentripplanner/dev
New Release
2 parents f162be7 + 53393d9 commit 07f2631

File tree

9 files changed

+222
-60
lines changed

9 files changed

+222
-60
lines changed

Diff for: __tests__/actions/__snapshots__/api.js.snap

+42-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,48 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`actions > api should make a query to OTP 1`] = `"/api/plan?arriveBy=false&fromPlace=12%2C34&showIntermediateStops=true&toPlace=34%2C12"`;
3+
exports[`actions > api > planTrip should make a query to OTP with customOtpQueryBuilder in state config 1`] = `"/api/plan?from=here&to=there"`;
44

5-
exports[`actions > api should make a query to OTP 2`] = `
5+
exports[`actions > api > planTrip should make a query to OTP with customOtpQueryBuilder in state config 2`] = `
6+
Array [
7+
Array [
8+
Object {
9+
"type": "PLAN_REQUEST",
10+
},
11+
],
12+
Array [
13+
Object {
14+
"payload": Object {
15+
"fake": "response",
16+
},
17+
"type": "PLAN_RESPONSE",
18+
},
19+
],
20+
]
21+
`;
22+
23+
exports[`actions > api > planTrip should make a query to OTP with customOtpQueryBuilder sent to action 1`] = `"/api/plan?from=here&to=there"`;
24+
25+
exports[`actions > api > planTrip should make a query to OTP with customOtpQueryBuilder sent to action 2`] = `
26+
Array [
27+
Array [
28+
Object {
29+
"type": "PLAN_REQUEST",
30+
},
31+
],
32+
Array [
33+
Object {
34+
"payload": Object {
35+
"fake": "response",
36+
},
37+
"type": "PLAN_RESPONSE",
38+
},
39+
],
40+
]
41+
`;
42+
43+
exports[`actions > api > planTrip should make a query to OTP with default settings 1`] = `"/api/plan?arriveBy=false&fromPlace=12%2C34&showIntermediateStops=true&toPlace=34%2C12"`;
44+
45+
exports[`actions > api > planTrip should make a query to OTP with default settings 2`] = `
646
Array [
747
Array [
848
Object {

Diff for: __tests__/actions/api.js

+71-37
Original file line numberDiff line numberDiff line change
@@ -10,48 +10,82 @@ function timeoutPromise (ms) {
1010
})
1111
}
1212

13-
const planTripAction = planTrip()
14-
1513
describe('actions > api', () => {
16-
it('should make a query to OTP', async () => {
17-
nock('http://mock-host.com')
18-
.get(/api\/plan/)
19-
.reply(200, {
20-
fake: 'response'
21-
})
22-
.on('request', (req, interceptor) => {
23-
expect(req.path).toMatchSnapshot()
24-
})
14+
describe('> planTrip', () => {
15+
const defaultState = {
16+
otp: {
17+
config: {
18+
api: {
19+
host: 'http://mock-host.com',
20+
path: '/api',
21+
port: 80
22+
}
23+
},
24+
currentQuery: {
25+
from: { lat: 12, lon: 34 },
26+
to: { lat: 34, lon: 12 }
27+
},
28+
searches: []
29+
}
30+
}
2531

26-
const mockDispatch = jest.fn()
27-
planTripAction(mockDispatch, () => {
28-
return {
29-
otp: {
30-
config: {
31-
api: {
32-
host: 'http://mock-host.com',
33-
path: '/api',
34-
port: 80
35-
}
36-
},
37-
currentQuery: {
38-
from: {
39-
lat: 12,
40-
lon: 34
41-
},
42-
to: {
43-
lat: 34,
44-
lon: 12
45-
}
32+
const customOtpQueryBuilder = () => {
33+
return `http://mock-host.com/api/plan?from=here&to=there`
34+
}
35+
36+
const stateWithCustomBuilderFunction = {
37+
otp: {
38+
config: {
39+
api: {
40+
host: 'http://mock-host.com',
41+
path: '/api',
42+
port: 80
4643
},
47-
searches: []
48-
}
44+
customOtpQueryBuilder
45+
},
46+
currentQuery: {
47+
from: { lat: 12, lon: 34 },
48+
to: { lat: 34, lon: 12 }
49+
},
50+
searches: []
4951
}
50-
})
52+
}
5153

52-
// wait for request to complete
53-
await timeoutPromise(100)
54+
const testCases = [{
55+
state: defaultState,
56+
title: 'default settings'
57+
}, {
58+
customOtpQueryBuilder,
59+
state: defaultState,
60+
title: 'customOtpQueryBuilder sent to action'
61+
}, {
62+
state: stateWithCustomBuilderFunction,
63+
title: 'customOtpQueryBuilder in state config'
64+
}]
5465

55-
expect(mockDispatch.mock.calls).toMatchSnapshot()
66+
testCases.forEach((testCase) => {
67+
it(`should make a query to OTP with ${testCase.title}`, async () => {
68+
const planTripAction = planTrip(testCase.customOtpQueryBuilder)
69+
70+
nock('http://mock-host.com')
71+
.get(/api\/plan/)
72+
.reply(200, {
73+
fake: 'response'
74+
})
75+
.on('request', (req, interceptor) => {
76+
expect(req.path).toMatchSnapshot()
77+
})
78+
79+
const mockDispatch = jest.fn()
80+
planTripAction(mockDispatch, () => {
81+
return testCase.state
82+
})
83+
84+
// wait for request to complete
85+
await timeoutPromise(100)
86+
87+
expect(mockDispatch.mock.calls).toMatchSnapshot()
88+
})
89+
})
5690
})
5791
})

Diff for: __tests__/util/state.js

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,45 @@
11
/* globals describe, expect, it */
22

3-
import {getDefaultQuery} from '../../lib/util/state'
3+
import {getDefaultQuery, queryIsValid} from '../../lib/util/state'
44

55
describe('util > state', () => {
66
it('getDefaultQuery should parse window hash if available', () => {
77
window.location.hash = '#plan?arriveBy=false&date=2017-02-03&fromPlace=12,34&toPlace=34,12&time=12:34'
88
expect(getDefaultQuery()).toMatchSnapshot()
99
})
10+
11+
describe('queryIsValid', () => {
12+
const fakeFromLocation = {
13+
lat: 12,
14+
lon: 34
15+
}
16+
const fakeToLocation = {
17+
lat: 34,
18+
lon: 12
19+
}
20+
const testCases = [{
21+
expected: false,
22+
input: {
23+
currentQuery: {
24+
from: fakeFromLocation
25+
}
26+
},
27+
title: 'should not be valid with only from location'
28+
}, {
29+
expected: true,
30+
input: {
31+
currentQuery: {
32+
from: fakeFromLocation,
33+
to: fakeToLocation
34+
}
35+
},
36+
title: 'should be valid with from and to locations'
37+
}]
38+
39+
testCases.forEach((testCase) => {
40+
it(testCase.title, () => {
41+
expect(queryIsValid(testCase.input))[testCase.expected ? 'toBeTruthy' : 'toBeFalsy']()
42+
})
43+
})
44+
})
1045
})

Diff for: example.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import {
2222
// OsmBaseLayer,
2323
PlanTripButton,
2424
createOtpReducer,
25-
ErrorMessage
25+
ErrorMessage,
26+
SwitchButton
2627
} from './lib'
2728

2829
// load the OTP configuration
@@ -63,6 +64,7 @@ class OtpRRExample extends Component {
6364
<Col xs={12} md={4} className='sidebar'>
6465
<LocationField type='from' label='Enter start location or click on map...' />
6566
<LocationField type='to' label='Enter destination or click on map...' />
67+
<SwitchButton />
6668
<ModeSelector />
6769
<DateTimeSelector />
6870
<ErrorMessage />

Diff for: lib/actions/api.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ if (typeof (fetch) === 'undefined') {
88
require('isomorphic-fetch')
99
}
1010

11-
import { hasValidLocation } from '../util/state'
11+
import { queryIsValid } from '../util/state'
1212

1313
export const receivedPlanResponse = createAction('PLAN_RESPONSE')
1414
export const requestPlanResponse = createAction('PLAN_REQUEST')
@@ -22,9 +22,9 @@ export function planTrip (customOtpQueryBuilder) {
2222
console.log('query hasn\'t changed')
2323
return
2424
}
25-
if (!hasValidLocation(otpState, 'from') || !hasValidLocation(otpState, 'to')) return // TODO: replace with isQueryValid?
25+
if (!queryIsValid(otpState)) return
2626
dispatch(requestPlanResponse())
27-
const queryBuilderFn = customOtpQueryBuilder || constructPlanQuery
27+
const queryBuilderFn = customOtpQueryBuilder || otpState.config.customOtpQueryBuilder || constructPlanQuery
2828
const url = queryBuilderFn(otpState.config.api, otpState.currentQuery)
2929
// setURLSearch(url)
3030
fetch(url)

Diff for: lib/actions/map.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ import { formChanged } from './form'
1313
* }
1414
*/
1515

16-
export const settingLocation = createAction('SET_LOCATION')
1716
export const clearingLocation = createAction('CLEAR_LOCATION')
17+
export const settingLocation = createAction('SET_LOCATION')
18+
export const switchingLocations = createAction('SWITCH_LOCATIONS')
19+
20+
export function clearLocation (payload) {
21+
return function (dispatch, getState) {
22+
dispatch(clearingLocation(payload))
23+
dispatch(formChanged())
24+
}
25+
}
1826

1927
export function setLocation (payload) {
2028
return function (dispatch, getState) {
@@ -23,9 +31,17 @@ export function setLocation (payload) {
2331
}
2432
}
2533

26-
export function clearLocation (payload) {
34+
export function switchLocations () {
2735
return function (dispatch, getState) {
28-
dispatch(clearingLocation(payload))
36+
const {from, to} = getState().otp.currentQuery
37+
dispatch(settingLocation({
38+
type: 'from',
39+
location: to
40+
}))
41+
dispatch(settingLocation({
42+
type: 'to',
43+
location: from
44+
}))
2945
dispatch(formChanged())
3046
}
3147
}

Diff for: lib/components/form/switch-button.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React, { Component, PropTypes } from 'react'
2+
import { Button } from 'react-bootstrap'
3+
import { connect } from 'react-redux'
4+
5+
import { switchLocations } from '../../actions/map'
6+
7+
class SwitchButton extends Component {
8+
static propTypes = {
9+
onClick: PropTypes.func
10+
}
11+
_onClick = () => {
12+
this.props.switchLocations()
13+
}
14+
render () {
15+
return (
16+
<Button
17+
onClick={this._onClick || this.props.onClick}
18+
>Switch</Button>
19+
)
20+
}
21+
}
22+
23+
const mapStateToProps = (state, ownProps) => {
24+
return {}
25+
}
26+
27+
const mapDispatchToProps = (dispatch, ownProps) => {
28+
return {
29+
switchLocations: () => { dispatch(switchLocations()) }
30+
}
31+
}
32+
33+
export default connect(mapStateToProps, mapDispatchToProps)(SwitchButton)

Diff for: lib/index.js

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
1-
import LocationField from './components/form/location-field'
2-
import PlanTripButton from './components/form/plan-trip-button'
3-
import ModeSelector from './components/form/mode-selector'
41
import DateTimeSelector from './components/form/date-time-selector'
52
import ErrorMessage from './components/form/error-message'
3+
import LocationField from './components/form/location-field'
4+
import ModeSelector from './components/form/mode-selector'
5+
import PlanTripButton from './components/form/plan-trip-button'
6+
import SwitchButton from './components/form/switch-button'
67

7-
import NarrativeItineraries from './components/narrative/narrative-itineraries'
8-
import NarrativeItinerary from './components/narrative/narrative-itinerary'
9-
10-
import BaseMap from './components/map/base-map'
118
import BaseLayers from './components/map/base-layers'
12-
import OsmBaseLayer from './components/map/osm-base-layer'
9+
import BaseMap from './components/map/base-map'
1310
import EndpointsOverlay from './components/map/endpoints-overlay'
1411
import ItineraryOverlay from './components/map/itinerary-overlay'
12+
import OsmBaseLayer from './components/map/osm-base-layer'
13+
14+
import NarrativeItineraries from './components/narrative/narrative-itineraries'
15+
import NarrativeItinerary from './components/narrative/narrative-itinerary'
1516

1617
import createOtpReducer from './reducers/create-otp-reducer'
1718

1819
export {
1920
// form components
20-
LocationField,
21-
PlanTripButton,
22-
ModeSelector,
2321
DateTimeSelector,
2422
ErrorMessage,
23+
LocationField,
24+
ModeSelector,
25+
PlanTripButton,
26+
SwitchButton,
2527

2628
// map components
27-
BaseMap,
2829
BaseLayers,
30+
BaseMap,
2931
EndpointsOverlay,
3032
ItineraryOverlay,
3133
OsmBaseLayer,

Diff for: lib/util/state.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export { hasValidLocation }
5656

5757
function queryIsValid (otpState) {
5858
return hasValidLocation(otpState, 'from') &&
59-
hasValidLocation(otpState, 'from')
59+
hasValidLocation(otpState, 'to')
6060
// TODO: add mode validation
6161
// TODO: add date/time validation
6262
}

0 commit comments

Comments
 (0)