@sports-alliance/sports-lib
Version:
A Library to for importing / exporting and processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc
1,155 lines (986 loc) • 43.3 kB
text/typescript
import fs from 'fs';
import { SportsLib } from '../src';
import { EventInterface } from '../src/events/event.interface';
import * as strava_3156040843 from './fixtures/strava-streams-compliance/suunto_export/strava_3156040843.json';
import * as strava_3182900697 from './fixtures/strava-streams-compliance/suunto_export/strava_3182900697.json';
import * as strava_343080886 from './fixtures/strava-streams-compliance/garmin_export/strava_343080886.json';
import * as strava_3171438371_gpx from './fixtures/strava-streams-compliance/garmin_export/strava_3171472783_gpx.json';
import * as strava_3171487458_tcx from './fixtures/strava-streams-compliance/garmin_export/strava_3171487458_tcx.json';
import * as strava_3183465494 from './fixtures/strava-streams-compliance/COROS_export/strava_3183465494.json';
import * as strava_3183490558 from './fixtures/strava-streams-compliance/COROS_export/strava_3183490558.json';
import * as strava_2709634581 from './fixtures/strava-streams-compliance/suunto_export/strava_2709634581.json';
import { DataAltitude } from '../src/data/data.altitude';
import { DataHeartRate } from '../src/data/data.heart-rate';
import { DataCadence } from '../src/data/data.cadence';
import { DataTemperature } from '../src/data/data.temperature';
import { DataPower } from '../src/data/data.power';
import { DataDistance } from '../src/data/data.distance';
import { DataGrade } from '../src/data/data.grade';
export const GRADE_TOLERANCE = 1.5;
function clone(obj: any) {
return JSON.parse(JSON.stringify(obj));
}
describe('Strava data compliance', () => {
const averageDeltaBetweenStreams = (actualStream: number[], expectedStream: number[]) => {
let deltaSum = 0;
actualStream.forEach((value, index) => {
deltaSum += Math.abs(value - expectedStream[index]);
});
return deltaSum / actualStream.length;
};
describe('Compliance with suunto export (flat activity)', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/suunto_export/5e5fde38c2de24635a30d383.fit';
const buffer = fs.readFileSync(path);
it(`should match time`, done => {
// Given
const stravaCadenceStream = clone(strava_3156040843.time);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true).length
);
expect(stravaCadenceStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
it(`should match altitude`, done => {
// Given
const stravaAltitudeStream = clone(strava_3156040843.altitude);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaAltitudeStream.length).toEqual(event.getFirstActivity().getStreamData(DataAltitude.type).length);
expect(stravaAltitudeStream).toEqual(
event
.getFirstActivity()
.getStreamData(DataAltitude.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match heart rate`, done => {
// Given
const stravaHeartRateStream = clone(strava_3156040843.heartrate);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaHeartRateStream.length).toEqual(event.getFirstActivity().getStreamData(DataHeartRate.type).length);
expect(stravaHeartRateStream).toEqual(
event
.getFirstActivity()
.getStreamData(DataHeartRate.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match cadence`, done => {
// Given
const stravaCadenceStream = clone(strava_3156040843.cadence);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(event.getFirstActivity().getStreamData(DataCadence.type).length);
expect(stravaCadenceStream).toEqual(
event
.getFirstActivity()
.getStreamData(DataCadence.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match temperature`, done => {
// Given
const stravaTemperatureStream = clone(strava_3156040843.temp);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaTemperatureStream.length).toEqual(
event.getFirstActivity().getStreamData(DataTemperature.type).length
);
expect(stravaTemperatureStream).toEqual(
event
.getFirstActivity()
.getStreamData(DataTemperature.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match power`, done => {
// Given
const stravaPowerStream = clone(strava_3156040843.watts);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaPowerStream.length).toEqual(event.getFirstActivity().getStreamData(DataPower.type).length);
expect(stravaPowerStream).toEqual(
event
.getFirstActivity()
.getStreamData(DataPower.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match distance with x% error max`, done => {
// Given
const tolerance = 0.6; // percent
const stravaDistanceStream = clone(strava_3156040843.distance);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = event
.getFirstActivity()
.getStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaDistanceStream.length).toEqual(streamData.length);
const commonCount = stravaDistanceStream.filter(
(value: number | null) => streamData.indexOf(value) !== -1
).length;
expect(commonCount + Math.ceil((stravaDistanceStream.length * tolerance) / 100)).toBeGreaterThanOrEqual(
stravaDistanceStream.length
);
// expect(stravaDistanceStream).toEqual(streamData);
done();
});
});
it(`should have an average grade diff lower than ${GRADE_TOLERANCE}%`, done => {
// Given
const toleranceAvgGradeDelta = GRADE_TOLERANCE;
const stravaGradeStream = clone(strava_3156040843.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
describe('Compliance with suunto export (flat activity) without paused records', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/suunto_export/5e5fde38c2de24635a30d383-2.fit';
const buffer = fs.readFileSync(path);
it(`should match time`, done => {
// Given
const stravaCadenceStream = clone(strava_3182900697.time);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true).length
);
expect(stravaCadenceStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
it(`should match altitude`, done => {
// Given
const stravaAltitudeStream = clone(strava_3182900697.altitude);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaAltitudeStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataAltitude.type).length
);
expect(stravaAltitudeStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataAltitude.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match heart rate`, done => {
// Given
const stravaHeartRateStream = clone(strava_3182900697.heartrate);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaHeartRateStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).length
);
expect(stravaHeartRateStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataHeartRate.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match cadence`, done => {
// Given
const stravaCadenceStream = clone(strava_3182900697.cadence);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataCadence.type).length
);
expect(stravaCadenceStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataCadence.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match distance with x% error max`, done => {
// Given
const tolerance = 0.6; // percent
const stravaDistanceStream = clone(strava_3182900697.distance);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = event
.getFirstActivity()
.getSquashedStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaDistanceStream.length).toEqual(streamData.length);
const commonCount = stravaDistanceStream.filter(
(value: number | null) => streamData.indexOf(value) !== -1
).length;
expect(commonCount + Math.ceil((stravaDistanceStream.length * tolerance) / 100)).toBeGreaterThanOrEqual(
stravaDistanceStream.length
);
// expect(stravaDistanceStream).toEqual(streamData);
done();
});
});
it(`should have an average grade diff lower than ${GRADE_TOLERANCE}%`, done => {
// Given
const toleranceAvgGradeDelta = GRADE_TOLERANCE;
const stravaGradeStream = clone(strava_3182900697.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
describe('Compliance with suunto export (uphill 1800 activity) without paused records ', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/suunto_export/suunto-uphill.fit';
const buffer = fs.readFileSync(path);
it(`should match time`, done => {
// Given
const stravaCadenceStream = clone(strava_2709634581.time);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(event.getFirstActivity().generateTimeStream().getData(true).length);
expect(stravaCadenceStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
it(`should match altitude`, done => {
// Given
const stravaAltitudeStream = clone(strava_2709634581.altitude);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaAltitudeStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataAltitude.type).length
);
expect(stravaAltitudeStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataAltitude.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match heart rate`, done => {
// Given
const stravaHeartRateStream = clone(strava_2709634581.heartrate);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaHeartRateStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).length
);
expect(stravaHeartRateStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataHeartRate.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match cadence`, done => {
// Given
const stravaCadenceStream = clone(strava_2709634581.cadence);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataCadence.type).length
);
expect(stravaCadenceStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataCadence.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it.skip(`should match distance with x% error max`, done => {
// Given
const tolerance = 0.6; // percent
const stravaDistanceStream = clone(strava_2709634581.distance);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = event
.getFirstActivity()
.getSquashedStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
// expect(stravaDistanceStream.length).toEqual(streamData.length);
const commonCount = stravaDistanceStream.filter(
(value: number | null) => streamData.indexOf(value) !== -1
).length;
// expect(commonCount + Math.ceil((stravaDistanceStream.length * tolerance) / 100)).toBeGreaterThanOrEqual(stravaDistanceStream.length);
expect(stravaDistanceStream).toEqual(streamData);
done();
});
});
it.skip(`should have an average grade diff lower than ${GRADE_TOLERANCE}%`, done => {
// Given
const toleranceAvgGradeDelta = GRADE_TOLERANCE;
const stravaGradeStream = clone(strava_3182900697.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
describe('Compliance with COROS export', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/COROS_export/OutdoorRun20200314145229.fit';
const buffer = fs.readFileSync(path);
it(`should match time`, done => {
// Given
const stravaCadenceStream = clone(strava_3183465494.time);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true).length
);
expect(stravaCadenceStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
it(`should match altitude`, done => {
// Given
const stravaAltitudeStream = clone(strava_3183465494.altitude);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaAltitudeStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataAltitude.type).length
);
expect(stravaAltitudeStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataAltitude.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match heart rate`, done => {
// Given
const stravaHeartRateStream = clone(strava_3183465494.heartrate);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaHeartRateStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).length
);
expect(stravaHeartRateStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataHeartRate.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match cadence`, done => {
// Given
const stravaCadenceStream = clone(strava_3183465494.cadence);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataCadence.type).length
);
expect(stravaCadenceStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataCadence.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match distance`, done => {
// Given
const tolerance = 0.6; // percent
const stravaDistanceStream = clone(strava_3183465494.distance);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = event
.getFirstActivity()
.getSquashedStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaDistanceStream.length).toEqual(streamData.length);
expect(stravaDistanceStream).toEqual(streamData);
done();
});
});
it(`should have an average grade diff lower than ${GRADE_TOLERANCE}%`, done => {
// Given
const toleranceAvgGradeDelta = GRADE_TOLERANCE;
const stravaGradeStream = clone(strava_3183465494.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
describe('Compliance with COROS export trail', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/COROS_export/TrailRun20200215111630.fit';
const buffer = fs.readFileSync(path);
it(`should match time`, done => {
// Given
const stravaCadenceStream = clone(strava_3183490558.time);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true).length
);
expect(stravaCadenceStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
it(`should match altitude`, done => {
// Given
const stravaAltitudeStream = clone(strava_3183490558.altitude);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaAltitudeStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataAltitude.type).length
);
expect(stravaAltitudeStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataAltitude.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match heart rate`, done => {
// Given
const stravaHeartRateStream = clone(strava_3183490558.heartrate);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaHeartRateStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).length
);
expect(stravaHeartRateStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataHeartRate.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match cadence`, done => {
// Given
const stravaCadenceStream = clone(strava_3183490558.cadence);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataCadence.type).length
);
// @todo leaving this to fail to investigate how to fill linear as seen with Suunto
expect(stravaCadenceStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataCadence.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it.skip(`should match distance`, done => {
// Given
const tolerance = 0.6; // percent
const stravaDistanceStream = clone(strava_3183490558.distance);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = event
.getFirstActivity()
.getSquashedStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaDistanceStream.length).toEqual(streamData.length);
expect(stravaDistanceStream).toEqual(streamData);
done();
});
});
it.skip(`should have an average grade diff lower than ${GRADE_TOLERANCE}%`, done => {
// Given
const toleranceAvgGradeDelta = GRADE_TOLERANCE;
const stravaGradeStream = clone(strava_3183490558.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
describe('Compliance with garmin export (hilly activity: https://connect.garmin.com/modern/activity/828989227 / https://www.strava.com/activities/343080886)', () => {
describe('Fit file version', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/garmin_export/garmin_828989227.fit';
const buffer = fs.readFileSync(path);
it(`should match time`, done => {
// Given
const stravaCadenceStream = clone(strava_343080886.time);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true).length
);
expect(stravaCadenceStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
it(`should match altitude`, done => {
// Given
const stravaAltitudeStream = clone(strava_343080886.altitude);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaAltitudeStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataAltitude.type).length
);
expect(stravaAltitudeStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataAltitude.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match heart rate`, done => {
// Given
const stravaHeartRateStream = clone(strava_343080886.heartrate);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaHeartRateStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).length
);
expect(stravaHeartRateStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataHeartRate.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match cadence`, done => {
// Given
const stravaCadenceStream = clone(strava_343080886.cadence);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataCadence.type).length
);
expect(stravaCadenceStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataCadence.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match distance with x% error max`, done => {
// Given
const tolerance = 0.0; // percent
const stravaDistanceStream = clone(strava_343080886.distance);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaDistanceStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataDistance.type).length
);
const streamData = event
.getFirstActivity()
.getSquashedStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaDistanceStream).toEqual(streamData);
done();
});
});
it(`should have an average grade diff lower than ${GRADE_TOLERANCE}%`, done => {
// Given
const toleranceAvgGradeDelta = GRADE_TOLERANCE;
const stravaGradeStream = clone(strava_343080886.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
describe('Tcx file version', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/garmin_export/garmin_828989227.tcx';
const doc = new DOMParser().parseFromString(fs.readFileSync(path).toString(), 'application/xml');
it(`should match time`, done => {
// Given
const stravaCadenceStream = clone(strava_3171487458_tcx.time);
// When
const eventInterfacePromise = SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true).length
);
expect(stravaCadenceStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
it(`should match altitude`, done => {
// Given
const stravaAltitudeStream = clone(strava_3171487458_tcx.altitude);
// When
const eventInterfacePromise = SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaAltitudeStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataAltitude.type).length
);
expect(stravaAltitudeStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataAltitude.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match heart rate`, done => {
// Given
const stravaHeartRateStream = clone(strava_3171487458_tcx.heartrate);
// When
const eventInterfacePromise = SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaHeartRateStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).length
);
expect(stravaHeartRateStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataHeartRate.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match cadence`, done => {
// Given
const stravaCadenceStream = clone(strava_3171487458_tcx.cadence);
// When
const eventInterfacePromise = SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaCadenceStream.length).toEqual(
event.getFirstActivity().getSquashedStreamData(DataCadence.type).length
);
expect(stravaCadenceStream).toEqual(
event
.getFirstActivity()
.getSquashedStreamData(DataCadence.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10))
);
done();
});
});
it(`should match distance with x% error max`, done => {
// Given
const tolerance = 2.6; // percent
const toleranceDelta = 0.1;
const stravaDistanceStream = clone(strava_3171487458_tcx.distance);
// When
const eventInterfacePromise = SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaDistanceStream.length).toEqual(streamData.length);
// const commonCount = stravaDistanceStream
// .filter((value: number) => streamData.indexOf(value) !== -1).length;
// We find the common then add the % tolerance and we check if its more than equal to the "strava" stream
// expect(commonCount + Math.ceil((stravaDistanceStream.length * tolerance) / 100)).toBeGreaterThanOrEqual(stravaDistanceStream.length);
expect(stravaDistanceStream).toMatchObject(streamData);
// const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaDistanceStream);
// expect(deltaBetweenStreams).toBeLessThan(toleranceDelta);
done();
});
});
it(`should have an average grade diff lower than ${GRADE_TOLERANCE}%`, done => {
// Given
const toleranceAvgGradeDelta = GRADE_TOLERANCE;
const stravaGradeStream = clone(strava_343080886.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
describe('GPX file version', () => {
const path = __dirname + '/fixtures/strava-streams-compliance/garmin_export/garmin_828989227.gpx';
const gpxString = fs.readFileSync(path).toString();
it(`should match time`, done => {
// Given
const stravaTimeStream = clone(strava_3171438371_gpx.time);
// When
const eventInterfacePromise = SportsLib.importFromGPX(gpxString);
// Then
eventInterfacePromise.then((event: EventInterface) => {
expect(stravaTimeStream.length).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true).length
);
expect(stravaTimeStream).toEqual(
event.getFirstActivity().generateTimeStream([DataDistance.type, DataAltitude.type]).getData(true)
);
done();
});
});
// Strava adds their own altitude so no go from here
// it(`should match altitude`, done => {
//
// // Given
// const stravaAltitudeStream = clone(strava_3171438371_gpx.altitude);
//
// // When
// const eventInterfacePromise = SportsLib.importFromGPX(gpxString);
//
// // Then
// eventInterfacePromise.then((event: EventInterface) => {
// expect(stravaAltitudeStream.length).toEqual(event.getFirstActivity().getSquashedStreamData(DataAltitude.type).length);
// expect(stravaAltitudeStream).toEqual(event.getFirstActivity().getSquashedStreamData(DataAltitude.type).map(value => value === null ? null : Math.round(value * 10) / 10));
// done();
// });
// });
// we skip this because https://support.strava.com/hc/en-us/community/posts/360054808092-Not-getting-Garmin-heart-rate-data-into-Strava
// it(`should match heart rate`, done => {
//
// // Given
// const stravaHeartRateStream = clone(strava_3171438371.heartrate);
//
// // When
// const eventInterfacePromise = SportsLib.importFromGPX(gpxString);
//
// // Then
// eventInterfacePromise.then((event: EventInterface) => {
// expect(stravaHeartRateStream.length).toEqual(event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).length);
// expect(stravaHeartRateStream).toEqual(event.getFirstActivity().getSquashedStreamData(DataHeartRate.type).map(value => value === null ? null : Math.round(value * 10) / 10));
// done();
// });
// });
// we skip this because https://support.strava.com/hc/en-us/community/posts/360054808092-Not-getting-Garmin-heart-rate-data-into-Strava
// it(`should match cadence`, done => {
//
// // Given
// const stravaCadenceStream = clone(strava_3171438371.cadence);
//
// // When
// const eventInterfacePromise = SportsLib.importFromGPX(gpxString);
//
// // Then
// eventInterfacePromise.then((event: EventInterface) => {
// expect(stravaCadenceStream.length).toEqual(event.getFirstActivity().getSquashedStreamData(DataCadence.type).length);
// expect(stravaCadenceStream).toEqual(event.getFirstActivity().getSquashedStreamData(DataCadence.type).map(value => value === null ? null : Math.round(value * 10) / 10));
// done();
// });
// });
// @todo try different geolib adapters etc to make this diff smaller or investigate more on how strava
// Bumps the distance
it(`should match distance with x% error max`, done => {
// Given
const deltaTolerance = 15; // percent
const stravaDistanceStream = clone(strava_3171438371_gpx.distance);
// When
const eventInterfacePromise = SportsLib.importFromGPX(gpxString);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataDistance.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaDistanceStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaDistanceStream);
expect(deltaBetweenStreams).toBeLessThan(deltaTolerance);
done();
});
});
// Tolerance is higher since distance is different unfortunately (see above test)
it(`should have an average grade diff lower than 3.2%`, done => {
// Given
const toleranceAvgGradeDelta = 3.2;
const stravaGradeStream = clone(strava_3171438371_gpx.grade_smooth);
// When
const eventInterfacePromise = SportsLib.importFromGPX(gpxString);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const streamData = <number[]>event
.getFirstActivity()
.getSquashedStreamData(DataGrade.type)
.map(value => (value === null ? null : Math.round(value * 10) / 10));
expect(stravaGradeStream.length).toEqual(streamData.length);
const deltaBetweenStreams = averageDeltaBetweenStreams(streamData, stravaGradeStream);
console.log(`Delta is ${deltaBetweenStreams}`);
expect(deltaBetweenStreams).toBeLessThan(toleranceAvgGradeDelta);
done();
});
});
});
});
});