mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-30 18:46:00 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c8ff2d2ff |
@@ -64,8 +64,8 @@ export function normalizeFlight(raw: AirtrailFlightRaw): AirtrailFlight {
|
|||||||
toCode: airportCode(raw.to),
|
toCode: airportCode(raw.to),
|
||||||
toName: raw.to?.name ?? null,
|
toName: raw.to?.name ?? null,
|
||||||
date: raw.date ?? null,
|
date: raw.date ?? null,
|
||||||
departure: raw.departureScheduled ?? null,
|
departure: raw.departureScheduled ?? raw.departure ?? null,
|
||||||
arrival: raw.arrivalScheduled ?? null,
|
arrival: raw.arrivalScheduled ?? raw.arrival ?? null,
|
||||||
airline: entityName(raw.airline),
|
airline: entityName(raw.airline),
|
||||||
flightNumber: raw.flightNumber ?? null,
|
flightNumber: raw.flightNumber ?? null,
|
||||||
aircraft: entityCode(raw.aircraft),
|
aircraft: entityCode(raw.aircraft),
|
||||||
@@ -103,11 +103,14 @@ function hasCoords(a: AirtrailAirport | null): a is AirtrailAirport & { lat: num
|
|||||||
|
|
||||||
/** Raw AirTrail flight → the data createReservation() expects (type:'flight'). */
|
/** Raw AirTrail flight → the data createReservation() expects (type:'flight'). */
|
||||||
export function mapFlightToReservation(raw: AirtrailFlightRaw): MappedReservation {
|
export function mapFlightToReservation(raw: AirtrailFlightRaw): MappedReservation {
|
||||||
// Read the SCHEDULED times only — TREK plans against the scheduled (booked) time,
|
// Prefer the scheduled (booked) time TREK plans against, but fall back to the
|
||||||
// not the actual/estimated `departure`/`arrival`. When a flight has no scheduled
|
// primary departure/arrival instant when AirTrail has no scheduled time. Manually
|
||||||
// time, the clock is left blank (date preserved) rather than fabricated.
|
// entered flights only set `departure`/`arrival` (the `*Scheduled` columns stay
|
||||||
const dep = localParts(raw.departureScheduled, raw.from?.tz ?? null);
|
// null), so reading scheduled alone dropped the clock — and the whole arrival —
|
||||||
const arr = localParts(raw.arrivalScheduled, raw.to?.tz ?? null);
|
// for the common case (#1336). Only when neither exists is the clock left blank
|
||||||
|
// (date preserved) rather than fabricated.
|
||||||
|
const dep = localParts(raw.departureScheduled ?? raw.departure, raw.from?.tz ?? null);
|
||||||
|
const arr = localParts(raw.arrivalScheduled ?? raw.arrival, raw.to?.tz ?? null);
|
||||||
|
|
||||||
const fromCode = airportCode(raw.from);
|
const fromCode = airportCode(raw.from);
|
||||||
const toCode = airportCode(raw.to);
|
const toCode = airportCode(raw.to);
|
||||||
@@ -194,8 +197,11 @@ export function canonicalHash(raw: AirtrailFlightRaw): string {
|
|||||||
to: airportCode(raw.to),
|
to: airportCode(raw.to),
|
||||||
date: raw.date ?? null,
|
date: raw.date ?? null,
|
||||||
datePrecision: raw.datePrecision ?? 'day',
|
datePrecision: raw.datePrecision ?? 'day',
|
||||||
departureScheduled: raw.departureScheduled ?? null,
|
// Hash the same instant the import uses (scheduled, else primary) so a change to
|
||||||
arrivalScheduled: raw.arrivalScheduled ?? null,
|
// whichever time TREK actually shows triggers a re-sync — and existing flights
|
||||||
|
// imported without a scheduled time re-sync once to pick up their clock (#1336).
|
||||||
|
departureScheduled: raw.departureScheduled ?? raw.departure ?? null,
|
||||||
|
arrivalScheduled: raw.arrivalScheduled ?? raw.arrival ?? null,
|
||||||
airline: entityCode(raw.airline),
|
airline: entityCode(raw.airline),
|
||||||
flightNumber: raw.flightNumber ?? null,
|
flightNumber: raw.flightNumber ?? null,
|
||||||
aircraft: entityCode(raw.aircraft),
|
aircraft: entityCode(raw.aircraft),
|
||||||
|
|||||||
@@ -51,11 +51,17 @@ describe('airtrailMapper.normalizeFlight', () => {
|
|||||||
flightNumber: 'BA178',
|
flightNumber: 'BA178',
|
||||||
seatClass: 'economy',
|
seatClass: 'economy',
|
||||||
});
|
});
|
||||||
// The picker preview surfaces the scheduled times, not the actual ones.
|
// The picker preview prefers the scheduled times over the actual ones.
|
||||||
expect(n.departure).toBe('2021-09-01T23:00:00.000+00:00');
|
expect(n.departure).toBe('2021-09-01T23:00:00.000+00:00');
|
||||||
expect(n.arrival).toBe('2021-09-02T07:00:00.000+00:00');
|
expect(n.arrival).toBe('2021-09-02T07:00:00.000+00:00');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('#1336 surfaces the primary departure/arrival when there is no scheduled time', () => {
|
||||||
|
const n = normalizeFlight(flight({ departureScheduled: null, arrivalScheduled: null }));
|
||||||
|
expect(n.departure).toBe('2021-09-01T23:42:00.000+00:00');
|
||||||
|
expect(n.arrival).toBe('2021-09-02T07:42:00.000+00:00');
|
||||||
|
});
|
||||||
|
|
||||||
it('falls back to ICAO when IATA is missing and tolerates null airports', () => {
|
it('falls back to ICAO when IATA is missing and tolerates null airports', () => {
|
||||||
const n = normalizeFlight(flight({ from: airport({ iata: null }), to: null }));
|
const n = normalizeFlight(flight({ from: airport({ iata: null }), to: null }));
|
||||||
expect(n.fromCode).toBe('KJFK');
|
expect(n.fromCode).toBe('KJFK');
|
||||||
@@ -74,8 +80,8 @@ describe('airtrailMapper.mapFlightToReservation', () => {
|
|||||||
expect(m.reservation_end_time).toBe('2021-09-02T08:00');
|
expect(m.reservation_end_time).toBe('2021-09-02T08:00');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('leaves the clock blank (date only) when the flight has no scheduled time', () => {
|
it('leaves the clock blank (date only) when the flight has no time at all', () => {
|
||||||
const m = mapFlightToReservation(flight({ departureScheduled: null, arrivalScheduled: null }));
|
const m = mapFlightToReservation(flight({ departure: null, arrival: null, departureScheduled: null, arrivalScheduled: null }));
|
||||||
// Date is preserved from the AirTrail canonical date; no fabricated 00:00.
|
// Date is preserved from the AirTrail canonical date; no fabricated 00:00.
|
||||||
expect(m.reservation_time).toBe('2021-09-01');
|
expect(m.reservation_time).toBe('2021-09-01');
|
||||||
expect(m.reservation_end_time).toBeNull();
|
expect(m.reservation_end_time).toBeNull();
|
||||||
@@ -83,6 +89,17 @@ describe('airtrailMapper.mapFlightToReservation', () => {
|
|||||||
expect(m.endpoints.find(e => e.role === 'to')?.local_time).toBeNull();
|
expect(m.endpoints.find(e => e.role === 'to')?.local_time).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('#1336 falls back to the primary departure/arrival when AirTrail has no scheduled times', () => {
|
||||||
|
// Manually-entered AirTrail flights set only departure/arrival; *Scheduled stays null.
|
||||||
|
const m = mapFlightToReservation(flight({ departureScheduled: null, arrivalScheduled: null }));
|
||||||
|
// departure 23:42 UTC at JFK (EDT) = 19:42 local; arrival 07:42 UTC at LHR (BST) = 08:42.
|
||||||
|
expect(m.reservation_time).toBe('2021-09-01T19:42');
|
||||||
|
expect(m.reservation_end_time).toBe('2021-09-02T08:42');
|
||||||
|
expect(m.endpoints.find(e => e.role === 'from')?.local_time).toBe('19:42');
|
||||||
|
expect(m.endpoints.find(e => e.role === 'to')?.local_time).toBe('08:42');
|
||||||
|
expect(m.endpoints.find(e => e.role === 'to')?.local_date).toBe('2021-09-02');
|
||||||
|
});
|
||||||
|
|
||||||
it('builds two endpoints with codes, coords and timezones', () => {
|
it('builds two endpoints with codes, coords and timezones', () => {
|
||||||
const m = mapFlightToReservation(flight());
|
const m = mapFlightToReservation(flight());
|
||||||
expect(m.endpoints).toHaveLength(2);
|
expect(m.endpoints).toHaveLength(2);
|
||||||
@@ -142,8 +159,8 @@ describe('airtrailMapper.mapFlightToReservation', () => {
|
|||||||
expect(m.endpoints.find(e => e.role === 'to')).toBeDefined();
|
expect(m.endpoints.find(e => e.role === 'to')).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('leaves the end time null for a partial flight with no scheduled arrival', () => {
|
it('leaves the end time null for a partial flight with no arrival time at all', () => {
|
||||||
const m = mapFlightToReservation(flight({ arrivalScheduled: null }));
|
const m = mapFlightToReservation(flight({ arrival: null, arrivalScheduled: null }));
|
||||||
expect(m.reservation_end_time).toBeNull();
|
expect(m.reservation_end_time).toBeNull();
|
||||||
expect(m.reservation_time).toBe('2021-09-01T19:00');
|
expect(m.reservation_time).toBe('2021-09-01T19:00');
|
||||||
});
|
});
|
||||||
@@ -164,12 +181,20 @@ describe('airtrailMapper.canonicalHash', () => {
|
|||||||
expect(canonicalHash(flight())).not.toBe(
|
expect(canonicalHash(flight())).not.toBe(
|
||||||
canonicalHash(flight({ departureScheduled: '2021-09-01T22:00:00.000+00:00' })),
|
canonicalHash(flight({ departureScheduled: '2021-09-01T22:00:00.000+00:00' })),
|
||||||
);
|
);
|
||||||
// ...but a change to the actual time alone must not (TREK never shows it).
|
// ...but a change to the actual time alone must not (TREK shows the scheduled one).
|
||||||
expect(canonicalHash(flight())).toBe(
|
expect(canonicalHash(flight())).toBe(
|
||||||
canonicalHash(flight({ departure: '2021-09-01T20:00:00.000+00:00', arrival: '2021-09-02T05:00:00.000+00:00' })),
|
canonicalHash(flight({ departure: '2021-09-01T20:00:00.000+00:00', arrival: '2021-09-02T05:00:00.000+00:00' })),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('#1336 tracks the primary departure when there is no scheduled time', () => {
|
||||||
|
// With no scheduled time, departure IS what TREK imports, so a change must re-sync.
|
||||||
|
const manual = flight({ departureScheduled: null, arrivalScheduled: null });
|
||||||
|
expect(canonicalHash(manual)).not.toBe(
|
||||||
|
canonicalHash(flight({ departureScheduled: null, arrivalScheduled: null, departure: '2021-09-01T20:00:00.000+00:00' })),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('is independent of seat ordering', () => {
|
it('is independent of seat ordering', () => {
|
||||||
const a = flight({
|
const a = flight({
|
||||||
seats: [
|
seats: [
|
||||||
|
|||||||
Reference in New Issue
Block a user