mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-06-19 10:42:33 +03:00
Compare commits
6 commits
9314e59053
...
7a1e513fa2
Author | SHA1 | Date | |
---|---|---|---|
|
7a1e513fa2 | ||
|
f1302b0a7b | ||
|
177a3cab3f | ||
|
71d1a4f1a9 | ||
|
6ff406ea79 | ||
|
2a23e1ad9b |
15 changed files with 185 additions and 100 deletions
34
api.js
34
api.js
|
@ -1,32 +1,9 @@
|
|||
import {createClient} from './index.js';
|
||||
import {profile as dbProfile} from './p/db/index.js';
|
||||
import {profile as dbnavProfile} from './p/dbnav/index.js';
|
||||
import {profile as dbwebProfile} from './p/dbweb/index.js';
|
||||
import {mapRouteParsers} from './lib/api-parsers.js';
|
||||
import {createHafasRestApi as createApi} from 'hafas-rest-api';
|
||||
import {loyaltyCardParser} from 'db-rest/lib/loyalty-cards.js';
|
||||
import {parseBoolean, parseInteger} from 'hafas-rest-api/lib/parse.js';
|
||||
|
||||
// TODO product support for nearby etc?
|
||||
const mapRouteParsers = (route, parsers) => {
|
||||
if (!route.includes('journey')) {
|
||||
return parsers;
|
||||
}
|
||||
return {
|
||||
...parsers,
|
||||
loyaltyCard: loyaltyCardParser,
|
||||
firstClass: {
|
||||
description: 'Search for first-class options?',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
parse: parseBoolean,
|
||||
},
|
||||
age: {
|
||||
description: 'Age of traveller',
|
||||
type: 'integer',
|
||||
defaultStr: '*adult*',
|
||||
parse: parseInteger,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const config = {
|
||||
hostname: process.env.HOSTNAME || 'localhost',
|
||||
|
@ -45,10 +22,15 @@ const config = {
|
|||
mapRouteParsers,
|
||||
};
|
||||
|
||||
const profiles = {
|
||||
db: dbProfile,
|
||||
dbnav: dbnavProfile,
|
||||
dbweb: dbwebProfile,
|
||||
};
|
||||
|
||||
const start = async () => {
|
||||
const vendo = createClient(
|
||||
process.env.DB_PROFILE == 'db' ? dbProfile : dbnavProfile,
|
||||
profiles[process.env.DB_PROFILE] || dbnavProfile,
|
||||
process.env.USER_AGENT || 'link-to-your-project-or-email',
|
||||
config,
|
||||
);
|
||||
|
|
|
@ -24,7 +24,7 @@ With `opt`, you can override the default options, which look like this:
|
|||
```js
|
||||
{
|
||||
when: new Date(),
|
||||
direction: null, // not supported
|
||||
direction: null, // only supported in `dbweb` and with `enrichStations=true` (experimental)
|
||||
line: null, // not supported
|
||||
duration: 10, // show departures for the next n minutes
|
||||
results: null, // max. number of results; `null` means "whatever HAFAS wants"
|
||||
|
@ -35,7 +35,7 @@ With `opt`, you can override the default options, which look like this:
|
|||
stopovers: false, // fetch & parse previous/next stopovers?, only supported with `dbweb` profile
|
||||
// departures at related stations
|
||||
// e.g. those that belong together on the metro map.
|
||||
includeRelatedStations: true, // only true supported
|
||||
includeRelatedStations: true,
|
||||
language: 'en' // language to get results in
|
||||
}
|
||||
```
|
||||
|
|
|
@ -385,13 +385,14 @@ paths:
|
|||
- bahncard-2nd-25
|
||||
- bahncard-1st-50
|
||||
- bahncard-2nd-50
|
||||
- bahncard-1st-100
|
||||
- bahncard-2nd-100
|
||||
- vorteilscard
|
||||
- halbtaxabo-railplus
|
||||
- halbtaxabo
|
||||
- voordeelurenabo-railplus
|
||||
- voordeelurenabo
|
||||
- shcard
|
||||
- generalabonnement
|
||||
- generalabonnement-1st
|
||||
- generalabonnement-2nd
|
||||
- nl-40
|
||||
- at-klimaticket
|
||||
- name: firstClass
|
||||
in: query
|
||||
description: Search for first-class options?
|
||||
|
@ -2087,7 +2088,7 @@ components:
|
|||
type: string
|
||||
format: date-time
|
||||
direction:
|
||||
description: only show departures heading to this station
|
||||
description: only show departures heading to this station, only supported for `dbweb` profile
|
||||
default: undefined
|
||||
type: string
|
||||
line:
|
||||
|
@ -2124,7 +2125,7 @@ components:
|
|||
type: boolean
|
||||
includeRelatedStations:
|
||||
description: departures at related stations
|
||||
default: false
|
||||
default: true
|
||||
type: boolean
|
||||
products:
|
||||
$ref: '#/components/schemas/Products'
|
||||
|
|
|
@ -6,9 +6,10 @@ const c = {
|
|||
VOORDEELURENABO: Symbol('Voordeelurenabo'),
|
||||
SHCARD: Symbol('SH-Card'),
|
||||
GENERALABONNEMENT: Symbol('General-Abonnement'),
|
||||
NL_40: Symbol('NL-40%'),
|
||||
AT_KLIMATICKET: Symbol('AT-KlimaTicket'),
|
||||
};
|
||||
|
||||
// see https://gist.github.com/juliuste/202bb04f450a79f8fa12a2ec3abcd72d
|
||||
const formatLoyaltyCard = (data) => {
|
||||
if (!data) {
|
||||
return {
|
||||
|
@ -19,7 +20,7 @@ const formatLoyaltyCard = (data) => {
|
|||
const cls = data.class === 1 ? 'KLASSE_1' : 'KLASSE_2';
|
||||
if (data.type.toString() === c.BAHNCARD.toString()) {
|
||||
return {
|
||||
art: 'BAHNCARD' + data.discount,
|
||||
art: 'BAHNCARD' + (data.business ? 'BUSINESS' : '') + data.discount,
|
||||
klasse: cls,
|
||||
};
|
||||
}
|
||||
|
@ -35,13 +36,24 @@ const formatLoyaltyCard = (data) => {
|
|||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
// TODO Rest
|
||||
if (data.type.toString() === c.GENERALABONNEMENT.toString()) {
|
||||
return {
|
||||
art: 'CH-GENERAL-ABONNEMENT',
|
||||
klasse: cls,
|
||||
};
|
||||
}
|
||||
if (data.type.toString() === c.NL_40.toString()) {
|
||||
return {
|
||||
art: 'NL-40_OHNE_RAILPLUS',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
if (data.type.toString() === c.AT_KLIMATICKET.toString()) {
|
||||
return {
|
||||
art: 'KLIMATICKET_OE',
|
||||
klasse: 'KLASSENLOS',
|
||||
};
|
||||
}
|
||||
return {
|
||||
art: 'KEINE_ERMAESSIGUNG',
|
||||
klasse: 'KLASSENLOS',
|
||||
|
|
41
index.js
41
index.js
|
@ -45,15 +45,23 @@ const loadEnrichedStationData = (profile) => new Promise((resolve, reject) => {
|
|||
});
|
||||
});
|
||||
|
||||
const applyEnrichedStationData = async (ctx, shouldLoadEnrichedStationData) => {
|
||||
const {profile, common} = ctx;
|
||||
if (shouldLoadEnrichedStationData && !common.locations) {
|
||||
const locations = await loadEnrichedStationData(profile);
|
||||
common.locations = locations;
|
||||
}
|
||||
};
|
||||
|
||||
const createClient = (profile, userAgent, opt = {}) => {
|
||||
profile = Object.assign({}, defaultProfile, profile);
|
||||
validateProfile(profile);
|
||||
const common = {};
|
||||
if (opt.enrichStations !== false) {
|
||||
loadEnrichedStationData(profile)
|
||||
.then(locations => {
|
||||
common.locations = locations;
|
||||
});
|
||||
let shouldLoadEnrichedStationData = false;
|
||||
if (typeof opt.enrichStations === 'function') {
|
||||
profile.enrichStation = opt.enrichStations;
|
||||
} else if (opt.enrichStations !== false) {
|
||||
shouldLoadEnrichedStationData = true;
|
||||
}
|
||||
|
||||
if ('string' !== typeof userAgent) {
|
||||
|
@ -64,6 +72,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
}
|
||||
|
||||
const _stationBoard = async (station, type, resultsField, parse, opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
if (isObj(station) && station.id) {
|
||||
station = station.id;
|
||||
} else if ('string' !== typeof station) {
|
||||
|
@ -106,9 +115,15 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
const {res} = await profile.request({profile, opt}, userAgent, req);
|
||||
|
||||
const ctx = {profile, opt, common, res};
|
||||
const results = (res[resultsField] || res.items || res.bahnhofstafelAbfahrtPositionen || res.bahnhofstafelAnkunftPositionen || res.entries)
|
||||
let results = (res[resultsField] || res.items || res.bahnhofstafelAbfahrtPositionen || res.bahnhofstafelAnkunftPositionen || res.entries)
|
||||
.map(res => parse(ctx, res)); // TODO sort?, slice
|
||||
|
||||
if (!opt.includeRelatedStations) {
|
||||
results = results.filter(r => !r.stop?.id || r.stop.id == station);
|
||||
}
|
||||
if (opt.direction) {
|
||||
results = results.filter(r => !r.nextStopovers || r.nextStopovers.find(s => s.stop?.id == opt.direction || s.stop?.name == opt.direction));
|
||||
}
|
||||
return {
|
||||
[resultsField]: results,
|
||||
realtimeDataUpdatedAt: null, // TODO
|
||||
|
@ -123,6 +138,7 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
};
|
||||
|
||||
const journeys = async (from, to, opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
if ('earlierThan' in opt && 'laterThan' in opt) {
|
||||
throw new TypeError('opt.earlierThan and opt.laterThan are mutually exclusive.');
|
||||
}
|
||||
|
@ -205,6 +221,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
};
|
||||
|
||||
const refreshJourney = async (refreshToken, opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
|
||||
if ('string' !== typeof refreshToken || !refreshToken) {
|
||||
throw new TypeError('refreshToken must be a non-empty string.');
|
||||
}
|
||||
|
@ -231,6 +249,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
};
|
||||
|
||||
const locations = async (query, opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
|
||||
if (!isNonEmptyString(query)) {
|
||||
throw new TypeError('query must be a non-empty string.');
|
||||
}
|
||||
|
@ -257,6 +277,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
};
|
||||
|
||||
const stop = async (stop, opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
|
||||
if (isObj(stop) && stop.id) {
|
||||
stop = stop.id;
|
||||
} else if ('string' !== typeof stop) {
|
||||
|
@ -278,6 +300,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
};
|
||||
|
||||
const nearby = async (location, opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
|
||||
validateLocation(location, 'location');
|
||||
|
||||
opt = Object.assign({
|
||||
|
@ -308,6 +332,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
};
|
||||
|
||||
const trip = async (id, opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
|
||||
if (!isNonEmptyString(id)) {
|
||||
throw new TypeError('id must be a non-empty string.');
|
||||
}
|
||||
|
@ -335,6 +361,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
// todo [breaking]: rename to trips()?
|
||||
const tripsByName = async (_lineNameOrFahrtNr = '*', _opt = {}) => {
|
||||
await applyEnrichedStationData({profile, common}, shouldLoadEnrichedStationData);
|
||||
|
||||
throw new Error('not implemented');
|
||||
};
|
||||
|
||||
|
@ -361,4 +389,5 @@ const createClient = (profile, userAgent, opt = {}) => {
|
|||
|
||||
export {
|
||||
createClient,
|
||||
loadEnrichedStationData,
|
||||
};
|
||||
|
|
74
lib/api-parsers.js
Normal file
74
lib/api-parsers.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
import {data as cards} from '../format/loyalty-cards.js';
|
||||
import {parseBoolean, parseInteger} from 'hafas-rest-api/lib/parse.js';
|
||||
|
||||
const typesByName = new Map([
|
||||
['bahncard-1st-25', {type: cards.BAHNCARD, discount: 25, class: 1}],
|
||||
['bahncard-2nd-25', {type: cards.BAHNCARD, discount: 25, class: 2}],
|
||||
['bahncard-1st-50', {type: cards.BAHNCARD, discount: 50, class: 1}],
|
||||
['bahncard-2nd-50', {type: cards.BAHNCARD, discount: 50, class: 2}],
|
||||
['bahncard-1st-100', {type: cards.BAHNCARD, discount: 100, class: 1}],
|
||||
['bahncard-2nd-100', {type: cards.BAHNCARD, discount: 100, class: 2}],
|
||||
['vorteilscard', {type: cards.VORTEILSCARD}],
|
||||
['halbtaxabo-railplus', {type: cards.HALBTAXABO}],
|
||||
['halbtaxabo', {type: cards.HALBTAXABO}],
|
||||
['voordeelurenabo-railplus', {type: cards.VOORDEELURENABO}],
|
||||
['voordeelurenabo', {type: cards.VOORDEELURENABO}],
|
||||
['shcard', {type: cards.SHCARD}],
|
||||
['generalabonnement-1st', {type: cards.GENERALABONNEMENT, class: 1}],
|
||||
['generalabonnement-2nd', {type: cards.GENERALABONNEMENT, class: 2}],
|
||||
['generalabonnement', {type: cards.GENERALABONNEMENT}],
|
||||
['nl-40', {type: cards.NL_40}],
|
||||
['at-klimaticket', {type: cards.AT_KLIMATICKET}],
|
||||
]);
|
||||
const types = Array.from(typesByName.keys());
|
||||
|
||||
const parseLoyaltyCard = (key, val) => {
|
||||
if (typesByName.has(val)) {
|
||||
return typesByName.get(val);
|
||||
}
|
||||
if (!val) {
|
||||
return null;
|
||||
}
|
||||
throw new Error(key + ' must be one of ' + types.join(', '));
|
||||
};
|
||||
|
||||
const parseArrayOr = (parseEntry) => {
|
||||
return (key, val) => {
|
||||
if (Array.isArray(val)) {
|
||||
return val.map(e => parseEntry(key, e));
|
||||
}
|
||||
return parseEntry(key, val);
|
||||
};
|
||||
};
|
||||
|
||||
const mapRouteParsers = (route, parsers) => {
|
||||
if (route !== 'journeys') {
|
||||
return parsers;
|
||||
}
|
||||
return {
|
||||
...parsers,
|
||||
firstClass: {
|
||||
description: 'Search for first-class options?',
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
parse: parseBoolean,
|
||||
},
|
||||
loyaltyCard: {
|
||||
description: 'Type of loyalty card in use.',
|
||||
type: 'string',
|
||||
enum: types,
|
||||
defaultStr: '*none*',
|
||||
parse: parseArrayOr(parseLoyaltyCard),
|
||||
},
|
||||
age: {
|
||||
description: 'Age of traveller',
|
||||
type: 'integer',
|
||||
defaultStr: '*adult*',
|
||||
parse: parseArrayOr(parseInteger),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
mapRouteParsers,
|
||||
};
|
|
@ -16,7 +16,7 @@ import {parseTrip} from '../parse/trip.js';
|
|||
import {parseJourneyLeg} from '../parse/journey-leg.js';
|
||||
import {parseJourney} from '../parse/journey.js';
|
||||
import {parseLine} from '../parse/line.js';
|
||||
import {parseLocation} from '../parse/location.js';
|
||||
import {parseLocation, enrichStation} from '../parse/location.js';
|
||||
import {parsePolyline} from '../parse/polyline.js';
|
||||
import {parseOperator} from '../parse/operator.js';
|
||||
import {parseRemarks, parseCancelled} from '../parse/remarks.js';
|
||||
|
@ -82,6 +82,7 @@ const defaultProfile = {
|
|||
parseLine,
|
||||
parseStationName: id,
|
||||
parseLocation,
|
||||
enrichStation,
|
||||
parsePolyline,
|
||||
parseOperator,
|
||||
parseRemarks,
|
||||
|
|
|
@ -8,7 +8,7 @@ const formatStationBoardReq = (ctx, station, type) => {
|
|||
ortExtId: station,
|
||||
zeit: profile.formatTimeOfDay(profile, opt.when),
|
||||
datum: profile.formatDate(profile, opt.when),
|
||||
mitVias: opt.stopovers || undefined,
|
||||
mitVias: opt.stopovers || Boolean(opt.direction) || undefined,
|
||||
verkehrsmittel: profile.formatProductsFilter(ctx, opt.products || {}),
|
||||
},
|
||||
method: 'GET',
|
||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "db-vendo-client",
|
||||
"version": "6.4.0",
|
||||
"version": "6.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "db-vendo-client",
|
||||
"version": "6.4.0",
|
||||
"version": "6.5.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@derhuerst/round-robin-scheduler": "^1.0.4",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "db-vendo-client",
|
||||
"description": "Client for bahn.de public transport APIs.",
|
||||
"version": "6.4.0",
|
||||
"version": "6.5.0",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
|
|
|
@ -40,7 +40,7 @@ const createParseArrOrDep = (prefix) => {
|
|||
res.remarks = profile.parseRemarks(ctx, d);
|
||||
}
|
||||
|
||||
if (opt.stopovers && Array.isArray(d.ueber)) {
|
||||
if ((opt.stopovers || opt.direction) && Array.isArray(d.ueber)) {
|
||||
const stopovers = d.ueber
|
||||
.map(viaName => profile.parseStopover(ctx, {name: viaName}, null));
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ const parseLocation = (ctx, l) => {
|
|||
}
|
||||
|
||||
const lid = parse(l.id || l.locationId, {delimiter: '@'});
|
||||
const res = {
|
||||
let res = {
|
||||
type: 'location',
|
||||
id: (l.extId || l.evaNr || lid.L || l.evaNumber || l.evaNo || l.bahnhofsId || '').replace(leadingZeros, '') || null,
|
||||
};
|
||||
|
@ -46,13 +46,7 @@ const parseLocation = (ctx, l) => {
|
|||
stop.products = profile.parseProducts(ctx, l.products);
|
||||
}
|
||||
|
||||
if (common && common.locations && common.locations[stop.id]) {
|
||||
delete stop.type;
|
||||
stop = {
|
||||
...common.locations[stop.id],
|
||||
...stop,
|
||||
};
|
||||
}
|
||||
stop = profile.enrichStation(ctx, stop);
|
||||
|
||||
// TODO isMeta
|
||||
// TODO entrances, lines
|
||||
|
@ -70,6 +64,8 @@ const parseLocation = (ctx, l) => {
|
|||
}
|
||||
|
||||
res.name = name;
|
||||
res = enrichStation(ctx, res);
|
||||
|
||||
if (l.type === ADDRESS || lid.A == '2') {
|
||||
res.address = name;
|
||||
}
|
||||
|
@ -80,6 +76,22 @@ const parseLocation = (ctx, l) => {
|
|||
return res;
|
||||
};
|
||||
|
||||
const enrichStation = (ctx, stop, locations) => {
|
||||
const {common} = ctx;
|
||||
const locs = locations || common?.locations;
|
||||
const rich = locs && (locs[stop.id] || locs[stop.name]);
|
||||
if (rich) {
|
||||
delete stop.type;
|
||||
delete stop.id;
|
||||
stop = {
|
||||
...rich,
|
||||
...stop,
|
||||
};
|
||||
}
|
||||
return stop;
|
||||
};
|
||||
|
||||
export {
|
||||
parseLocation,
|
||||
enrichStation,
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ import {profile as rawProfile} from '../p/dbweb/index.js';
|
|||
const res = require('./fixtures/dbweb-departures.json');
|
||||
import {dbwebDepartures as expected} from './fixtures/dbweb-departures.js';
|
||||
|
||||
const client = createClient(rawProfile, 'public-transport/hafas-client:test', {enrichStations: true});
|
||||
const client = createClient(rawProfile, 'public-transport/hafas-client:test', {enrichStations: false});
|
||||
const {profile} = client;
|
||||
|
||||
const opt = {
|
||||
|
|
|
@ -395,17 +395,8 @@ tap.test('trip details', async (t) => {
|
|||
});
|
||||
|
||||
tap.test('departures at Berlin Schwedter Str.', async (t) => {
|
||||
const res = await new Promise((resolve) => {
|
||||
let interval = setInterval(async () => { // repeat evaluating `departures()` until stations are enriched
|
||||
const res = await client.departures(blnSchwedterStr, {
|
||||
duration: 5, when,
|
||||
});
|
||||
|
||||
if (res.departures[0].stop.name !== undefined) { // ctx.common.locations have loaded
|
||||
clearInterval(interval);
|
||||
return resolve(res);
|
||||
}
|
||||
}, 4000);
|
||||
const res = await client.departures(blnSchwedterStr, {
|
||||
duration: 5, when,
|
||||
});
|
||||
|
||||
await testDepartures({
|
||||
|
@ -418,42 +409,24 @@ tap.test('departures at Berlin Schwedter Str.', async (t) => {
|
|||
});
|
||||
|
||||
tap.test('departures with station object', async (t) => {
|
||||
const res = await new Promise((resolve) => {
|
||||
let interval = setInterval(async () => { // repeat evaluating `departures()` until stations are enriched
|
||||
const res = await client.departures({
|
||||
type: 'station',
|
||||
id: jungfernheide,
|
||||
name: 'Berlin Jungfernheide',
|
||||
location: {
|
||||
type: 'location',
|
||||
latitude: 1.23,
|
||||
longitude: 2.34,
|
||||
},
|
||||
}, {when});
|
||||
|
||||
if (res.departures[0].stop.name !== undefined) { // ctx.common.locations have loaded
|
||||
clearInterval(interval);
|
||||
return resolve(res);
|
||||
}
|
||||
}, 4000);
|
||||
});
|
||||
const res = await client.departures({
|
||||
type: 'station',
|
||||
id: jungfernheide,
|
||||
name: 'Berlin Jungfernheide',
|
||||
location: {
|
||||
type: 'location',
|
||||
latitude: 1.23,
|
||||
longitude: 2.34,
|
||||
},
|
||||
}, {when});
|
||||
|
||||
validate(t, res, 'departuresResponse', 'res');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('arrivals at Berlin Schwedter Str.', async (t) => {
|
||||
const res = await new Promise((resolve) => {
|
||||
let interval = setInterval(async () => { // repeat evaluating `arrivals()` until stations are enriched
|
||||
const res = await client.arrivals(blnSchwedterStr, {
|
||||
duration: 5, when,
|
||||
});
|
||||
|
||||
if (res.arrivals[0].stop.name !== undefined) { // ctx.common.locations have loaded
|
||||
clearInterval(interval);
|
||||
return resolve(res);
|
||||
}
|
||||
}, 4000);
|
||||
const res = await client.arrivals(blnSchwedterStr, {
|
||||
duration: 5, when,
|
||||
});
|
||||
|
||||
await testArrivals({
|
||||
|
|
|
@ -4,6 +4,7 @@ import {parseProducts} from '../../parse/products.js';
|
|||
|
||||
const profile = {
|
||||
parseLocation: parse,
|
||||
enrichStation: (ctx, stop) => stop,
|
||||
parseStationName: (_, name) => name.toLowerCase(),
|
||||
parseProducts,
|
||||
products: [{
|
||||
|
|
Loading…
Add table
Reference in a new issue