mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-06-19 10:42:33 +03:00
Compare commits
4 commits
14b80dbf33
...
6c2081c14e
Author | SHA1 | Date | |
---|---|---|---|
|
6c2081c14e | ||
|
f741a13670 | ||
|
bcaad526c7 | ||
|
162b946bac |
17 changed files with 282 additions and 211 deletions
|
@ -621,13 +621,17 @@
|
||||||
"Zusammenfassung",
|
"Zusammenfassung",
|
||||||
"Züttlingen",
|
"Züttlingen",
|
||||||
"Zwickauer",
|
"Zwickauer",
|
||||||
"zwischenhalte"
|
"zwischenhalte",
|
||||||
|
"bestprice",
|
||||||
|
"intervalle",
|
||||||
|
"Intervalle",
|
||||||
|
"tagesbest"
|
||||||
],
|
],
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
"docs/dumps/**",
|
"docs/dumps/**",
|
||||||
"test/e2e/fixtures/**",
|
"test/e2e/fixtures/**",
|
||||||
"test/parse/remarks.js",
|
"test/parse/remarks.js",
|
||||||
"test/parse/dbnav-journey.js"
|
"test/parse/dbnav-journey.js"
|
||||||
],
|
],
|
||||||
"dictionaries": [
|
"dictionaries": [
|
||||||
"node"
|
"node"
|
||||||
|
|
|
@ -75,7 +75,9 @@ With `opt`, you can override the default options, which look like this:
|
||||||
subStops: true, // not supported
|
subStops: true, // not supported
|
||||||
entrances: true, // not supported
|
entrances: true, // not supported
|
||||||
remarks: true, // parse & expose hints & warnings?
|
remarks: true, // parse & expose hints & warnings?
|
||||||
scheduledDays: false, // not yet supported
|
scheduledDays: false, // returns a field `serviceDays` (instead of `scheduledDays` in hafas-client!) with a different, human-readable structure
|
||||||
|
notOnlyFastRoutes: false, // if true, also show journeys that are mathematically non-optimal
|
||||||
|
bestprice: false, // search for lowest prices across the entire day, returns list of journeys sorted by price
|
||||||
firstClass: false, // first or second class for tickets
|
firstClass: false, // first or second class for tickets
|
||||||
loyaltyCard: null, // BahnCards etc., see below
|
loyaltyCard: null, // BahnCards etc., see below
|
||||||
language: 'en', // language to get results in
|
language: 'en', // language to get results in
|
||||||
|
|
|
@ -365,7 +365,19 @@ paths:
|
||||||
default: true
|
default: true
|
||||||
- name: scheduledDays
|
- name: scheduledDays
|
||||||
in: query
|
in: query
|
||||||
description: Parse & return dates each journey is valid on?
|
description: Parse & return dates the journey is valid on?, returns a field `serviceDays` (instead of `scheduledDays` in hafas-client!) with a different, human-readable structure
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
- name: notOnlyFastRoutes
|
||||||
|
in: query
|
||||||
|
description: if true, also show journeys that are mathematically non-optimal
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
- name: bestprice
|
||||||
|
in: query
|
||||||
|
description: search for lowest prices across the entire day
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
@ -683,7 +695,19 @@ paths:
|
||||||
default: true
|
default: true
|
||||||
- name: scheduledDays
|
- name: scheduledDays
|
||||||
in: query
|
in: query
|
||||||
description: Parse & return dates the journey is valid on?
|
description: Parse & return dates the journey is valid on?, returns a field `serviceDays` (instead of `scheduledDays` in hafas-client!) with a different, human-readable structure
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
- name: notOnlyFastRoutes
|
||||||
|
in: query
|
||||||
|
description: if true, also show journeys that are mathematically non-optimal
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
- name: bestprice
|
||||||
|
in: query
|
||||||
|
description: search for lowest prices across the entire day
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
|
14
index.js
14
index.js
|
@ -85,10 +85,10 @@ const createClient = (profile, userAgent, opt = {}) => {
|
||||||
throw new TypeError('type must be a non-empty string.');
|
throw new TypeError('type must be a non-empty string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!profile.departuresGetPasslist && 'stopovers' in opt) {
|
if (!profile.departuresGetPasslist && opt.stopovers) {
|
||||||
throw new Error('opt.stopovers is not supported by this endpoint');
|
throw new Error('opt.stopovers is not supported by this endpoint');
|
||||||
}
|
}
|
||||||
if (!profile.departuresStbFltrEquiv && 'includeRelatedStations' in opt) { // TODO artificially filter?
|
if (!profile.departuresStbFltrEquiv && 'includeRelatedStations' in opt) {
|
||||||
throw new Error('opt.includeRelatedStations is not supported by this endpoint');
|
throw new Error('opt.includeRelatedStations is not supported by this endpoint');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +185,8 @@ const createClient = (profile, userAgent, opt = {}) => {
|
||||||
entrances: true, // parse & expose entrances of stops/stations?
|
entrances: true, // parse & expose entrances of stops/stations?
|
||||||
remarks: true, // parse & expose hints & warnings?
|
remarks: true, // parse & expose hints & warnings?
|
||||||
scheduledDays: false, // parse & expose dates each journey is valid on?
|
scheduledDays: false, // parse & expose dates each journey is valid on?
|
||||||
|
notOnlyFastRoutes: false, // if true, also show routes that are mathematically non-optimal
|
||||||
|
bestprice: false, // search for lowest prices across the entire day
|
||||||
}, opt);
|
}, opt);
|
||||||
|
|
||||||
if (opt.when !== undefined) {
|
if (opt.when !== undefined) {
|
||||||
|
@ -210,9 +212,15 @@ const createClient = (profile, userAgent, opt = {}) => {
|
||||||
const req = profile.formatJourneysReq({profile, opt}, from, to, when, outFrwd, journeysRef);
|
const req = profile.formatJourneysReq({profile, opt}, from, to, when, outFrwd, journeysRef);
|
||||||
const {res} = await profile.request({profile, opt}, userAgent, req);
|
const {res} = await profile.request({profile, opt}, userAgent, req);
|
||||||
const ctx = {profile, opt, common, res};
|
const ctx = {profile, opt, common, res};
|
||||||
const verbindungen = Number.isInteger(opt.results) ? res.verbindungen.slice(0, opt.results) : res.verbindungen;
|
if (opt.bestprice) {
|
||||||
|
res.verbindungen = (res.intervalle || res.tagesbestPreisIntervalle).flatMap(i => i.verbindungen.map(v => ({...v, ...v.verbindung})));
|
||||||
|
}
|
||||||
|
const verbindungen = Number.isInteger(opt.results) && opt.results != 3 ? res.verbindungen.slice(0, opt.results) : res.verbindungen; // TODO remove default from hafas-rest-api
|
||||||
const journeys = verbindungen
|
const journeys = verbindungen
|
||||||
.map(j => profile.parseJourney(ctx, j));
|
.map(j => profile.parseJourney(ctx, j));
|
||||||
|
if (opt.bestprice) {
|
||||||
|
journeys.sort((a, b) => a.price?.amount - b.price?.amount);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
earlierRef: res.verbindungReference?.earlier || res.frueherContext || null,
|
earlierRef: res.verbindungReference?.earlier || res.frueherContext || null,
|
||||||
|
|
|
@ -50,7 +50,7 @@ const mapRouteParsers = (route, parsers) => {
|
||||||
firstClass: {
|
firstClass: {
|
||||||
description: 'Search for first-class options?',
|
description: 'Search for first-class options?',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: 'false',
|
default: false,
|
||||||
parse: parseBoolean,
|
parse: parseBoolean,
|
||||||
},
|
},
|
||||||
loyaltyCard: {
|
loyaltyCard: {
|
||||||
|
@ -66,6 +66,18 @@ const mapRouteParsers = (route, parsers) => {
|
||||||
defaultStr: '*adult*',
|
defaultStr: '*adult*',
|
||||||
parse: parseArrayOr(parseInteger),
|
parse: parseArrayOr(parseInteger),
|
||||||
},
|
},
|
||||||
|
notOnlyFastRoutes: {
|
||||||
|
description: 'If true, also show routes that are mathematically non-optimal',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
parse: parseBoolean,
|
||||||
|
},
|
||||||
|
bestprice: {
|
||||||
|
description: 'Search for lowest prices across the entire day',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
parse: parseBoolean,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"journeysEndpoint": "https://app.vendo.noncd.db.de/mob/angebote/fahrplan",
|
"journeysEndpoint": "https://app.vendo.noncd.db.de/mob/angebote/fahrplan",
|
||||||
|
"bestpriceEndpoint": "https://app.vendo.noncd.db.de/mob/angebote/tagesbestpreis",
|
||||||
"refreshJourneysEndpointTickets": "https://app.vendo.noncd.db.de/mob/angebote/recon",
|
"refreshJourneysEndpointTickets": "https://app.vendo.noncd.db.de/mob/angebote/recon",
|
||||||
"refreshJourneysEndpointPolyline": "https://app.vendo.noncd.db.de/mob/trip/recon",
|
"refreshJourneysEndpointPolyline": "https://app.vendo.noncd.db.de/mob/trip/recon",
|
||||||
"locationsEndpoint": "https://app.vendo.noncd.db.de/mob/location/search",
|
"locationsEndpoint": "https://app.vendo.noncd.db.de/mob/location/search",
|
||||||
|
|
|
@ -55,8 +55,11 @@ const formatJourneysReq = (ctx, from, to, when, outFrwd, journeysRef) => {
|
||||||
if (journeysRef) {
|
if (journeysRef) {
|
||||||
query.reiseHin.wunsch.context = journeysRef;
|
query.reiseHin.wunsch.context = journeysRef;
|
||||||
}
|
}
|
||||||
|
if (opt.notOnlyFastRoutes) {
|
||||||
|
query.reiseHin.wunsch.economic = true;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
endpoint: ctx.profile.journeysEndpoint,
|
endpoint: opt.bestprice ? profile.bestpriceEndpoint : profile.journeysEndpoint,
|
||||||
body: query,
|
body: query,
|
||||||
headers: getHeaders('application/x.db.vendo.mob.verbindungssuche.v8+json'),
|
headers: getHeaders('application/x.db.vendo.mob.verbindungssuche.v8+json'),
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"journeysEndpoint": "https://int.bahn.de/web/api/angebote/fahrplan",
|
"journeysEndpoint": "https://int.bahn.de/web/api/angebote/fahrplan",
|
||||||
|
"bestpriceEndpoint": "https://int.bahn.de/web/api/angebote/tagesbestpreis",
|
||||||
"refreshJourneysEndpointTickets": "https://int.bahn.de/web/api/angebote/recon",
|
"refreshJourneysEndpointTickets": "https://int.bahn.de/web/api/angebote/recon",
|
||||||
"refreshJourneysEndpointPolyline": "https://int.bahn.de/web/api/reiseloesung/verbindung",
|
"refreshJourneysEndpointPolyline": "https://int.bahn.de/web/api/reiseloesung/verbindung",
|
||||||
"locationsEndpoint": "https://int.bahn.de/web/api/reiseloesung/orte",
|
"locationsEndpoint": "https://int.bahn.de/web/api/reiseloesung/orte",
|
||||||
|
|
|
@ -13,7 +13,7 @@ const formatJourneysReq = (ctx, from, to, when, outFrwd, journeysRef) => {
|
||||||
deutschlandTicketVorhanden: false,
|
deutschlandTicketVorhanden: false,
|
||||||
nurDeutschlandTicketVerbindungen: false,
|
nurDeutschlandTicketVerbindungen: false,
|
||||||
reservierungsKontingenteVorhanden: false,
|
reservierungsKontingenteVorhanden: false,
|
||||||
schnelleVerbindungen: true,
|
schnelleVerbindungen: !opt.notOnlyFastRoutes,
|
||||||
sitzplatzOnly: false,
|
sitzplatzOnly: false,
|
||||||
abfahrtsHalt: from.lid,
|
abfahrtsHalt: from.lid,
|
||||||
zwischenhalte: opt.via
|
zwischenhalte: opt.via
|
||||||
|
@ -35,14 +35,14 @@ const formatJourneysReq = (ctx, from, to, when, outFrwd, journeysRef) => {
|
||||||
if (opt.results !== null) {
|
if (opt.results !== null) {
|
||||||
// TODO query.numF = opt.results;
|
// TODO query.numF = opt.results;
|
||||||
}
|
}
|
||||||
query = Object.assign(query, ctx.profile.formatTravellers(ctx));
|
query = Object.assign(query, profile.formatTravellers(ctx));
|
||||||
return {
|
return {
|
||||||
endpoint: ctx.profile.journeysEndpoint,
|
endpoint: opt.bestprice ? profile.bestpriceEndpoint : profile.journeysEndpoint,
|
||||||
body: query,
|
body: query,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// TODO poly conditional other endpoint
|
|
||||||
const formatRefreshJourneyReq = (ctx, refreshToken) => {
|
const formatRefreshJourneyReq = (ctx, refreshToken) => {
|
||||||
const {profile, opt} = ctx;
|
const {profile, opt} = ctx;
|
||||||
if (opt.tickets) {
|
if (opt.tickets) {
|
||||||
|
|
|
@ -69,7 +69,15 @@ const parseJourney = (ctx, jj) => { // j = raw journey
|
||||||
// TODO
|
// TODO
|
||||||
if (opt.scheduledDays && j.serviceDays) {
|
if (opt.scheduledDays && j.serviceDays) {
|
||||||
// todo [breaking]: rename to scheduledDates
|
// todo [breaking]: rename to scheduledDates
|
||||||
// res.scheduledDays = profile.parseScheduledDays(ctx, j.serviceDays);
|
// TODO parse scheduledDays as before
|
||||||
|
res.serviceDays = j.serviceDays.map(d => ({
|
||||||
|
irregular: d.irregular,
|
||||||
|
lastDateInPeriod: d.lastDateInPeriod || d.letztesDatumInZeitraum,
|
||||||
|
planningPeriodBegin: d.planningPeriodBegin || d.planungsZeitraumAnfang,
|
||||||
|
planningPeriodEnd: d.planningPeriodEnd || d.planungsZeitraumEnde,
|
||||||
|
regular: d.regular,
|
||||||
|
weekdays: d.weekdays || d.wochentage,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.price = profile.parsePrice(ctx, jj);
|
res.price = profile.parsePrice(ctx, jj);
|
||||||
|
|
|
@ -9,7 +9,7 @@ const parseLoadFactor = (opt, auslastung) => {
|
||||||
if (!auslastung) {
|
if (!auslastung) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const cls = opt.firstClass
|
const cls = opt.firstClass === true
|
||||||
? 'KLASSE_1'
|
? 'KLASSE_1'
|
||||||
: 'KLASSE_2';
|
: 'KLASSE_2';
|
||||||
const load = auslastung.find(a => a.klasse === cls)?.stufe;
|
const load = auslastung.find(a => a.klasse === cls)?.stufe;
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
|
const PARTIAL_FARE_HINT = 'Teilpreis / partial fare';
|
||||||
|
|
||||||
const parsePrice = (ctx, raw) => {
|
const parsePrice = (ctx, raw) => {
|
||||||
const p = raw.angebotsPreis || raw.angebote?.preise?.gesamt?.ab; // TODO teilpreis
|
const p = raw.angebotsPreis || raw.angebote?.preise?.gesamt?.ab || raw.abPreis;
|
||||||
if (p?.betrag) {
|
if (p?.betrag) {
|
||||||
|
const partialFare = raw.hasTeilpreis ?? raw.angebote?.preise?.istTeilpreis ?? raw.teilpreis;
|
||||||
return {
|
return {
|
||||||
amount: p.betrag,
|
amount: p.betrag,
|
||||||
currency: p.waehrung,
|
currency: p.waehrung,
|
||||||
hint: null,
|
hint: partialFare ? PARTIAL_FARE_HINT : null,
|
||||||
|
partialFare: partialFare,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -45,7 +48,7 @@ const parseTickets = (ctx, j) => {
|
||||||
partialFare: s.teilpreis,
|
partialFare: s.teilpreis,
|
||||||
};
|
};
|
||||||
if (s.teilpreis) {
|
if (s.teilpreis) {
|
||||||
p.addData = 'Teilpreis / partial fare';
|
p.addData = PARTIAL_FARE_HINT;
|
||||||
}
|
}
|
||||||
const conds = s.konditionsAnzeigen || s.konditionen;
|
const conds = s.konditionsAnzeigen || s.konditionen;
|
||||||
if (conds) {
|
if (conds) {
|
||||||
|
|
|
@ -8,14 +8,14 @@
|
||||||
|
|
||||||
This is an early version. What works:
|
This is an early version. What works:
|
||||||
|
|
||||||
* `journeys()`, `refreshJourney()` including tickets
|
* `journeys()`, `refreshJourney()` including tickets and bestprice option
|
||||||
* `locations()`, `nearby()`,
|
* `locations()`, `nearby()`,
|
||||||
* `departures()`, `arrivals()` boards
|
* `departures()`, `arrivals()` boards
|
||||||
* `trip()`
|
* `trip()`
|
||||||
|
|
||||||
What doesn't work:
|
What doesn't work:
|
||||||
|
|
||||||
* `journeys()` details like scheduledDays, stop/station groups, some line details ...
|
* `journeys()` details like stop/station groups, some line details ...
|
||||||
* loadFactor and other details in boards
|
* loadFactor and other details in boards
|
||||||
* certain stop details like products for `locations()` and geopositions for boards – this can be remedied with `enrichStations` in the config (turned on by default), enriching stop info with [db-hafas-stations](https://github.com/derhuerst/db-hafas-stations).
|
* certain stop details like products for `locations()` and geopositions for boards – this can be remedied with `enrichStations` in the config (turned on by default), enriching stop info with [db-hafas-stations](https://github.com/derhuerst/db-hafas-stations).
|
||||||
* some query options/filters (e.g. routingMode for journeys, direction for boards)
|
* some query options/filters (e.g. routingMode for journeys, direction for boards)
|
||||||
|
|
|
@ -99,184 +99,186 @@ const potsdamHbf = '8012666';
|
||||||
const berlinSüdkreuz = '8011113';
|
const berlinSüdkreuz = '8011113';
|
||||||
const kölnHbf = '8000207';
|
const kölnHbf = '8000207';
|
||||||
|
|
||||||
tap.test('journeys – Berlin Schwedter Str. to München Hbf', async (t) => {
|
if (!process.env.VCR_OFF) {
|
||||||
const res = await client.journeys(blnSchwedterStr, münchenHbf, {
|
tap.test('journeys – Berlin Schwedter Str. to München Hbf', async (t) => {
|
||||||
results: 4,
|
const res = await client.journeys(blnSchwedterStr, münchenHbf, {
|
||||||
departure: when,
|
results: 4,
|
||||||
stopovers: true,
|
departure: when,
|
||||||
});
|
stopovers: true,
|
||||||
|
});
|
||||||
|
|
||||||
await testJourneysStationToStation({
|
await testJourneysStationToStation({
|
||||||
test: t,
|
test: t,
|
||||||
res,
|
res,
|
||||||
validate,
|
validate,
|
||||||
fromId: blnSchwedterStr,
|
fromId: blnSchwedterStr,
|
||||||
toId: münchenHbf,
|
toId: münchenHbf,
|
||||||
});
|
});
|
||||||
// todo: find a journey where there pricing info is always available
|
// todo: find a journey where there pricing info is always available
|
||||||
for (let journey of res.journeys) {
|
for (let journey of res.journeys) {
|
||||||
if (journey.price) {
|
if (journey.price) {
|
||||||
assertValidPrice(t, journey.price);
|
assertValidPrice(t, journey.price);
|
||||||
|
}
|
||||||
|
if (journey.tickets) {
|
||||||
|
assertValidTickets(t, journey.tickets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (journey.tickets) {
|
t.end();
|
||||||
assertValidTickets(t, journey.tickets);
|
});
|
||||||
|
|
||||||
|
tap.test('refreshJourney – valid tickets', async (t) => {
|
||||||
|
const T_MOCK = 1710831600 * 1000; // 2024-03-19T08:00:00+01:00
|
||||||
|
const when = createWhen(dbProfile.timezone, dbProfile.locale, T_MOCK);
|
||||||
|
|
||||||
|
const journeysRes = await client.journeys(berlinHbf, münchenHbf, {
|
||||||
|
results: 4,
|
||||||
|
departure: when,
|
||||||
|
stopovers: true,
|
||||||
|
});
|
||||||
|
const refreshWithoutTicketsRes = await client.refreshJourney(journeysRes.journeys[0].refreshToken, {
|
||||||
|
tickets: false,
|
||||||
|
});
|
||||||
|
const refreshWithTicketsRes = await client.refreshJourney(journeysRes.journeys[0].refreshToken, {
|
||||||
|
tickets: true,
|
||||||
|
});
|
||||||
|
for (let res of [refreshWithoutTicketsRes, refreshWithTicketsRes]) {
|
||||||
|
if (res.journey.tickets !== undefined) {
|
||||||
|
assertValidTickets(t, res.journey.tickets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('refreshJourney – valid tickets', async (t) => {
|
t.end();
|
||||||
const T_MOCK = 1710831600 * 1000; // 2024-03-19T08:00:00+01:00
|
|
||||||
const when = createWhen(dbProfile.timezone, dbProfile.locale, T_MOCK);
|
|
||||||
|
|
||||||
const journeysRes = await client.journeys(berlinHbf, münchenHbf, {
|
|
||||||
results: 4,
|
|
||||||
departure: when,
|
|
||||||
stopovers: true,
|
|
||||||
});
|
|
||||||
const refreshWithoutTicketsRes = await client.refreshJourney(journeysRes.journeys[0].refreshToken, {
|
|
||||||
tickets: false,
|
|
||||||
});
|
|
||||||
const refreshWithTicketsRes = await client.refreshJourney(journeysRes.journeys[0].refreshToken, {
|
|
||||||
tickets: true,
|
|
||||||
});
|
|
||||||
for (let res of [refreshWithoutTicketsRes, refreshWithTicketsRes]) {
|
|
||||||
if (res.journey.tickets !== undefined) {
|
|
||||||
assertValidTickets(t, res.journey.tickets);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
// todo: journeys, only one product
|
|
||||||
|
|
||||||
tap.test('journeys – fails with no product', async (t) => {
|
|
||||||
await journeysFailsWithNoProduct({
|
|
||||||
test: t,
|
|
||||||
fetchJourneys: client.journeys,
|
|
||||||
fromId: blnSchwedterStr,
|
|
||||||
toId: münchenHbf,
|
|
||||||
when,
|
|
||||||
products: dbProfile.products,
|
|
||||||
});
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('Berlin Schwedter Str. to Torfstraße 17', async (t) => {
|
|
||||||
const torfstr = {
|
|
||||||
type: 'location',
|
|
||||||
address: 'Torfstraße 17',
|
|
||||||
latitude: 52.5416823,
|
|
||||||
longitude: 13.3491223,
|
|
||||||
};
|
|
||||||
const res = await client.journeys(blnSchwedterStr, torfstr, {
|
|
||||||
results: 3,
|
|
||||||
departure: when,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await testJourneysStationToAddress({
|
// todo: journeys, only one product
|
||||||
test: t,
|
|
||||||
res,
|
|
||||||
validate,
|
|
||||||
fromId: blnSchwedterStr,
|
|
||||||
to: torfstr,
|
|
||||||
});
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('Berlin Schwedter Str. to ATZE Musiktheater', async (t) => {
|
tap.test('journeys – fails with no product', async (t) => {
|
||||||
const atze = {
|
await journeysFailsWithNoProduct({
|
||||||
type: 'location',
|
|
||||||
id: '991598902',
|
|
||||||
poi: true,
|
|
||||||
name: 'Berlin, Atze Musiktheater für Kinder (Kultur und U',
|
|
||||||
latitude: 52.542417,
|
|
||||||
longitude: 13.350437,
|
|
||||||
};
|
|
||||||
const res = await client.journeys(blnSchwedterStr, atze, {
|
|
||||||
results: 3,
|
|
||||||
departure: when,
|
|
||||||
});
|
|
||||||
|
|
||||||
await testJourneysStationToPoi({
|
|
||||||
test: t,
|
|
||||||
res,
|
|
||||||
validate,
|
|
||||||
fromId: blnSchwedterStr,
|
|
||||||
to: atze,
|
|
||||||
});
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('journeys: via works – with detour', async (t) => {
|
|
||||||
// Going from Westhafen to Wedding via Württembergallee without detour
|
|
||||||
// is currently impossible. We check if the routing engine computes a detour.
|
|
||||||
const res = await client.journeys(westhafen, wedding, {
|
|
||||||
via: württembergallee,
|
|
||||||
results: 1,
|
|
||||||
departure: when,
|
|
||||||
stopovers: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await testJourneysWithDetour({
|
|
||||||
test: t,
|
|
||||||
res,
|
|
||||||
validate,
|
|
||||||
detourIds: [württembergallee],
|
|
||||||
});
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
// todo: walkingSpeed "Berlin - Charlottenburg, Hallerstraße" -> jungfernheide
|
|
||||||
// todo: without detour
|
|
||||||
|
|
||||||
|
|
||||||
// todo: with the DB endpoint, earlierRef/laterRef is missing queries many days in the future
|
|
||||||
tap.skip('earlier/later journeys, Jungfernheide -> München Hbf', async (t) => {
|
|
||||||
await testEarlierLaterJourneys({
|
|
||||||
test: t,
|
|
||||||
fetchJourneys: client.journeys,
|
|
||||||
validate,
|
|
||||||
fromId: jungfernheide,
|
|
||||||
toId: münchenHbf,
|
|
||||||
when,
|
|
||||||
});
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!process.env.VCR_MODE) {
|
|
||||||
tap.test('journeys – leg cycle & alternatives', async (t) => {
|
|
||||||
await testLegCycleAlternatives({
|
|
||||||
test: t,
|
test: t,
|
||||||
fetchJourneys: client.journeys,
|
fetchJourneys: client.journeys,
|
||||||
fromId: blnTiergarten,
|
fromId: blnSchwedterStr,
|
||||||
toId: blnJannowitzbrücke,
|
toId: münchenHbf,
|
||||||
|
when,
|
||||||
|
products: dbProfile.products,
|
||||||
|
});
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('Berlin Schwedter Str. to Torfstraße 17', async (t) => {
|
||||||
|
const torfstr = {
|
||||||
|
type: 'location',
|
||||||
|
address: 'Torfstraße 17',
|
||||||
|
latitude: 52.5416823,
|
||||||
|
longitude: 13.3491223,
|
||||||
|
};
|
||||||
|
const res = await client.journeys(blnSchwedterStr, torfstr, {
|
||||||
|
results: 3,
|
||||||
|
departure: when,
|
||||||
|
});
|
||||||
|
|
||||||
|
await testJourneysStationToAddress({
|
||||||
|
test: t,
|
||||||
|
res,
|
||||||
|
validate,
|
||||||
|
fromId: blnSchwedterStr,
|
||||||
|
to: torfstr,
|
||||||
|
});
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('Berlin Schwedter Str. to ATZE Musiktheater', async (t) => {
|
||||||
|
const atze = {
|
||||||
|
type: 'location',
|
||||||
|
id: '991598902',
|
||||||
|
poi: true,
|
||||||
|
name: 'Berlin, Atze Musiktheater für Kinder (Kultur und U',
|
||||||
|
latitude: 52.542417,
|
||||||
|
longitude: 13.350437,
|
||||||
|
};
|
||||||
|
const res = await client.journeys(blnSchwedterStr, atze, {
|
||||||
|
results: 3,
|
||||||
|
departure: when,
|
||||||
|
});
|
||||||
|
|
||||||
|
await testJourneysStationToPoi({
|
||||||
|
test: t,
|
||||||
|
res,
|
||||||
|
validate,
|
||||||
|
fromId: blnSchwedterStr,
|
||||||
|
to: atze,
|
||||||
|
});
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('journeys: via works – with detour', async (t) => {
|
||||||
|
// Going from Westhafen to Wedding via Württembergallee without detour
|
||||||
|
// is currently impossible. We check if the routing engine computes a detour.
|
||||||
|
const res = await client.journeys(westhafen, wedding, {
|
||||||
|
via: württembergallee,
|
||||||
|
results: 1,
|
||||||
|
departure: when,
|
||||||
|
stopovers: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await testJourneysWithDetour({
|
||||||
|
test: t,
|
||||||
|
res,
|
||||||
|
validate,
|
||||||
|
detourIds: [württembergallee],
|
||||||
|
});
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
// todo: walkingSpeed "Berlin - Charlottenburg, Hallerstraße" -> jungfernheide
|
||||||
|
// todo: without detour
|
||||||
|
|
||||||
|
|
||||||
|
// todo: with the DB endpoint, earlierRef/laterRef is missing queries many days in the future
|
||||||
|
tap.skip('earlier/later journeys, Jungfernheide -> München Hbf', async (t) => {
|
||||||
|
await testEarlierLaterJourneys({
|
||||||
|
test: t,
|
||||||
|
fetchJourneys: client.journeys,
|
||||||
|
validate,
|
||||||
|
fromId: jungfernheide,
|
||||||
|
toId: münchenHbf,
|
||||||
|
when,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!process.env.VCR_MODE) {
|
||||||
|
tap.test('journeys – leg cycle & alternatives', async (t) => {
|
||||||
|
await testLegCycleAlternatives({
|
||||||
|
test: t,
|
||||||
|
fetchJourneys: client.journeys,
|
||||||
|
fromId: blnTiergarten,
|
||||||
|
toId: blnJannowitzbrücke,
|
||||||
|
when,
|
||||||
|
});
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tap.test('refreshJourney', async (t) => {
|
||||||
|
const T_MOCK = 1710831600 * 1000; // 2024-03-19T08:00:00+01:00
|
||||||
|
const when = createWhen(dbProfile.timezone, dbProfile.locale, T_MOCK);
|
||||||
|
const validate = createValidate({...cfg, when});
|
||||||
|
|
||||||
|
await testRefreshJourney({
|
||||||
|
test: t,
|
||||||
|
fetchJourneys: client.journeys,
|
||||||
|
refreshJourney: client.refreshJourney,
|
||||||
|
validate,
|
||||||
|
fromId: jungfernheide,
|
||||||
|
toId: münchenHbf,
|
||||||
when,
|
when,
|
||||||
});
|
});
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
tap.test('refreshJourney', async (t) => {
|
|
||||||
const T_MOCK = 1710831600 * 1000; // 2024-03-19T08:00:00+01:00
|
|
||||||
const when = createWhen(dbProfile.timezone, dbProfile.locale, T_MOCK);
|
|
||||||
const validate = createValidate({...cfg, when});
|
|
||||||
|
|
||||||
await testRefreshJourney({
|
/*
|
||||||
test: t,
|
|
||||||
fetchJourneys: client.journeys,
|
|
||||||
refreshJourney: client.refreshJourney,
|
|
||||||
validate,
|
|
||||||
fromId: jungfernheide,
|
|
||||||
toId: münchenHbf,
|
|
||||||
when,
|
|
||||||
});
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
tap.skip('journeysFromTrip – U Mehringdamm to U Naturkundemuseum, reroute to Spittelmarkt.', async (t) => {
|
tap.skip('journeysFromTrip – U Mehringdamm to U Naturkundemuseum, reroute to Spittelmarkt.', async (t) => {
|
||||||
const blnMehringdamm = '730939';
|
const blnMehringdamm = '730939';
|
||||||
const blnStadtmitte = '732541';
|
const blnStadtmitte = '732541';
|
||||||
|
@ -366,33 +368,34 @@ tap.skip('journeysFromTrip – U Mehringdamm to U Naturkundemuseum, reroute to S
|
||||||
}
|
}
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
tap.test('trip details', async (t) => {
|
tap.test('trip details', async (t) => {
|
||||||
const res = await client.journeys(berlinHbf, münchenHbf, {
|
const res = await client.journeys(berlinHbf, münchenHbf, {
|
||||||
results: 1, departure: when,
|
results: 1, departure: when,
|
||||||
|
});
|
||||||
|
|
||||||
|
const p = res.journeys[0].legs.find(l => !l.walking);
|
||||||
|
t.ok(p.tripId, 'precondition failed');
|
||||||
|
t.ok(p.line.name, 'precondition failed');
|
||||||
|
|
||||||
|
const tripRes = await client.trip(p.tripId, {when});
|
||||||
|
|
||||||
|
const validate = createValidate(cfg, {
|
||||||
|
trip: (cfg) => {
|
||||||
|
const validateTrip = createValidateTrip(cfg);
|
||||||
|
const validateTripWithFakeDirection = (val, trip, name) => {
|
||||||
|
validateTrip(val, {
|
||||||
|
...trip,
|
||||||
|
direction: trip.direction || 'foo', // todo, see #49
|
||||||
|
}, name);
|
||||||
|
};
|
||||||
|
return validateTripWithFakeDirection;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
validate(t, tripRes, 'tripResult', 'tripRes');
|
||||||
|
|
||||||
|
t.end();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
const p = res.journeys[0].legs.find(l => !l.walking);
|
|
||||||
t.ok(p.tripId, 'precondition failed');
|
|
||||||
t.ok(p.line.name, 'precondition failed');
|
|
||||||
|
|
||||||
const tripRes = await client.trip(p.tripId, {when});
|
|
||||||
|
|
||||||
const validate = createValidate(cfg, {
|
|
||||||
trip: (cfg) => {
|
|
||||||
const validateTrip = createValidateTrip(cfg);
|
|
||||||
const validateTripWithFakeDirection = (val, trip, name) => {
|
|
||||||
validateTrip(val, {
|
|
||||||
...trip,
|
|
||||||
direction: trip.direction || 'foo', // todo, see #49
|
|
||||||
}, name);
|
|
||||||
};
|
|
||||||
return validateTripWithFakeDirection;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
validate(t, tripRes, 'tripResult', 'tripRes');
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('departures at Berlin Schwedter Str.', async (t) => {
|
tap.test('departures at Berlin Schwedter Str.', async (t) => {
|
||||||
const res = await client.departures(blnSchwedterStr, {
|
const res = await client.departures(blnSchwedterStr, {
|
||||||
|
|
1
test/fixtures/dbnav-refresh-journey.js
vendored
1
test/fixtures/dbnav-refresh-journey.js
vendored
|
@ -212,6 +212,7 @@ const dbNavJourney = {
|
||||||
amount: 43.99,
|
amount: 43.99,
|
||||||
currency: 'EUR',
|
currency: 'EUR',
|
||||||
hint: null,
|
hint: null,
|
||||||
|
partialFare: false,
|
||||||
},
|
},
|
||||||
tickets: [
|
tickets: [
|
||||||
{
|
{
|
||||||
|
|
2
test/fixtures/dbweb-journey.js
vendored
2
test/fixtures/dbweb-journey.js
vendored
|
@ -292,7 +292,7 @@ const dbwebJourney = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
refreshToken: '¶HKI¶T$A=1@O=Köln Hbf@X=6958730@Y=50943029@L=8000207@a=128@$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$202504110511$202504110512$S 12$$1$$$$$$§W$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$202504110512$202504110519$$$1$$$$$$§T$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$A=1@O=Nürnberg Hbf@X=11082989@Y=49445615@L=8000284@a=128@$202504110520$202504110858$ICE 523$$1$$$$$$',
|
refreshToken: '¶HKI¶T$A=1@O=Köln Hbf@X=6958730@Y=50943029@L=8000207@a=128@$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$202504110511$202504110512$S 12$$1$$$$$$§W$A=1@O=Köln Messe/Deutz@X=6975000@Y=50940872@L=8003368@a=128@$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$202504110512$202504110519$$$1$$$$$$§T$A=1@O=Köln Messe/Deutz Gl.11-12@X=6974065@Y=50941717@L=8073368@a=128@$A=1@O=Nürnberg Hbf@X=11082989@Y=49445615@L=8000284@a=128@$202504110520$202504110858$ICE 523$$1$$$$$$',
|
||||||
price: {amount: 31.49, currency: 'EUR', hint: null},
|
price: {amount: 31.49, currency: 'EUR', hint: null, partialFare: false},
|
||||||
tickets: [{
|
tickets: [{
|
||||||
name: 'from',
|
name: 'from',
|
||||||
priceObj: {
|
priceObj: {
|
||||||
|
|
1
test/fixtures/dbweb-refresh-journey.js
vendored
1
test/fixtures/dbweb-refresh-journey.js
vendored
|
@ -210,6 +210,7 @@ const dbJourney = {
|
||||||
amount: 27.99,
|
amount: 27.99,
|
||||||
currency: 'EUR',
|
currency: 'EUR',
|
||||||
hint: null,
|
hint: null,
|
||||||
|
partialFare: false,
|
||||||
},
|
},
|
||||||
tickets: [
|
tickets: [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue