'use strict' const a = require('assert') const isRoughlyEqual = require('is-roughly-equal') const stations = require('vbb-stations-autocomplete') const tapePromise = require('tape-promise').default const tape = require('tape') const shorten = require('vbb-short-station-name') const co = require('./co') const createClient = require('..') const vbbProfile = require('../p/vbb') const { assertValidStation: _assertValidStation, assertValidPoi, assertValidAddress, assertValidLocation, assertValidLine: _assertValidLine, assertValidStopover, hour, createWhen, assertValidWhen, assertValidTicket } = require('./util') const when = createWhen('Europe/Berlin', 'de-DE') const assertValidStation = (t, s, coordsOptional = false) => { _assertValidStation(t, s, coordsOptional) t.equal(s.name, shorten(s.name)) } const assertValidStationProducts = (t, p) => { t.ok(p) t.equal(typeof p.suburban, 'boolean') t.equal(typeof p.subway, 'boolean') t.equal(typeof p.tram, 'boolean') t.equal(typeof p.bus, 'boolean') t.equal(typeof p.ferry, 'boolean') t.equal(typeof p.express, 'boolean') t.equal(typeof p.regional, 'boolean') } const assertValidLine = (t, l) => { _assertValidLine(t, l) if (l.symbol !== null) t.equal(typeof l.symbol, 'string') if (l.nr !== null) t.equal(typeof l.nr, 'number') if (l.metro !== null) t.equal(typeof l.metro, 'boolean') if (l.express !== null) t.equal(typeof l.express, 'boolean') if (l.night !== null) t.equal(typeof l.night, 'boolean') } const findStation = (query) => stations(query, true, false)[0] const test = tapePromise(tape) const client = createClient(vbbProfile) const amrumerStr = '900000009101' const spichernstr = '900000042101' const bismarckstr = '900000024201' test('journeys – station to station', co(function* (t) { const journeys = yield client.journeys(spichernstr, amrumerStr, { results: 3, departure: when, passedStations: true }) t.ok(Array.isArray(journeys)) t.strictEqual(journeys.length, 3) for (let journey of journeys) { t.equal(journey.type, 'journey') t.ok(Array.isArray(journey.legs)) t.strictEqual(journey.legs.length, 1) const leg = journey.legs[0] // todo: all legs t.equal(typeof leg.id, 'string') t.ok(leg.id) assertValidStation(t, leg.origin) assertValidStationProducts(t, leg.origin.products) t.ok(leg.origin.name.indexOf('(Berlin)') === -1) t.strictEqual(leg.origin.id, spichernstr) assertValidWhen(t, leg.departure, when) assertValidStation(t, leg.destination) assertValidStationProducts(t, leg.destination.products) t.strictEqual(leg.destination.id, amrumerStr) assertValidWhen(t, leg.arrival, when) assertValidLine(t, leg.line) if (!findStation(leg.direction)) { const err = new Error('unknown direction: ' + leg.direction) err.stack = err.stack.split('\n').slice(0, 2).join('\n') console.error(err) } t.ok(leg.direction.indexOf('(Berlin)') === -1) t.ok(Array.isArray(leg.passed)) for (let passed of leg.passed) assertValidStopover(t, passed) // todo: find a journey where there ticket info is always available if (journey.tickets) { t.ok(Array.isArray(journey.tickets)) for (let ticket of journey.tickets) assertValidTicket(t, ticket) } } t.end() })) test('journeys – only subway', co(function* (t) { const journeys = yield client.journeys(spichernstr, bismarckstr, { results: 20, departure: when, products: { suburban: false, subway: true, tram: false, bus: false, ferry: false, express: false, regional: false } }) t.ok(Array.isArray(journeys)) t.ok(journeys.length > 1) for (let journey of journeys) { for (let leg of journey.legs) { if (leg.line) { assertValidLine(t, leg.line) t.equal(leg.line.mode, 'train') t.equal(leg.line.product, 'subway') } } } t.end() })) test('journeys – fails with no product', co(function* (t) { t.plan(1) try { client.journeys(spichernstr, bismarckstr, { departure: when, products: { suburban: false, subway: false, tram: false, bus: false, ferry: false, express: false, regional: false } }) // silence rejections, we're only interested in exceptions .catch(() => {}) } catch (err) { t.ok(err, 'error thrown') t.end() } })) test('journeys before date/time', co(function* (t) { const journeys = yield client.journeys(spichernstr, bismarckstr, { results: 3, arrival: when }) for (let i = 0; i < journeys.length; i++) { const j = journeys[i] const name = `journeys[${i}]` const lastLeg = j.legs[j.legs.length - 1] const arr = +new Date(lastLeg.arrival) t.ok(arr <= when, name + '.arrival is after `when`') } t.end() })) test('earlier/later journeys', co(function* (t) { const model = yield client.journeys(spichernstr, bismarckstr, { results: 3, departure: when }) t.equal(typeof model.earlierRef, 'string') t.ok(model.earlierRef) t.equal(typeof model.laterRef, 'string') t.ok(model.laterRef) // departure/arrival and earlierThan/laterThan should be mutually exclusive t.throws(() => { client.journeys(spichernstr, bismarckstr, { departure: when, earlierThan: model.earlierRef }) }) t.throws(() => { client.journeys(spichernstr, bismarckstr, { departure: when, laterThan: model.laterRef }) }) t.throws(() => { client.journeys(spichernstr, bismarckstr, { arrival: when, earlierThan: model.earlierRef }) }) t.throws(() => { client.journeys(spichernstr, bismarckstr, { arrival: when, laterThan: model.laterRef }) }) let earliestDep = Infinity, latestDep = -Infinity for (let j of model) { const dep = +new Date(j.legs[0].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.legs[0].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.legs[0].departure) > latestDep) } t.end() })) test('journey leg details', co(function* (t) { const journeys = yield client.journeys(spichernstr, amrumerStr, { results: 1, departure: when }) const p = journeys[0].legs[0] t.ok(p.id, 'precondition failed') t.ok(p.line.name, 'precondition failed') const leg = yield client.journeyLeg(p.id, p.line.name, {when}) t.equal(typeof leg.id, 'string') t.ok(leg.id) assertValidLine(t, leg.line) t.equal(typeof leg.direction, 'string') t.ok(leg.direction) t.ok(Array.isArray(leg.passed)) for (let passed of leg.passed) assertValidStopover(t, passed) t.end() })) test('journeys – station to address', co(function* (t) { const journeys = yield client.journeys(spichernstr, { type: 'location', address: 'Torfstr. 17, Berlin', latitude: 52.541797, longitude: 13.350042 }, {results: 1, departure: when}) t.ok(Array.isArray(journeys)) t.strictEqual(journeys.length, 1) const journey = journeys[0] const leg = journey.legs[journey.legs.length - 1] assertValidStation(t, leg.origin) assertValidStationProducts(t, leg.origin.products) assertValidWhen(t, leg.departure, when) const dest = leg.destination assertValidAddress(t, dest) t.strictEqual(dest.address, '13353 Berlin-Wedding, Torfstr. 17') t.ok(isRoughlyEqual(.0001, dest.latitude, 52.541797)) t.ok(isRoughlyEqual(.0001, dest.longitude, 13.350042)) assertValidWhen(t, leg.arrival, when) t.end() })) test('journeys – station to POI', co(function* (t) { const journeys = yield client.journeys(spichernstr, { type: 'location', id: '900980720', name: 'Berlin, Atze Musiktheater für Kinder', latitude: 52.543333, longitude: 13.351686 }, {results: 1, departure: when}) t.ok(Array.isArray(journeys)) t.strictEqual(journeys.length, 1) const journey = journeys[0] const leg = journey.legs[journey.legs.length - 1] assertValidStation(t, leg.origin) assertValidStationProducts(t, leg.origin.products) assertValidWhen(t, leg.departure, when) const dest = leg.destination assertValidPoi(t, dest) 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.longitude, 13.351686)) assertValidWhen(t, leg.arrival, when) t.end() })) test('journeys: via works – with detour', co(function* (t) { // Going from Westhafen to Wedding via Württembergalle without detour // is currently impossible. We check if the routing engine computes a detour. const westhafen = '900000001201' const wedding = '900000009104' const württembergallee = '900000026153' const [journey] = yield client.journeys(westhafen, wedding, { via: württembergallee, results: 1, departure: when, passedStations: true }) t.ok(journey) const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === württembergallee)) t.ok(l, 'Württembergalle is not being passed') t.end() })) test('journeys: via works – without detour', co(function* (t) { // When going from Ruhleben to Zoo via Kastanienallee, there is *no need* // to change trains / no need for a "detour". const ruhleben = '900000025202' const zoo = '900000023201' const kastanienallee = '900000020152' const [journey] = yield client.journeys(ruhleben, zoo, { via: kastanienallee, results: 1, departure: when, passedStations: true }) t.ok(journey) const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === kastanienallee)) t.ok(l, 'Kastanienallee is not being passed') t.end() })) test('departures', co(function* (t) { const deps = yield client.departures(spichernstr, {duration: 5, when}) t.ok(Array.isArray(deps)) t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) for (let dep of deps) { t.equal(typeof dep.journeyId, 'string') t.ok(dep.journeyId) t.equal(dep.station.name, 'U Spichernstr.') assertValidStation(t, dep.station) assertValidStationProducts(t, dep.station.products) t.strictEqual(dep.station.id, spichernstr) assertValidWhen(t, dep.when, when) if (!findStation(dep.direction)) { const err = new Error('unknown direction: ' + dep.direction) err.stack = err.stack.split('\n').slice(0, 2).join('\n') console.error(err) } assertValidLine(t, dep.line) } t.end() })) test('departures with station object', co(function* (t) { yield client.departures({ type: 'station', id: spichernstr, name: 'U Spichernstr', location: { type: 'location', latitude: 1.23, longitude: 2.34 } }, {when}) t.ok('did not fail') t.end() })) test('departures at 7-digit station', co(function* (t) { const eisenach = '8010097' // see derhuerst/vbb-hafas#22 yield client.departures(eisenach, {when}) t.pass('did not fail') t.end() })) test('nearby', co(function* (t) { // Berliner Str./Bundesallee const nearby = yield client.nearby({ type: 'location', latitude: 52.4873452, longitude: 13.3310411 }, {distance: 200}) t.ok(Array.isArray(nearby)) for (let n of nearby) { if (n.type === 'station') assertValidStation(t, n) else assertValidLocation(t, n, false) } t.equal(nearby[0].id, '900000044201') t.equal(nearby[0].name, 'U Berliner Str.') t.ok(nearby[0].distance > 0) t.ok(nearby[0].distance < 100) t.equal(nearby[1].id, '900000043252') t.equal(nearby[1].name, 'Landhausstr.') t.ok(nearby[1].distance > 100) t.ok(nearby[1].distance < 200) t.end() })) test('locations', co(function* (t) { const locations = yield client.locations('Alexanderplatz', {results: 20}) t.ok(Array.isArray(locations)) t.ok(locations.length > 0) t.ok(locations.length <= 20) for (let l of locations) { if (l.type === 'station') assertValidStation(t, l) else assertValidLocation(t, l) } t.ok(locations.find(s => s.type === 'station')) t.ok(locations.find(s => s.id && s.name)) // POIs t.ok(locations.find(s => !s.name && s.address)) // addresses t.end() })) test('location', co(function* (t) { const loc = yield client.location(spichernstr) assertValidStation(t, loc) t.equal(loc.id, spichernstr) t.ok(Array.isArray(loc.lines)) if (Array.isArray(loc.lines)) { for (let line of loc.lines) assertValidLine(t, line) } t.end() })) test('radar', co(function* (t) { const vehicles = yield client.radar({ north: 52.52411, west: 13.41002, south: 52.51942, east: 13.41709 }, { duration: 5 * 60, when }) t.ok(Array.isArray(vehicles)) t.ok(vehicles.length > 0) for (let v of vehicles) { if (!findStation(v.direction)) { const err = new Error('unknown direction: ' + v.direction) err.stack = err.stack.split('\n').slice(0, 2).join('\n') console.error(err) } assertValidLine(t, v.line) t.equal(typeof v.location.latitude, 'number') t.ok(v.location.latitude <= 55, 'vehicle is too far away') t.ok(v.location.latitude >= 45, 'vehicle is too far away') t.equal(typeof v.location.longitude, 'number') t.ok(v.location.longitude >= 9, 'vehicle is too far away') t.ok(v.location.longitude <= 15, 'vehicle is too far away') t.ok(Array.isArray(v.nextStops)) for (let st of v.nextStops) { assertValidStopover(t, st, true) t.strictEqual(st.station.name.indexOf('(Berlin)'), -1) if (st.arrival) { t.equal(typeof st.arrival, 'string') const arr = +new Date(st.arrival) // note that this can be an ICE train t.ok(isRoughlyEqual(14 * hour, +when, arr)) } if (st.departure) { t.equal(typeof st.departure, 'string') const dep = +new Date(st.departure) // note that this can be an ICE train t.ok(isRoughlyEqual(14 * hour, +when, dep)) } } t.ok(Array.isArray(v.frames)) for (let f of v.frames) { assertValidStation(t, f.origin, true) assertValidStationProducts(t, f.origin.products) t.strictEqual(f.origin.name.indexOf('(Berlin)'), -1) assertValidStation(t, f.destination, true) assertValidStationProducts(t, f.destination.products) t.strictEqual(f.destination.name.indexOf('(Berlin)'), -1) t.equal(typeof f.t, 'number') } } t.end() }))