Skip to content

Commit d9c58bd

Browse files
committed
fix(editor): prevent an infinite loop when sorting trips
fixes #426
1 parent 0f10fa2 commit d9c58bd

File tree

5 files changed

+118
-2
lines changed

5 files changed

+118
-2
lines changed

__tests__/test-utils/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// @flow
2+
3+
export function expectArrayToMatchContents (actual: any, expected: Array<any>) {
4+
expect(actual).toHaveLength(expected.length)
5+
expect(actual).toEqual(expect.arrayContaining(expected))
6+
}

lib/editor/util/__tests__/index.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// @flow
2+
3+
import clone from 'lodash/cloneDeep'
4+
5+
import {sortAndFilterTrips} from '../'
6+
import {expectArrayToMatchContents} from '../../../../__tests__/test-utils'
7+
import {mockTripWithoutStopTimes} from '../../../mock-data'
8+
9+
describe('editor > util > index', () => {
10+
describe('sortAndFilterTrips', () => {
11+
it('should sort trips where there are no stop times', () => {
12+
const mockTrips = [
13+
clone(mockTripWithoutStopTimes),
14+
clone(mockTripWithoutStopTimes)
15+
]
16+
mockTrips[0].id = 2
17+
mockTrips[0].tripId = 'test-trip-id-2'
18+
expectArrayToMatchContents(
19+
sortAndFilterTrips(mockTrips, undefined)
20+
.map(trip => trip.tripId),
21+
['test-trip-id-2', 'test-trip-id-1']
22+
)
23+
})
24+
25+
it('should sort trips where one of the trips has no stop times', () => {
26+
const mockTrips = [
27+
clone(mockTripWithoutStopTimes),
28+
clone(mockTripWithoutStopTimes)
29+
]
30+
mockTrips[0].id = 2
31+
mockTrips[0].tripId = 'test-trip-id-2'
32+
mockTrips[0].stopTimes.push({
33+
arrivalTime: 12345,
34+
departureTime: 12345,
35+
id: 1,
36+
shape_dist_traveled: 0,
37+
stopHeadsign: '',
38+
stopId: '1',
39+
stopSequence: 1
40+
})
41+
expectArrayToMatchContents(
42+
sortAndFilterTrips(mockTrips, undefined)
43+
.map(trip => trip.tripId),
44+
['test-trip-id-1', 'test-trip-id-2']
45+
)
46+
})
47+
48+
it('should sort trips where both of the trips have stop times', () => {
49+
const mockTrips = [
50+
clone(mockTripWithoutStopTimes),
51+
clone(mockTripWithoutStopTimes)
52+
]
53+
mockTrips[0].id = 2
54+
mockTrips[0].tripId = 'test-trip-id-2'
55+
mockTrips[0].stopTimes.push({
56+
arrivalTime: 12345,
57+
departureTime: 12345,
58+
id: 1,
59+
shape_dist_traveled: 0,
60+
stopHeadsign: '',
61+
stopId: '1',
62+
stopSequence: 1
63+
})
64+
mockTrips[1].stopTimes.push({
65+
arrivalTime: 45678,
66+
departureTime: 45678,
67+
id: 1,
68+
shape_dist_traveled: 0,
69+
stopHeadsign: '',
70+
stopId: '1',
71+
stopSequence: 1
72+
})
73+
expectArrayToMatchContents(
74+
sortAndFilterTrips(mockTrips, undefined)
75+
.map(trip => trip.tripId),
76+
['test-trip-id-2', 'test-trip-id-1']
77+
)
78+
})
79+
})
80+
})

lib/editor/util/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function getStopsForPattern (pattern: Pattern, stops: Array<GtfsStop>): A
4545

4646
export function sortAndFilterTrips (
4747
trips: ?Array<Trip>,
48-
useFrequency: boolean
48+
useFrequency: ?boolean
4949
): Array<Trip> {
5050
return trips
5151
? trips
@@ -60,6 +60,9 @@ export function sortAndFilterTrips (
6060
// only has one frequency entry (for now).
6161
return a.frequencies[0].startTime - b.frequencies[0].startTime
6262
}
63+
if (a.stopTimes.length === 0 && b.stopTimes.length === 0) return 0
64+
if (a.stopTimes.length === 0) return -1
65+
if (b.stopTimes.length === 0) return 1
6366
// There may be a case where a null value (skipped stop) appears first
6467
// in the list of stopTimes. In this case, we want to compare on the
6568
// first stopTime that exists for each pair of trips.

lib/mock-data.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ import {defaultState as defaultSignActiveState} from './signs/reducers/active'
2525
import {defaultState as defaultSignSignsState} from './signs/reducers/signs'
2626
import type {AppState} from './types/reducers'
2727

28+
export const mockTripWithoutStopTimes = {
29+
bikes_allowed: null,
30+
blockId: '1',
31+
directionId: 0,
32+
frequencies: [],
33+
id: 3,
34+
pattern_id: '1',
35+
route_id: 'GL',
36+
service_id: 'GL-1',
37+
shape_id: '19',
38+
stopTimes: [],
39+
tripHeadsign: 'Walla Walla',
40+
tripId: 'test-trip-id-1',
41+
tripShortName: null,
42+
wheelchair_accessible: null
43+
}
44+
2845
const defaultManagerState = {
2946
languages: defaultManagerLanguagesState,
3047
projects: defaultManagerProjectsState,

lib/types/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,11 +593,21 @@ type Frequency = {
593593
}
594594

595595
export type Trip = {|
596+
bikes_allowed: ?boolean,
597+
blockId: string,
598+
directionId: number,
596599
frequencies: Array<Frequency>,
597600
id: null | number,
601+
pattern_id: string,
602+
route_id: string,
603+
service_id: string,
604+
shape_id: string,
598605
stopTimes: Array<StopTime>,
606+
tripHeadsign: string,
599607
tripId: string,
600-
useFrequency: boolean
608+
tripShortName: ?string,
609+
useFrequency?: boolean,
610+
wheelchair_accessible: ?boolean
601611
|}
602612

603613
export type Entity = ScheduleException |

0 commit comments

Comments
 (0)