mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-02-23 15:19:35 +02:00
merge derhuerst/master into jfilter/master
This commit is contained in:
commit
47e26163e5
18 changed files with 509 additions and 193 deletions
|
@ -41,6 +41,8 @@ With `opt`, you can override the default options, which look like this:
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
when: new Date(),
|
when: new Date(),
|
||||||
|
earlierThan: null, // ref to get journeys earlier than the last query
|
||||||
|
laterThan: null, // ref to get journeys later than the last query
|
||||||
results: 5, // how many journeys?
|
results: 5, // how many journeys?
|
||||||
via: null, // let journeys pass this station
|
via: null, // let journeys pass this station
|
||||||
passedStations: false, // return stations on the way?
|
passedStations: false, // return stations on the way?
|
||||||
|
@ -85,7 +87,8 @@ client.journeys('900000003201', '900000100008', {
|
||||||
The response may look like this:
|
The response may look like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
[ {
|
[
|
||||||
|
{
|
||||||
legs: [ {
|
legs: [ {
|
||||||
id: '1|31041|35|86|17122017',
|
id: '1|31041|35|86|17122017',
|
||||||
origin: {
|
origin: {
|
||||||
|
@ -201,7 +204,10 @@ The response may look like this:
|
||||||
},
|
},
|
||||||
arrival: '2017-12-17T19:47:00.000+01:00',
|
arrival: '2017-12-17T19:47:00.000+01:00',
|
||||||
arrivalDelay: 30
|
arrivalDelay: 30
|
||||||
} ]
|
},
|
||||||
|
earlierRef: '…', // use with the `earlierThan` option
|
||||||
|
laterRef: '…' // use with the `laterThan` option
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
Some [profiles](../p) are able to parse the ticket information, if returned by the API. For example, if you pass `tickets: true` with the [VBB profile](../p/vbb), each `journey` will have a tickets array that looks like this:
|
Some [profiles](../p) are able to parse the ticket information, if returned by the API. For example, if you pass `tickets: true` with the [VBB profile](../p/vbb), each `journey` will have a tickets array that looks like this:
|
||||||
|
@ -242,3 +248,31 @@ Some [profiles](../p) are able to parse the ticket information, if returned by t
|
||||||
```
|
```
|
||||||
|
|
||||||
If a journey leg has been cancelled, a `cancelled: true` will be added. Also, `departure`/`departureDelay`/`departurePlatform` and `arrival`/`arrivalDelay`/`arrivalPlatform` will be `null`.
|
If a journey leg has been cancelled, a `cancelled: true` will be added. Also, `departure`/`departureDelay`/`departurePlatform` and `arrival`/`arrivalDelay`/`arrivalPlatform` will be `null`.
|
||||||
|
|
||||||
|
To get more journeys earlier/later than the current set of results, pass `journeys.earlierRef`/`journeys.laterRef` into `opt.earlierThan`/`opt.laterThan`. For example, query *later* journeys as follows:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const hbf = '900000003201'
|
||||||
|
const heinrichHeineStr = '900000100008'
|
||||||
|
|
||||||
|
client.journeys(hbf, heinrichHeineStr)
|
||||||
|
.then((journeys) => {
|
||||||
|
const lastJourney = journeys[journeys.length - 1]
|
||||||
|
console.log('departure of last journey', lastJourney.departure)
|
||||||
|
|
||||||
|
// get later journeys
|
||||||
|
return client.journeys(hbf, heinrichHeineStr, {
|
||||||
|
laterThan: journeys.laterRef
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then((laterourneys) => {
|
||||||
|
const firstJourney = laterourneys[laterourneys.length - 1]
|
||||||
|
console.log('departure of first (later) journey', firstJourney.departure)
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
departure of last journey 2017-12-17T19:07:00.000+01:00
|
||||||
|
departure of first (later) journey 2017-12-17T19:19:00.000+01:00
|
||||||
|
```
|
||||||
|
|
|
@ -7,7 +7,7 @@ const formatLocationIdentifier = (data) => {
|
||||||
for (let key in data) {
|
for (let key in data) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(data, key)) continue
|
if (!Object.prototype.hasOwnProperty.call(data, key)) continue
|
||||||
|
|
||||||
str += key + '=' + data[key] + sep // todo: escape
|
str += key + '=' + data[key] + sep // todo: escape, but how?
|
||||||
}
|
}
|
||||||
|
|
||||||
return str
|
return str
|
||||||
|
|
|
@ -13,9 +13,9 @@ const formatPoi = (p) => {
|
||||||
lid: formatLocationIdentifier({
|
lid: formatLocationIdentifier({
|
||||||
A: '4', // POI
|
A: '4', // POI
|
||||||
O: p.name,
|
O: p.name,
|
||||||
|
L: p.id,
|
||||||
X: formatCoord(p.longitude),
|
X: formatCoord(p.longitude),
|
||||||
Y: formatCoord(p.latitude),
|
Y: formatCoord(p.latitude)
|
||||||
L: p.id
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const createFormatBitmask = (modes) => {
|
const createFormatBitmask = (allProducts) => {
|
||||||
const formatBitmask = (products) => {
|
const formatBitmask = (products) => {
|
||||||
if(Object.keys(products).length === 0) throw new Error('products filter must not be empty')
|
if(Object.keys(products).length === 0) throw new Error('products filter must not be empty')
|
||||||
let bitmask = 0
|
let bitmask = 0
|
||||||
for (let product in products) {
|
for (let product in products) {
|
||||||
if (products[product] === true) bitmask += modes[product].bitmask
|
if (!allProducts[product]) throw new Error('unknown product ' + product)
|
||||||
|
if (products[product] === true) bitmask += allProducts[product].bitmask
|
||||||
}
|
}
|
||||||
return bitmask
|
return bitmask
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const formatStation = id => ({type: 'S', lid: 'L=' + id})
|
const formatLocationIdentifier = require('./location-identifier')
|
||||||
|
|
||||||
|
const formatStation = (id) => {
|
||||||
|
return {
|
||||||
|
// todo: name necessary?
|
||||||
|
lid: formatLocationIdentifier({
|
||||||
|
A: '1', // station
|
||||||
|
L: id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = formatStation
|
module.exports = formatStation
|
||||||
|
|
82
index.js
82
index.js
|
@ -7,12 +7,15 @@ const validateProfile = require('./lib/validate-profile')
|
||||||
const defaultProfile = require('./lib/default-profile')
|
const defaultProfile = require('./lib/default-profile')
|
||||||
const _request = require('./lib/request')
|
const _request = require('./lib/request')
|
||||||
|
|
||||||
|
const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o)
|
||||||
|
const isNonEmptyString = str => 'string' === typeof str && str.length > 0
|
||||||
|
|
||||||
const createClient = (profile, request = _request) => {
|
const createClient = (profile, request = _request) => {
|
||||||
profile = Object.assign({}, defaultProfile, profile)
|
profile = Object.assign({}, defaultProfile, profile)
|
||||||
validateProfile(profile)
|
validateProfile(profile)
|
||||||
|
|
||||||
const departures = (station, opt = {}) => {
|
const departures = (station, opt = {}) => {
|
||||||
if ('object' === typeof station) station = profile.formatStation(station.id)
|
if (isObj(station)) station = profile.formatStation(station.id)
|
||||||
else if ('string' === typeof station) station = profile.formatStation(station)
|
else if ('string' === typeof station) station = profile.formatStation(station)
|
||||||
else throw new Error('station must be an object or a string.')
|
else throw new Error('station must be an object or a string.')
|
||||||
|
|
||||||
|
@ -49,6 +52,29 @@ const createClient = (profile, request = _request) => {
|
||||||
from = profile.formatLocation(profile, from)
|
from = profile.formatLocation(profile, from)
|
||||||
to = profile.formatLocation(profile, to)
|
to = profile.formatLocation(profile, to)
|
||||||
|
|
||||||
|
if (('earlierThan' in opt) && ('laterThan' in opt)) {
|
||||||
|
throw new Error('opt.laterThan and opt.laterThan are mutually exclusive.')
|
||||||
|
}
|
||||||
|
let journeysRef = null
|
||||||
|
if ('earlierThan' in opt) {
|
||||||
|
if (!isNonEmptyString(opt.earlierThan)) {
|
||||||
|
throw new Error('opt.earlierThan must be a non-empty string.')
|
||||||
|
}
|
||||||
|
if ('when' in opt) {
|
||||||
|
throw new Error('opt.earlierThan and opt.when are mutually exclusive.')
|
||||||
|
}
|
||||||
|
journeysRef = opt.earlierThan
|
||||||
|
}
|
||||||
|
if ('laterThan' in opt) {
|
||||||
|
if (!isNonEmptyString(opt.laterThan)) {
|
||||||
|
throw new Error('opt.laterThan must be a non-empty string.')
|
||||||
|
}
|
||||||
|
if ('when' in opt) {
|
||||||
|
throw new Error('opt.laterThan and opt.when are mutually exclusive.')
|
||||||
|
}
|
||||||
|
journeysRef = opt.laterThan
|
||||||
|
}
|
||||||
|
|
||||||
opt = Object.assign({
|
opt = Object.assign({
|
||||||
results: 5, // how many journeys?
|
results: 5, // how many journeys?
|
||||||
via: null, // let journeys pass this station?
|
via: null, // let journeys pass this station?
|
||||||
|
@ -75,10 +101,18 @@ const createClient = (profile, request = _request) => {
|
||||||
filters.push(profile.filters.accessibility[opt.accessibility])
|
filters.push(profile.filters.accessibility[opt.accessibility])
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = profile.transformJourneysQuery({
|
// With protocol version `1.16`, the VBB endpoint fails with
|
||||||
outDate: profile.formatDate(profile, opt.when),
|
// `CGI_READ_FAILED` if you pass `numF`, the parameter for the number
|
||||||
outTime: profile.formatTime(profile, opt.when),
|
// of results. To circumvent this, we loop here, collecting journeys
|
||||||
numF: opt.results,
|
// until we have enough.
|
||||||
|
// see https://github.com/derhuerst/hafas-client/pull/23#issuecomment-370246163
|
||||||
|
// todo: check if `numF` is supported again, revert this change
|
||||||
|
const journeys = []
|
||||||
|
const more = (when, journeysRef) => {
|
||||||
|
const query = {
|
||||||
|
outDate: profile.formatDate(profile, when),
|
||||||
|
outTime: profile.formatTime(profile, when),
|
||||||
|
ctxScr: journeysRef,
|
||||||
getPasslist: !!opt.passedStations,
|
getPasslist: !!opt.passedStations,
|
||||||
maxChg: opt.transfers,
|
maxChg: opt.transfers,
|
||||||
minChgTime: opt.transferTime,
|
minChgTime: opt.transferTime,
|
||||||
|
@ -93,22 +127,44 @@ const createClient = (profile, request = _request) => {
|
||||||
outFrwd: true, // todo: what is this?
|
outFrwd: true, // todo: what is this?
|
||||||
getIV: false, // todo: walk & bike as alternatives?
|
getIV: false, // todo: walk & bike as alternatives?
|
||||||
getPolyline: false // todo: shape for displaying on a map?
|
getPolyline: false // todo: shape for displaying on a map?
|
||||||
}, opt)
|
}
|
||||||
|
if (profile.journeysNumF) query.numF = opt.results
|
||||||
|
|
||||||
return request(profile, {
|
return request(profile, {
|
||||||
cfg: {polyEnc: 'GPA'},
|
cfg: {polyEnc: 'GPA'},
|
||||||
meth: 'TripSearch',
|
meth: 'TripSearch',
|
||||||
req: query
|
req: profile.transformJourneysQuery(query, opt)
|
||||||
})
|
})
|
||||||
.then((d) => {
|
.then((d) => {
|
||||||
if (!Array.isArray(d.outConL)) return []
|
if (!Array.isArray(d.outConL)) return []
|
||||||
const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks)
|
const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks)
|
||||||
return d.outConL.map(parse)
|
if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB
|
||||||
|
|
||||||
|
let latestDep = -Infinity
|
||||||
|
for (let j of d.outConL) {
|
||||||
|
j = parse(j)
|
||||||
|
journeys.push(j)
|
||||||
|
|
||||||
|
if (journeys.length === opt.results) { // collected enough
|
||||||
|
journeys.laterRef = d.outCtxScrF
|
||||||
|
return journeys
|
||||||
|
}
|
||||||
|
const dep = +new Date(j.departure)
|
||||||
|
if (dep > latestDep) latestDep = dep
|
||||||
|
}
|
||||||
|
|
||||||
|
const when = new Date(latestDep)
|
||||||
|
return more(when, d.outCtxScrF) // otherwise continue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return more(opt.when, journeysRef)
|
||||||
|
}
|
||||||
|
|
||||||
const locations = (query, opt = {}) => {
|
const locations = (query, opt = {}) => {
|
||||||
if ('string' !== typeof query) throw new Error('query must be a string.')
|
if (!isNonEmptyString(query)) {
|
||||||
|
throw new Error('query must be a non-empty string.')
|
||||||
|
}
|
||||||
opt = Object.assign({
|
opt = Object.assign({
|
||||||
fuzzy: true, // find only exact matches?
|
fuzzy: true, // find only exact matches?
|
||||||
results: 10, // how many search results?
|
results: 10, // how many search results?
|
||||||
|
@ -158,7 +214,7 @@ const createClient = (profile, request = _request) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const nearby = (location, opt = {}) => {
|
const nearby = (location, opt = {}) => {
|
||||||
if ('object' !== typeof location || Array.isArray(location)) {
|
if (!isObj(location)) {
|
||||||
throw new Error('location must be an object.')
|
throw new Error('location must be an object.')
|
||||||
} else if (location.type !== 'location') {
|
} else if (location.type !== 'location') {
|
||||||
throw new Error('invalid location object.')
|
throw new Error('invalid location object.')
|
||||||
|
@ -200,6 +256,12 @@ const createClient = (profile, request = _request) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const journeyLeg = (ref, lineName, opt = {}) => {
|
const journeyLeg = (ref, lineName, opt = {}) => {
|
||||||
|
if (!isNonEmptyString(ref)) {
|
||||||
|
throw new Error('ref must be a non-empty string.')
|
||||||
|
}
|
||||||
|
if (!isNonEmptyString(lineName)) {
|
||||||
|
throw new Error('lineName must be a non-empty string.')
|
||||||
|
}
|
||||||
opt = Object.assign({
|
opt = Object.assign({
|
||||||
passedStations: true // return stations on the way?
|
passedStations: true // return stations on the way?
|
||||||
}, opt)
|
}, opt)
|
||||||
|
|
|
@ -26,6 +26,10 @@ const filters = require('../format/filters')
|
||||||
const id = x => x
|
const id = x => x
|
||||||
|
|
||||||
const defaultProfile = {
|
const defaultProfile = {
|
||||||
|
salt: null,
|
||||||
|
addChecksum: false,
|
||||||
|
addMicMac: false,
|
||||||
|
|
||||||
transformReqBody: id,
|
transformReqBody: id,
|
||||||
transformReq: id,
|
transformReq: id,
|
||||||
|
|
||||||
|
@ -55,6 +59,7 @@ const defaultProfile = {
|
||||||
formatRectangle,
|
formatRectangle,
|
||||||
filters,
|
filters,
|
||||||
|
|
||||||
|
journeysNumF: true, // `journeys()` method: support for `numF` field
|
||||||
journeyLeg: false,
|
journeyLeg: false,
|
||||||
radar: false
|
radar: false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const crypto = require('crypto')
|
||||||
let captureStackTrace = () => {}
|
let captureStackTrace = () => {}
|
||||||
if (process.env.NODE_ENV === 'dev') {
|
if (process.env.NODE_ENV === 'dev') {
|
||||||
captureStackTrace = require('capture-stack-trace')
|
captureStackTrace = require('capture-stack-trace')
|
||||||
|
@ -8,6 +9,8 @@ const {stringify} = require('query-string')
|
||||||
const Promise = require('pinkie-promise')
|
const Promise = require('pinkie-promise')
|
||||||
const {fetch} = require('fetch-ponyfill')({Promise})
|
const {fetch} = require('fetch-ponyfill')({Promise})
|
||||||
|
|
||||||
|
const md5 = input => crypto.createHash('md5').update(input).digest()
|
||||||
|
|
||||||
const request = (profile, data) => {
|
const request = (profile, data) => {
|
||||||
const body = profile.transformReqBody({lang: 'en', svcReqL: [data]})
|
const body = profile.transformReqBody({lang: 'en', svcReqL: [data]})
|
||||||
const req = profile.transformReq({
|
const req = profile.transformReq({
|
||||||
|
@ -19,14 +22,37 @@ const request = (profile, data) => {
|
||||||
'Accept-Encoding': 'gzip, deflate',
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
'user-agent': 'https://github.com/derhuerst/hafas-client'
|
'user-agent': 'https://github.com/derhuerst/hafas-client'
|
||||||
},
|
},
|
||||||
query: null
|
query: {}
|
||||||
})
|
})
|
||||||
const url = profile.endpoint + (req.query ? '?' + stringify(req.query) : '')
|
|
||||||
|
if (profile.addChecksum || profile.addMicMac) {
|
||||||
|
if (!Buffer.isBuffer(profile.salt)) {
|
||||||
|
throw new Error('profile.salt must be a Buffer.')
|
||||||
|
}
|
||||||
|
if (profile.addChecksum) {
|
||||||
|
const checksum = md5(Buffer.concat([
|
||||||
|
Buffer.from(req.body, 'utf8'),
|
||||||
|
profile.salt
|
||||||
|
]))
|
||||||
|
req.query.checksum = checksum.toString('hex')
|
||||||
|
}
|
||||||
|
if (profile.addMicMac) {
|
||||||
|
const mic = md5(Buffer.from(req.body, 'utf8'))
|
||||||
|
req.query.mic = mic.toString('hex')
|
||||||
|
|
||||||
|
const micAsHex = Buffer.from(mic.toString('hex'), 'utf8')
|
||||||
|
const mac = md5(Buffer.concat([micAsHex, profile.salt]))
|
||||||
|
req.query.mac = mac.toString('hex')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = profile.endpoint + '?' + stringify(req.query)
|
||||||
|
|
||||||
// Async stack traces are not supported everywhere yet, so we create our own.
|
// Async stack traces are not supported everywhere yet, so we create our own.
|
||||||
const err = new Error()
|
const err = new Error()
|
||||||
err.isHafasError = true
|
err.isHafasError = true
|
||||||
err.request = body
|
err.request = body
|
||||||
|
err.url = url
|
||||||
captureStackTrace(err)
|
captureStackTrace(err)
|
||||||
|
|
||||||
return fetch(url, req)
|
return fetch(url, req)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const crypto = require('crypto')
|
|
||||||
|
|
||||||
const _createParseLine = require('../../parse/line')
|
const _createParseLine = require('../../parse/line')
|
||||||
const _createParseJourney = require('../../parse/journey')
|
const _createParseJourney = require('../../parse/journey')
|
||||||
const _formatStation = require('../../format/station')
|
const _formatStation = require('../../format/station')
|
||||||
|
@ -23,17 +21,6 @@ const transformReqBody = (body) => {
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
const salt = 'bdI8UVj40K5fvxwf'
|
|
||||||
const transformReq = (req) => {
|
|
||||||
const hash = crypto.createHash('md5')
|
|
||||||
hash.update(req.body + salt)
|
|
||||||
|
|
||||||
if (!req.query) req.query = {}
|
|
||||||
req.query.checksum = hash.digest('hex')
|
|
||||||
|
|
||||||
return req
|
|
||||||
}
|
|
||||||
|
|
||||||
const transformJourneysQuery = (query, opt) => {
|
const transformJourneysQuery = (query, opt) => {
|
||||||
const filters = query.jnyFltrL
|
const filters = query.jnyFltrL
|
||||||
if (opt.bike) filters.push(bike)
|
if (opt.bike) filters.push(bike)
|
||||||
|
@ -142,8 +129,11 @@ const dbProfile = {
|
||||||
locale: 'de-DE',
|
locale: 'de-DE',
|
||||||
timezone: 'Europe/Berlin',
|
timezone: 'Europe/Berlin',
|
||||||
endpoint: 'https://reiseauskunft.bahn.de/bin/mgate.exe',
|
endpoint: 'https://reiseauskunft.bahn.de/bin/mgate.exe',
|
||||||
|
|
||||||
|
salt: Buffer.from('bdI8UVj40K5fvxwf', 'utf8'),
|
||||||
|
addChecksum: true,
|
||||||
|
|
||||||
transformReqBody,
|
transformReqBody,
|
||||||
transformReq,
|
|
||||||
transformJourneysQuery,
|
transformJourneysQuery,
|
||||||
|
|
||||||
products: modes.allProducts,
|
products: modes.allProducts,
|
||||||
|
|
|
@ -20,10 +20,10 @@ const modes = require('./modes')
|
||||||
const formatBitmask = createFormatBitmask(modes)
|
const formatBitmask = createFormatBitmask(modes)
|
||||||
|
|
||||||
const transformReqBody = (body) => {
|
const transformReqBody = (body) => {
|
||||||
body.client = {type: 'IPA', id: 'BVG', name: 'FahrInfo', v: '4070700'}
|
body.client = {type: 'IPA', id: 'VBB', name: 'vbbPROD', v: '4010300'}
|
||||||
body.ext = 'BVG.1'
|
body.ext = 'VBB.1'
|
||||||
body.ver = '1.15' // todo: 1.16 with `mic` and `mac` query params
|
body.ver = '1.16'
|
||||||
body.auth = {type: 'AID', aid: '1Rxs112shyHLatUX4fofnmdxK'}
|
body.auth = {type: 'AID', aid: 'hafas-vbb-apps'}
|
||||||
|
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,13 @@ const formatProducts = (products) => {
|
||||||
const vbbProfile = {
|
const vbbProfile = {
|
||||||
locale: 'de-DE',
|
locale: 'de-DE',
|
||||||
timezone: 'Europe/Berlin',
|
timezone: 'Europe/Berlin',
|
||||||
endpoint: 'https://bvg-apps.hafas.de/bin/mgate.exe',
|
endpoint: 'https://fahrinfo.vbb.de/bin/mgate.exe',
|
||||||
|
|
||||||
|
// https://gist.github.com/derhuerst/a8d94a433358abc015ff77df4481070c#file-haf_config_base-properties-L39
|
||||||
|
// https://runkit.com/derhuerst/hafas-decrypt-encrypted-mac-salt
|
||||||
|
salt: Buffer.from('5243544a4d3266467846667878516649', 'hex'),
|
||||||
|
addMicMac: true,
|
||||||
|
|
||||||
transformReqBody,
|
transformReqBody,
|
||||||
|
|
||||||
products: modes.allProducts,
|
products: modes.allProducts,
|
||||||
|
@ -183,6 +189,7 @@ const vbbProfile = {
|
||||||
formatStation,
|
formatStation,
|
||||||
formatProducts,
|
formatProducts,
|
||||||
|
|
||||||
|
journeysNumF: false,
|
||||||
journeyLeg: true,
|
journeyLeg: true,
|
||||||
radar: true
|
radar: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "hafas-client",
|
"name": "hafas-client",
|
||||||
"description": "JavaScript client for HAFAS public transport APIs.",
|
"description": "JavaScript client for HAFAS public transport APIs.",
|
||||||
"version": "2.3.2",
|
"version": "2.3.3",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
|
|
@ -32,7 +32,11 @@ const createParseDeparture = (profile, stations, lines, remarks) => {
|
||||||
// see also derhuerst/vbb-rest#19
|
// see also derhuerst/vbb-rest#19
|
||||||
if (d.stbStop.aCncl || d.stbStop.dCncl) {
|
if (d.stbStop.aCncl || d.stbStop.dCncl) {
|
||||||
res.cancelled = true
|
res.cancelled = true
|
||||||
|
Object.defineProperty(res, 'canceled', {value: true})
|
||||||
res.when = res.delay = null
|
res.when = res.delay = null
|
||||||
|
|
||||||
|
const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeS)
|
||||||
|
res.formerScheduledWhen = when.toISO()
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -75,11 +75,17 @@ const createParseJourneyLeg = (profile, stations, lines, remarks) => {
|
||||||
// see also derhuerst/vbb-rest#19
|
// see also derhuerst/vbb-rest#19
|
||||||
if (pt.arr.aCncl) {
|
if (pt.arr.aCncl) {
|
||||||
res.cancelled = true
|
res.cancelled = true
|
||||||
|
Object.defineProperty(res, 'canceled', {value: true})
|
||||||
res.arrival = res.arrivalPlatform = res.arrivalDelay = null
|
res.arrival = res.arrivalPlatform = res.arrivalDelay = null
|
||||||
|
const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeS)
|
||||||
|
res.formerScheduledArrival = arr.toISO()
|
||||||
}
|
}
|
||||||
if (pt.dep.dCncl) {
|
if (pt.dep.dCncl) {
|
||||||
res.cancelled = true
|
res.cancelled = true
|
||||||
|
Object.defineProperty(res, 'canceled', {value: true})
|
||||||
res.departure = res.departurePlatform = res.departureDelay = null
|
res.departure = res.departurePlatform = res.departureDelay = null
|
||||||
|
const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeS)
|
||||||
|
res.formerScheduledDeparture = dep.toISO()
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -14,6 +14,7 @@ const createParseJourney = (profile, stations, lines, remarks) => {
|
||||||
const parseJourney = (j) => {
|
const parseJourney = (j) => {
|
||||||
const legs = j.secL.map(leg => parseLeg(j, leg))
|
const legs = j.secL.map(leg => parseLeg(j, leg))
|
||||||
const res = {
|
const res = {
|
||||||
|
type: 'journey',
|
||||||
legs,
|
legs,
|
||||||
origin: legs[0].origin,
|
origin: legs[0].origin,
|
||||||
destination: legs[legs.length - 1].destination,
|
destination: legs[legs.length - 1].destination,
|
||||||
|
@ -22,7 +23,16 @@ const createParseJourney = (profile, stations, lines, remarks) => {
|
||||||
}
|
}
|
||||||
if (legs.some(p => p.cancelled)) {
|
if (legs.some(p => p.cancelled)) {
|
||||||
res.cancelled = true
|
res.cancelled = true
|
||||||
|
Object.defineProperty(res, 'canceled', {value: true})
|
||||||
res.departure = res.arrival = null
|
res.departure = res.arrival = null
|
||||||
|
|
||||||
|
const firstLeg = j.secL[0]
|
||||||
|
const dep = profile.parseDateTime(profile, j.date, firstLeg.dep.dTimeS)
|
||||||
|
res.formerScheduledDeparture = dep.toISO()
|
||||||
|
|
||||||
|
const lastLeg = j.secL[j.secL.length - 1]
|
||||||
|
const arr = profile.parseDateTime(profile, j.date, lastLeg.arr.aTimeS)
|
||||||
|
res.formerScheduledArrival = arr.toISO()
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -175,6 +175,7 @@ The returned [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript
|
||||||
- [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas#vbb-hafas) – JavaScript client for Berlin & Brandenburg public transport HAFAS API.
|
- [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas#vbb-hafas) – JavaScript client for Berlin & Brandenburg public transport HAFAS API.
|
||||||
- [`hafas-departures-in-direction`](https://github.com/derhuerst/hafas-departures-in-direction#hafas-departures-in-direction) – Pass in a HAFAS client, get departures in a certain direction.
|
- [`hafas-departures-in-direction`](https://github.com/derhuerst/hafas-departures-in-direction#hafas-departures-in-direction) – Pass in a HAFAS client, get departures in a certain direction.
|
||||||
- [`hafas-collect-departures-at`](https://github.com/derhuerst/hafas-collect-departures-at#hafas-collect-departures-at) – Utility to collect departures, using any HAFAS client.
|
- [`hafas-collect-departures-at`](https://github.com/derhuerst/hafas-collect-departures-at#hafas-collect-departures-at) – Utility to collect departures, using any HAFAS client.
|
||||||
|
- [`hafas-discover-stations`](https://github.com/derhuerst/hafas-discover-stations#hafas-discover-stations) – Pass in a HAFAS client, discover stations by querying departures.
|
||||||
- [`hafas-rest-api`](https://github.com/derhuerst/hafas-rest-api#hafas-rest-api) – Expose a HAFAS client via an HTTP REST API.
|
- [`hafas-rest-api`](https://github.com/derhuerst/hafas-rest-api#hafas-rest-api) – Expose a HAFAS client via an HTTP REST API.
|
||||||
- [List of european long-distance transport operators, available API endpoints, GTFS feeds and client modules.](https://github.com/public-transport/european-transport-operators)
|
- [List of european long-distance transport operators, available API endpoints, GTFS feeds and client modules.](https://github.com/public-transport/european-transport-operators)
|
||||||
- [Collection of european transport JavaScript modules.](https://github.com/public-transport/european-transport-modules)
|
- [Collection of european transport JavaScript modules.](https://github.com/public-transport/european-transport-modules)
|
||||||
|
|
76
test/db.js
76
test/db.js
|
@ -53,7 +53,7 @@ const findStation = (id) => new Promise((yay, nay) => {
|
||||||
|
|
||||||
const isJungfernheide = (s) => {
|
const isJungfernheide = (s) => {
|
||||||
return s.type === 'station' &&
|
return s.type === 'station' &&
|
||||||
(s.id === '008011167' || s.id === '8011167') &&
|
(s.id === '008011167' || s.id === jungfernh) &&
|
||||||
s.name === 'Berlin Jungfernheide' &&
|
s.name === 'Berlin Jungfernheide' &&
|
||||||
s.location &&
|
s.location &&
|
||||||
isRoughlyEqual(s.location.latitude, 52.530408, .0005) &&
|
isRoughlyEqual(s.location.latitude, 52.530408, .0005) &&
|
||||||
|
@ -62,7 +62,7 @@ const isJungfernheide = (s) => {
|
||||||
|
|
||||||
const assertIsJungfernheide = (t, s) => {
|
const assertIsJungfernheide = (t, s) => {
|
||||||
t.equal(s.type, 'station')
|
t.equal(s.type, 'station')
|
||||||
t.ok(s.id === '008011167' || s.id === '8011167', 'id should be 8011167')
|
t.ok(s.id === '008011167' || s.id === jungfernh, 'id should be 8011167')
|
||||||
t.equal(s.name, 'Berlin Jungfernheide')
|
t.equal(s.name, 'Berlin Jungfernheide')
|
||||||
t.ok(s.location)
|
t.ok(s.location)
|
||||||
t.ok(isRoughlyEqual(s.location.latitude, 52.530408, .0005))
|
t.ok(isRoughlyEqual(s.location.latitude, 52.530408, .0005))
|
||||||
|
@ -92,14 +92,22 @@ const assertValidPrice = (t, p) => {
|
||||||
const test = tapePromise(tape)
|
const test = tapePromise(tape)
|
||||||
const client = createClient(dbProfile)
|
const client = createClient(dbProfile)
|
||||||
|
|
||||||
|
const jungfernh = '8011167'
|
||||||
|
const berlinHbf = '8011160'
|
||||||
|
const münchenHbf = '8000261'
|
||||||
|
const hannoverHbf = '8000152'
|
||||||
|
const regensburgHbf = '8000309'
|
||||||
|
|
||||||
test('Berlin Jungfernheide to München Hbf', co(function* (t) {
|
test('Berlin Jungfernheide to München Hbf', co(function* (t) {
|
||||||
const journeys = yield client.journeys('8011167', '8000261', {
|
const journeys = yield client.journeys(jungfernh, münchenHbf, {
|
||||||
when, passedStations: true
|
when, passedStations: true
|
||||||
})
|
})
|
||||||
|
|
||||||
t.ok(Array.isArray(journeys))
|
t.ok(Array.isArray(journeys))
|
||||||
t.ok(journeys.length > 0, 'no journeys')
|
t.ok(journeys.length > 0, 'no journeys')
|
||||||
for (let journey of journeys) {
|
for (let journey of journeys) {
|
||||||
|
t.equal(journey.type, 'journey')
|
||||||
|
|
||||||
assertValidStation(t, journey.origin)
|
assertValidStation(t, journey.origin)
|
||||||
assertValidStationProducts(t, journey.origin.products)
|
assertValidStationProducts(t, journey.origin.products)
|
||||||
if (!(yield findStation(journey.origin.id))) {
|
if (!(yield findStation(journey.origin.id))) {
|
||||||
|
@ -152,7 +160,7 @@ test('Berlin Jungfernheide to München Hbf', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) {
|
test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) {
|
||||||
const journeys = yield client.journeys('8011167', {
|
const journeys = yield client.journeys(jungfernh, {
|
||||||
type: 'location', address: 'Torfstraße 17',
|
type: 'location', address: 'Torfstraße 17',
|
||||||
latitude: 52.5416823, longitude: 13.3491223
|
latitude: 52.5416823, longitude: 13.3491223
|
||||||
}, {when})
|
}, {when})
|
||||||
|
@ -181,7 +189,7 @@ test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) {
|
test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) {
|
||||||
const journeys = yield client.journeys('8011167', {
|
const journeys = yield client.journeys(jungfernh, {
|
||||||
type: 'location', id: '991598902', name: 'ATZE Musiktheater',
|
type: 'location', id: '991598902', name: 'ATZE Musiktheater',
|
||||||
latitude: 52.542417, longitude: 13.350437
|
latitude: 52.542417, longitude: 13.350437
|
||||||
}, {when})
|
}, {when})
|
||||||
|
@ -210,9 +218,6 @@ test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('Berlin Hbf to München Hbf with stopover at Hannover Hbf', co(function* (t) {
|
test('Berlin Hbf to München Hbf with stopover at Hannover Hbf', co(function* (t) {
|
||||||
const berlinHbf = '8011160'
|
|
||||||
const münchenHbf = '8000261'
|
|
||||||
const hannoverHbf = '8000152'
|
|
||||||
const [journey] = yield client.journeys(berlinHbf, münchenHbf, {
|
const [journey] = yield client.journeys(berlinHbf, münchenHbf, {
|
||||||
via: hannoverHbf,
|
via: hannoverHbf,
|
||||||
results: 1
|
results: 1
|
||||||
|
@ -228,8 +233,58 @@ test('Berlin Hbf to München Hbf with stopover at Hannover Hbf', co(function* (t
|
||||||
t.end()
|
t.end()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) {
|
||||||
|
const model = yield client.journeys(jungfernh, münchenHbf, {
|
||||||
|
results: 3, when
|
||||||
|
})
|
||||||
|
|
||||||
|
t.equal(typeof model.earlierRef, 'string')
|
||||||
|
t.ok(model.earlierRef)
|
||||||
|
t.equal(typeof model.laterRef, 'string')
|
||||||
|
t.ok(model.laterRef)
|
||||||
|
|
||||||
|
// when and earlierThan/laterThan should be mutually exclusive
|
||||||
|
t.throws(() => {
|
||||||
|
client.journeys(jungfernh, münchenHbf, {
|
||||||
|
when, earlierThan: model.earlierRef
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.throws(() => {
|
||||||
|
client.journeys(jungfernh, münchenHbf, {
|
||||||
|
when, laterThan: model.laterRef
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let earliestDep = Infinity, latestDep = -Infinity
|
||||||
|
for (let j of model) {
|
||||||
|
const dep = +new Date(j.departure)
|
||||||
|
if (dep < earliestDep) earliestDep = dep
|
||||||
|
else if (dep > latestDep) latestDep = dep
|
||||||
|
}
|
||||||
|
|
||||||
|
const earlier = yield client.journeys(jungfernh, münchenHbf, {
|
||||||
|
results: 3,
|
||||||
|
// todo: single journey ref?
|
||||||
|
earlierThan: model.earlierRef
|
||||||
|
})
|
||||||
|
for (let j of earlier) {
|
||||||
|
t.ok(new Date(j.departure) < earliestDep)
|
||||||
|
}
|
||||||
|
|
||||||
|
const later = yield client.journeys(jungfernh, münchenHbf, {
|
||||||
|
results: 3,
|
||||||
|
// todo: single journey ref?
|
||||||
|
laterThan: model.laterRef
|
||||||
|
})
|
||||||
|
for (let j of later) {
|
||||||
|
t.ok(new Date(j.departure) > latestDep)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
}))
|
||||||
|
|
||||||
test('departures at Berlin Jungfernheide', co(function* (t) {
|
test('departures at Berlin Jungfernheide', co(function* (t) {
|
||||||
const deps = yield client.departures('8011167', {
|
const deps = yield client.departures(jungfernh, {
|
||||||
duration: 5, when
|
duration: 5, when
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -250,7 +305,7 @@ test('departures at Berlin Jungfernheide', co(function* (t) {
|
||||||
test('departures with station object', co(function* (t) {
|
test('departures with station object', co(function* (t) {
|
||||||
yield client.departures({
|
yield client.departures({
|
||||||
type: 'station',
|
type: 'station',
|
||||||
id: '8011167',
|
id: jungfernh,
|
||||||
name: 'Berlin Jungfernheide',
|
name: 'Berlin Jungfernheide',
|
||||||
location: {
|
location: {
|
||||||
type: 'location',
|
type: 'location',
|
||||||
|
@ -306,7 +361,6 @@ test('locations named Jungfernheide', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('location', co(function* (t) {
|
test('location', co(function* (t) {
|
||||||
const regensburgHbf = '8000309'
|
|
||||||
const loc = yield client.location(regensburgHbf)
|
const loc = yield client.location(regensburgHbf)
|
||||||
|
|
||||||
assertValidStation(t, loc)
|
assertValidStation(t, loc)
|
||||||
|
|
70
test/oebb.js
70
test/oebb.js
|
@ -110,9 +110,14 @@ const assertValidLine = (t, l) => { // with optional mode
|
||||||
const test = tapePromise(tape)
|
const test = tapePromise(tape)
|
||||||
const client = createClient(oebbProfile)
|
const client = createClient(oebbProfile)
|
||||||
|
|
||||||
|
const salzburgHbf = '8100002'
|
||||||
|
const wienWestbahnhof = '1291501'
|
||||||
|
const wien = '1190100'
|
||||||
|
const klagenfurtHbf = '8100085'
|
||||||
|
const muenchenHbf = '8000261'
|
||||||
|
const grazHbf = '8100173'
|
||||||
|
|
||||||
test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) {
|
test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) {
|
||||||
const salzburgHbf = '8100002'
|
|
||||||
const wienWestbahnhof = '1291501'
|
|
||||||
const journeys = yield client.journeys(salzburgHbf, wienWestbahnhof, {
|
const journeys = yield client.journeys(salzburgHbf, wienWestbahnhof, {
|
||||||
when, passedStations: true
|
when, passedStations: true
|
||||||
})
|
})
|
||||||
|
@ -120,6 +125,8 @@ test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) {
|
||||||
t.ok(Array.isArray(journeys))
|
t.ok(Array.isArray(journeys))
|
||||||
t.ok(journeys.length > 0, 'no journeys')
|
t.ok(journeys.length > 0, 'no journeys')
|
||||||
for (let journey of journeys) {
|
for (let journey of journeys) {
|
||||||
|
t.equal(journey.type, 'journey')
|
||||||
|
|
||||||
assertValidStation(t, journey.origin)
|
assertValidStation(t, journey.origin)
|
||||||
assertValidStationProducts(t, journey.origin.products)
|
assertValidStationProducts(t, journey.origin.products)
|
||||||
// todo
|
// todo
|
||||||
|
@ -176,7 +183,6 @@ test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) {
|
test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) {
|
||||||
const salzburgHbf = '8100002'
|
|
||||||
const wagramerStr = {
|
const wagramerStr = {
|
||||||
type: 'location',
|
type: 'location',
|
||||||
latitude: 48.236216,
|
latitude: 48.236216,
|
||||||
|
@ -221,7 +227,6 @@ test('Albertina to Salzburg Hbf', co(function* (t) {
|
||||||
name: 'Albertina',
|
name: 'Albertina',
|
||||||
id: '975900003'
|
id: '975900003'
|
||||||
}
|
}
|
||||||
const salzburgHbf = '8100002'
|
|
||||||
const journeys = yield client.journeys(albertina, salzburgHbf, {when})
|
const journeys = yield client.journeys(albertina, salzburgHbf, {when})
|
||||||
|
|
||||||
t.ok(Array.isArray(journeys))
|
t.ok(Array.isArray(journeys))
|
||||||
|
@ -253,9 +258,6 @@ test('Albertina to Salzburg Hbf', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('Wien to Klagenfurt Hbf with stopover at Salzburg Hbf', co(function* (t) {
|
test('Wien to Klagenfurt Hbf with stopover at Salzburg Hbf', co(function* (t) {
|
||||||
const wien = '1190100'
|
|
||||||
const klagenfurtHbf = '8100085'
|
|
||||||
const salzburgHbf = '8100002'
|
|
||||||
const [journey] = yield client.journeys(wien, klagenfurtHbf, {
|
const [journey] = yield client.journeys(wien, klagenfurtHbf, {
|
||||||
via: salzburgHbf,
|
via: salzburgHbf,
|
||||||
results: 1,
|
results: 1,
|
||||||
|
@ -272,9 +274,57 @@ test('Wien to Klagenfurt Hbf with stopover at Salzburg Hbf', co(function* (t) {
|
||||||
t.end()
|
t.end()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t) {
|
||||||
|
const model = yield client.journeys(salzburgHbf, wienWestbahnhof, {
|
||||||
|
results: 3, when
|
||||||
|
})
|
||||||
|
|
||||||
|
t.equal(typeof model.earlierRef, 'string')
|
||||||
|
t.ok(model.earlierRef)
|
||||||
|
t.equal(typeof model.laterRef, 'string')
|
||||||
|
t.ok(model.laterRef)
|
||||||
|
|
||||||
|
// when and earlierThan/laterThan should be mutually exclusive
|
||||||
|
t.throws(() => {
|
||||||
|
client.journeys(salzburgHbf, wienWestbahnhof, {
|
||||||
|
when, earlierThan: model.earlierRef
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.throws(() => {
|
||||||
|
client.journeys(salzburgHbf, wienWestbahnhof, {
|
||||||
|
when, laterThan: model.laterRef
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let earliestDep = Infinity, latestDep = -Infinity
|
||||||
|
for (let j of model) {
|
||||||
|
const dep = +new Date(j.departure)
|
||||||
|
if (dep < earliestDep) earliestDep = dep
|
||||||
|
else if (dep > latestDep) latestDep = dep
|
||||||
|
}
|
||||||
|
|
||||||
|
const earlier = yield client.journeys(salzburgHbf, wienWestbahnhof, {
|
||||||
|
results: 3,
|
||||||
|
// todo: single journey ref?
|
||||||
|
earlierThan: model.earlierRef
|
||||||
|
})
|
||||||
|
for (let j of earlier) {
|
||||||
|
t.ok(new Date(j.departure) < earliestDep)
|
||||||
|
}
|
||||||
|
|
||||||
|
const later = yield client.journeys(salzburgHbf, wienWestbahnhof, {
|
||||||
|
results: 3,
|
||||||
|
// todo: single journey ref?
|
||||||
|
laterThan: model.laterRef
|
||||||
|
})
|
||||||
|
for (let j of later) {
|
||||||
|
t.ok(new Date(j.departure) > latestDep)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
}))
|
||||||
|
|
||||||
test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) {
|
test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) {
|
||||||
const wienWestbahnhof = '1291501'
|
|
||||||
const muenchenHbf = '8000261'
|
|
||||||
const journeys = yield client.journeys(wienWestbahnhof, muenchenHbf, {
|
const journeys = yield client.journeys(wienWestbahnhof, muenchenHbf, {
|
||||||
results: 1, when
|
results: 1, when
|
||||||
})
|
})
|
||||||
|
@ -299,7 +349,6 @@ test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('departures at Salzburg Hbf', co(function* (t) {
|
test('departures at Salzburg Hbf', co(function* (t) {
|
||||||
const salzburgHbf = '8100002'
|
|
||||||
const deps = yield client.departures(salzburgHbf, {
|
const deps = yield client.departures(salzburgHbf, {
|
||||||
duration: 5, when
|
duration: 5, when
|
||||||
})
|
})
|
||||||
|
@ -363,7 +412,6 @@ test('locations named Salzburg', co(function* (t) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
test('location', co(function* (t) {
|
test('location', co(function* (t) {
|
||||||
const grazHbf = '8100173'
|
|
||||||
const loc = yield client.location(grazHbf)
|
const loc = yield client.location(grazHbf)
|
||||||
|
|
||||||
assertValidStation(t, loc)
|
assertValidStation(t, loc)
|
||||||
|
|
74
test/vbb.js
74
test/vbb.js
|
@ -67,6 +67,8 @@ test('journeys – station to station', co(function* (t) {
|
||||||
t.strictEqual(journeys.length, 3)
|
t.strictEqual(journeys.length, 3)
|
||||||
|
|
||||||
for (let journey of journeys) {
|
for (let journey of journeys) {
|
||||||
|
t.equal(journey.type, 'journey')
|
||||||
|
|
||||||
assertValidStation(t, journey.origin)
|
assertValidStation(t, journey.origin)
|
||||||
assertValidStationProducts(t, journey.origin.products)
|
assertValidStationProducts(t, journey.origin.products)
|
||||||
t.ok(journey.origin.name.indexOf('(Berlin)') === -1)
|
t.ok(journey.origin.name.indexOf('(Berlin)') === -1)
|
||||||
|
@ -146,7 +148,7 @@ test('journeys – only subway', co(function* (t) {
|
||||||
|
|
||||||
test('journeys – fails with no product', co(function* (t) {
|
test('journeys – fails with no product', co(function* (t) {
|
||||||
try {
|
try {
|
||||||
yield client.journeys(spichernstr, bismarckstr, {
|
client.journeys(spichernstr, bismarckstr, {
|
||||||
when,
|
when,
|
||||||
products: {
|
products: {
|
||||||
suburban: false,
|
suburban: false,
|
||||||
|
@ -158,12 +160,64 @@ test('journeys – fails with no product', co(function* (t) {
|
||||||
regional: false
|
regional: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// silence rejections, we're only interested in exceptions
|
||||||
|
.catch(() => {})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
t.ok(err, 'error thrown')
|
t.ok(err, 'error thrown')
|
||||||
t.end()
|
t.end()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
test('earlier/later journeys', co(function* (t) {
|
||||||
|
const model = yield client.journeys(spichernstr, bismarckstr, {
|
||||||
|
results: 3, when
|
||||||
|
})
|
||||||
|
|
||||||
|
t.equal(typeof model.earlierRef, 'string')
|
||||||
|
t.ok(model.earlierRef)
|
||||||
|
t.equal(typeof model.laterRef, 'string')
|
||||||
|
t.ok(model.laterRef)
|
||||||
|
|
||||||
|
// when and earlierThan/laterThan should be mutually exclusive
|
||||||
|
t.throws(() => {
|
||||||
|
client.journeys(spichernstr, bismarckstr, {
|
||||||
|
when, earlierThan: model.earlierRef
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.throws(() => {
|
||||||
|
client.journeys(spichernstr, bismarckstr, {
|
||||||
|
when, laterThan: model.laterRef
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let earliestDep = Infinity, latestDep = -Infinity
|
||||||
|
for (let j of model) {
|
||||||
|
const dep = +new Date(j.departure)
|
||||||
|
if (dep < earliestDep) earliestDep = dep
|
||||||
|
else if (dep > latestDep) latestDep = dep
|
||||||
|
}
|
||||||
|
|
||||||
|
const earlier = yield client.journeys(spichernstr, bismarckstr, {
|
||||||
|
results: 3,
|
||||||
|
// todo: single journey ref?
|
||||||
|
earlierThan: model.earlierRef
|
||||||
|
})
|
||||||
|
for (let j of earlier) {
|
||||||
|
t.ok(new Date(j.departure) < earliestDep)
|
||||||
|
}
|
||||||
|
|
||||||
|
const later = yield client.journeys(spichernstr, bismarckstr, {
|
||||||
|
results: 3,
|
||||||
|
// todo: single journey ref?
|
||||||
|
laterThan: model.laterRef
|
||||||
|
})
|
||||||
|
for (let j of later) {
|
||||||
|
t.ok(new Date(j.departure) > latestDep)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.end()
|
||||||
|
}))
|
||||||
|
|
||||||
test('journey leg details', co(function* (t) {
|
test('journey leg details', co(function* (t) {
|
||||||
const journeys = yield client.journeys(spichernstr, amrumerStr, {
|
const journeys = yield client.journeys(spichernstr, amrumerStr, {
|
||||||
results: 1, when
|
results: 1, when
|
||||||
|
@ -192,8 +246,9 @@ test('journey leg details', co(function* (t) {
|
||||||
|
|
||||||
test('journeys – station to address', co(function* (t) {
|
test('journeys – station to address', co(function* (t) {
|
||||||
const journeys = yield client.journeys(spichernstr, {
|
const journeys = yield client.journeys(spichernstr, {
|
||||||
type: 'location', address: 'Torfstraße 17',
|
type: 'location',
|
||||||
latitude: 52.5416823, longitude: 13.3491223
|
address: 'Torfstr. 17, Berlin',
|
||||||
|
latitude: 52.541797, longitude: 13.350042
|
||||||
}, {results: 1, when})
|
}, {results: 1, when})
|
||||||
|
|
||||||
t.ok(Array.isArray(journeys))
|
t.ok(Array.isArray(journeys))
|
||||||
|
@ -207,9 +262,9 @@ test('journeys – station to address', co(function* (t) {
|
||||||
|
|
||||||
const dest = leg.destination
|
const dest = leg.destination
|
||||||
assertValidAddress(t, dest)
|
assertValidAddress(t, dest)
|
||||||
t.strictEqual(dest.address, 'Torfstraße 17')
|
t.strictEqual(dest.address, '13353 Berlin-Wedding, Torfstr. 17')
|
||||||
t.ok(isRoughlyEqual(.0001, dest.latitude, 52.5416823))
|
t.ok(isRoughlyEqual(.0001, dest.latitude, 52.541797))
|
||||||
t.ok(isRoughlyEqual(.0001, dest.longitude, 13.3491223))
|
t.ok(isRoughlyEqual(.0001, dest.longitude, 13.350042))
|
||||||
assertValidWhen(t, leg.arrival, when)
|
assertValidWhen(t, leg.arrival, when)
|
||||||
|
|
||||||
t.end()
|
t.end()
|
||||||
|
@ -219,7 +274,9 @@ test('journeys – station to address', co(function* (t) {
|
||||||
|
|
||||||
test('journeys – station to POI', co(function* (t) {
|
test('journeys – station to POI', co(function* (t) {
|
||||||
const journeys = yield client.journeys(spichernstr, {
|
const journeys = yield client.journeys(spichernstr, {
|
||||||
type: 'location', id: '9980720', name: 'ATZE Musiktheater',
|
type: 'location',
|
||||||
|
id: '900980720',
|
||||||
|
name: 'Berlin, Atze Musiktheater für Kinder',
|
||||||
latitude: 52.543333, longitude: 13.351686
|
latitude: 52.543333, longitude: 13.351686
|
||||||
}, {results: 1, when})
|
}, {results: 1, when})
|
||||||
|
|
||||||
|
@ -234,7 +291,8 @@ test('journeys – station to POI', co(function* (t) {
|
||||||
|
|
||||||
const dest = leg.destination
|
const dest = leg.destination
|
||||||
assertValidPoi(t, dest)
|
assertValidPoi(t, dest)
|
||||||
t.strictEqual(dest.name, 'ATZE Musiktheater')
|
t.strictEqual(dest.id, '900980720')
|
||||||
|
t.strictEqual(dest.name, 'Berlin, Atze Musiktheater für Kinder')
|
||||||
t.ok(isRoughlyEqual(.0001, dest.latitude, 52.543333))
|
t.ok(isRoughlyEqual(.0001, dest.latitude, 52.543333))
|
||||||
t.ok(isRoughlyEqual(.0001, dest.longitude, 13.351686))
|
t.ok(isRoughlyEqual(.0001, dest.longitude, 13.351686))
|
||||||
assertValidWhen(t, leg.arrival, when)
|
assertValidWhen(t, leg.arrival, when)
|
||||||
|
|
Loading…
Add table
Reference in a new issue