@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
2,033 lines • 161 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const index_1 = require("../index");
const data_heart_rate_1 = require("../data/data.heart-rate");
const data_longitude_degrees_1 = require("../data/data.longitude-degrees");
const data_latitude_degrees_1 = require("../data/data.latitude-degrees");
const data_distance_1 = require("../data/data.distance");
const data_speed_1 = require("../data/data.speed");
const data_cadence_1 = require("../data/data.cadence");
const data_power_1 = require("../data/data.power");
const data_altitude_1 = require("../data/data.altitude");
const activity_types_1 = require("../activities/activity.types");
const data_ascent_1 = require("../data/data.ascent");
const spec_utils_1 = require("./spec-utils");
const data_timer_time_1 = require("../data/data.timer-time");
const data_pause_1 = require("../data/data.pause");
const data_energy_1 = require("../data/data.energy");
const data_heart_rate_avg_1 = require("../data/data.heart-rate-avg");
const data_cadence_max_1 = require("../data/data.cadence-max");
const data_power_avg_1 = require("../data/data.power-avg");
const data_cadence_avg_1 = require("../data/data.cadence-avg");
const data_speed_avg_1 = require("../data/data.speed-avg");
const data_power_max_1 = require("../data/data.power-max");
const data_descent_1 = require("../data/data.descent");
const data_heart_rate_max_1 = require("../data/data.heart-rate-max");
const data_pool_length_1 = require("../data/data.pool-length");
const data_total_cycles_1 = require("../data/data-total-cycles");
const data_active_lengths_1 = require("../data/data-active-lengths");
const data_pace_avg_1 = require("../data/data.pace-avg");
const data_temperature_avg_1 = require("../data/data.temperature-avg");
const data_moving_time_1 = require("../data/data.moving-time");
const data_elapsed_time_1 = require("../data/data.elapsed-time");
const xmldom_1 = __importDefault(require("@xmldom/xmldom"));
const data_grade_adjusted_pace_avg_1 = require("../data/data.grade-adjusted-pace-avg");
const data_grade_adjusted_speed_1 = require("../data/data.grade-adjusted-speed");
const data_active_lap_1 = require("../data/data-active-lap");
const data_swolf_25m_1 = require("../data/data.swolf-25m");
const data_swolf_50m_1 = require("../data/data.swolf-50m");
const data_power_balance_left_1 = require("../data/data.power-balance-left");
const data_power_balance_right_1 = require("../data/data.power-balance-right");
const data_power_torque_effectiveness_left_1 = require("../data/data.power-torque-effectiveness-left");
const data_power_torque_effectiveness_right_1 = require("../data/data.power-torque-effectiveness-right");
const data_power_pedal_smoothness_right_1 = require("../data/data.power-pedal-smoothness-right");
const data_power_pedal_smoothness_left_1 = require("../data/data.power-pedal-smoothness-left");
const data_power_normalized_1 = require("../data/data.power-normalized");
const data_power_intensity_factor_1 = require("../data/data.power-intensity-factor");
const data_training_stress_score_1 = require("../data/data.training-stress-score");
const data_power_work_1 = require("../data/data.power-work");
const data_cycling_standing_time_1 = require("../data/data.cycling-standing-time");
const data_cycling_seated_time_1 = require("../data/data.cycling-seated-time");
const data_vertical_oscillation_avg_1 = require("../data/data.vertical-oscillation-avg");
const data_vertical_ratio_avg_1 = require("../data/data.vertical-ratio-avg");
const data_ground_contact_time_avg_1 = require("../data/data.ground-contact-time-avg");
const data_ground_contact_time_balance_left_1 = require("../data/data-ground-contact-time-balance-left");
const data_ground_contact_time_balance_right_1 = require("../data/data-ground-contact-time-balance-right");
const data_stance_time_balance_right_1 = require("../data/data-stance-time-balance-right");
const data_stance_time_balance_left_1 = require("../data/data-stance-time-balance-left");
const data_avg_stride_length_1 = require("../data/data.avg-stride-length");
const data_aerobic_training_effect_1 = require("../data/data-aerobic-training-effect");
const data_anaerobic_training_effect_1 = require("../data/data-anaerobic-training-effect");
const data_altitude_smooth_1 = require("../data/data.altitude-smooth");
const data_grade_1 = require("../data/data.grade");
const activity_parsing_options_1 = require("../activities/activity-parsing-options");
const helpers_1 = require("../events/utilities/helpers");
describe('FIT/TCX/GPX activity parsing compliance', () => {
const domParser = new xmldom_1.default.DOMParser();
/* it('Template: should parse FIT activity file', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/xxxxxxxx OR https://www.strava.com/activities/xxxxxxx (should be "public")
const path = '/path/to/your/file.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const activity = event.getFirstActivity();
done();
});
});*/
/* it('Template: should parse TCX activity file', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/xxxxxxxx OR https://www.strava.com/activities/xxxxxxx (should be "public")
const path = '/path/to/your/file.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const activity = event.getFirstActivity();
done();
});
});*/
/* it('Template: should parse GPX activity file', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/xxxxxxxx OR https://www.strava.com/activities/xxxxxxx (should be "public")
const path = '/path/to/your/file.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = SportsLib.importFromGPX(gpxString, xmldom.DOMParser);
// Then
eventInterfacePromise.then((event: EventInterface) => {
const activity = event.getFirstActivity();
done();
});
});*/
describe('Swimming', () => {
describe('FIT', () => {
it('should parse swimming pool FIT file (1)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/6688025408 OR https://www.strava.com/activities/4232464474
const path = __dirname + '/fixtures/swim/fit/6688025408.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Forerunner 935');
expect(activity.creator.productId).toEqual(2691);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Swimming);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(2300);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(458);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(20);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(23);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(126);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(146);
expect(activity.getStat(data_pool_length_1.DataPoolLength.type).getValue()).toEqual(50);
expect(activity.getStat(data_active_lengths_1.DataActiveLengths.type).getValue()).toEqual(46);
expect(activity.getStat(data_swolf_25m_1.DataSWOLF25m.type).getValue()).toEqual(36.7);
expect(activity.getStat(data_swolf_50m_1.DataSWOLF50m.type).getValue()).toEqual(73.4);
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(806);
const laps = activity.getLaps();
expect(laps.length).toEqual(36);
expect(laps[0].getStat(data_active_lap_1.DataActiveLap.type).getValue()).toBeTruthy();
expect(laps[1].getStat(data_active_lap_1.DataActiveLap.type).getValue()).toBeFalsy();
expect(laps[1].lapId).toEqual(2);
expect(activity.hasStreamData(data_speed_1.DataSpeed.type)).toBeTruthy();
expect(activity.hasStreamData(data_heart_rate_1.DataHeartRate.type)).toBeTruthy();
expect(activity.hasStreamData(data_cadence_1.DataCadence.type)).toBeTruthy();
expect(activity.hasStreamData(data_distance_1.DataDistance.type)).toBeTruthy();
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '01:50');
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '44:06');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:06:03');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:06:23');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 0);
done();
})
.catch(e => done(e));
});
it('should parse swimming pool FIT file (2)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/6021532030 OR https://www.strava.com/activities/5258304128
const path = __dirname + '/fixtures/swim/fit/6021532030.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Swimming);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(780);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(235);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(26);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(28);
expect(activity.getStat(data_pool_length_1.DataPoolLength.type).getValue()).toEqual(20);
expect(activity.getStat(data_active_lengths_1.DataActiveLengths.type).getValue()).toEqual(39);
expect(activity.getStat(data_swolf_25m_1.DataSWOLF25m.type).getValue()).toEqual(63.9);
expect(activity.getStat(data_swolf_50m_1.DataSWOLF50m.type).getValue()).toEqual(127.7);
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(596);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '02:58');
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '23:09');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '23:09');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '23:31');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse swimming pool FIT file (3)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/6860712481 OR https://www.strava.com/activities/5376106733
const path = __dirname + '/fixtures/swim/fit/6860712481.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Swimming);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(2000);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(401);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(28);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(131);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(157);
expect(activity.getStat(data_pool_length_1.DataPoolLength.type).getValue()).toEqual(25);
expect(activity.getStat(data_active_lengths_1.DataActiveLengths.type).getValue()).toEqual(80);
expect(activity.getStat(data_swolf_25m_1.DataSWOLF25m.type).getValue()).toEqual(46.1);
expect(activity.getStat(data_swolf_50m_1.DataSWOLF50m.type).getValue()).toEqual(92.2);
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(984);
// TODO Check streams based on laps for swims performed in a pool
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '02:06');
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '41:25');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '47:27');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '49:43');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse swimming pool FIT file (4)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/7617306288 OR https://www.strava.com/activities/6076695527
const path = __dirname + '/fixtures/swim/fit/7617306288.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Swimming);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(150);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '01:54');
expect(activity.getLaps().length).toEqual(6);
done();
})
.catch(e => done(e));
});
it('should parse swimming open water FIT file (1)', done => {
// Given https://connect.garmin.com/modern/activity/6788312639/1 OR https://www.strava.com/activities/5305763421
const path = __dirname + '/fixtures/swim/fit/6788312639-1.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.OpenWaterSwimming);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(1505.03);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(403);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(32);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(48);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(158);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(170);
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(993);
expect(activity.getStat(data_swolf_25m_1.DataSWOLF25m.type).getValue()).toEqual(47.5);
expect(activity.getStat(data_swolf_50m_1.DataSWOLF50m.type).getValue()).toEqual(95);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '02:04');
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '24:28');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '31:06');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '31:06');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
describe('TCX', () => {
it('should parse swimming pool TCX file (1)', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/6832570127 OR https://www.strava.com/activities/5347012220
// Given FIT Source: https://connect.garmin.com/modern/activity/6688025408 OR https://www.strava.com/activities/4232464474
const path = __dirname + '/fixtures/swim/tcx/6688025408.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
// expect(activity.type).toEqual(ActivityTypes.Swimming); // TODO To be auto-detected by sports-data-science library (work in progress)
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(2300);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(458);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(119);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(146);
const laps = activity.getLaps();
expect(laps.length).toEqual(36);
expect(laps[0].getStat(data_active_lap_1.DataActiveLap.type).getValue()).toBeTruthy();
expect(laps[1].getStat(data_active_lap_1.DataActiveLap.type).getValue()).toBeFalsy();
expect(laps[1].lapId).toEqual(2);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '01:50');
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '44:06');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:06:03');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:06:23');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime);
done();
})
.catch(e => done(e));
});
it('should parse swimming pool TCX file (2)', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/6861441616 or https://www.strava.com/activities/5378130384
// Given FIT Source: https://connect.garmin.com/modern/activity/6860712481 OR https://www.strava.com/activities/5376106733
const path = __dirname + '/fixtures/swim/tcx/6860712481.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
// expect(activity.type).toEqual(ActivityTypes.Swimming); // TODO To be auto-detected by sports-data-science library (work in progress)
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(2000);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(401);
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(984);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(128);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(156);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '02:05');
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '41:25');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '47:27');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '47:27');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 0);
done();
})
.catch(e => done(e));
});
});
describe('GPX', () => {
it('should parse swimming open water GPX file (1)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6867388194 OR https://www.strava.com/activities/5382390985
// Given FIT Source: https://connect.garmin.com/modern/activity/4623204522/1 OR https://www.strava.com/activities/5352030147
const path = __dirname + '/fixtures/swim/gpx/4623204522-1.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
// Swim
const swimActivity = event.getFirstActivity();
// expect(swimActivity.type).toEqual(ActivityTypes.OpenWaterSwimming); // TODO To be auto-detected by sports-data-science library (work in progress)
expect(swimActivity.getStreamData(data_speed_1.DataSpeed.type).length).toEqual(3689);
expect(swimActivity.startDate.toISOString()).toEqual('2018-10-13T17:05:01.000Z');
expect(swimActivity.endDate.toISOString()).toEqual('2018-10-13T18:06:29.000Z');
expect(swimActivity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(3869, 0);
expect(swimActivity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(36);
expect(swimActivity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(39);
expect(swimActivity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(74);
expect(swimActivity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(115);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(swimActivity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '01:31', 2);
// Verifying time data
const movingTime = swimActivity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = swimActivity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = swimActivity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = swimActivity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:00:35', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:01:29');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:01:29');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse swimming open water GPX file (2)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6867446668 OR https://www.strava.com/activities/5382426315
// Given FIT Source: https://connect.garmin.com/modern/activity/6090674952 OR https://www.strava.com/activities/5366783958
const path = __dirname + '/fixtures/swim/gpx/6090674952.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const swimActivity = event.getFirstActivity();
// expect(swimActivity.type).toEqual(ActivityTypes.OpenWaterSwimming); // TODO To be auto-detected by sports-data-science library (work in progress)
expect(swimActivity.startDate.toISOString()).toEqual('2021-01-04T16:22:18.000Z');
expect(swimActivity.endDate.toISOString()).toEqual('2021-01-04T17:37:05.000Z');
expect(swimActivity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(2537, 0);
expect(swimActivity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(145);
expect(swimActivity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(184);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(swimActivity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '02:29');
// Verifying time data
const movingTime = swimActivity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = swimActivity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = swimActivity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = swimActivity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '55:43', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:14:47');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:14:47');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
});
describe('Running', () => {
describe('FIT', () => {
it('should parse running FIT file (1)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/2067489619 OR https://www.strava.com/activities/1221143311
const path = __dirname + '/fixtures/runs/fit/2067489619.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin VivoActive HR');
expect(activity.creator.manufacturer).toEqual('garmin');
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(4489.39);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(58);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(88);
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(2782);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(130);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(179);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '10:28', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '09:11', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '45:42');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '47:00');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '47:04');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running FIT file (2)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/6782987395 OR https://www.strava.com/activities/2451375851
const path = __dirname + '/fixtures/runs/fit/6782987395.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(14004.85);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(79);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(111);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(137);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(167);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '05:56', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '05:19', 1);
const laps = activity.getLaps();
expect(laps.length).toEqual(2);
expect(laps[0].getStat(data_active_lap_1.DataActiveLap.type).getValue()).toBeTruthy();
expect(laps[1].getStat(data_active_lap_1.DataActiveLap.type).getValue()).toBeTruthy();
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:19:37');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:23:09');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:23:09');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running FIT file (Suunto export) (3)', done => {
// Given FIT Source (Suunto export): https://connect.garmin.com/modern/activity/6909950168 OR https://www.strava.com/activities/5423646653
const path = __dirname + '/fixtures/runs/fit/6909950168.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.TrailRunning);
expect(activity.creator.name).toEqual('Suunto 9');
expect(activity.creator.productId).toEqual(34);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(6458);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(487);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(90);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(94);
expect(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue()).toEqual(227);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toEqual(294);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(148);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(161);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '05:35', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '05:34', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '35:42');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '36:01');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '36:36');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running FIT file (Suunto export) (4)', done => {
// Given FIT Source (Suunto export): https://connect.garmin.com/modern/activity/6910052863 OR https://www.strava.com/activities/5423660493
const path = __dirname + '/fixtures/runs/fit/6910052863.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.TrailRunning);
expect(activity.creator.name).toEqual('Suunto 9');
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(8225);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toEqual(1817);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(2540);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(44);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(96);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(164);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(174);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '19:20', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '07:32', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '02:35:40');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '02:39:03');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '02:40:02');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running FIT file (Suunto export) (5)', done => {
// Given FIT Source (Suunto export): https://connect.garmin.com/modern/activity/6914448270 OR https://www.strava.com/activities/5427988571
const path = __dirname + '/fixtures/runs/fit/6914448270.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
expect(activity.creator.name).toEqual('Suunto 9 Baroless');
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(49897);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toEqual(1063);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(4445);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(74);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(119);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '06:49', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '06:20', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue(); // TODO If moving time unknown or INVALID loop on all records in activity utils
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '05:28:14');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '05:40:19');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '05:40:19');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running FIT file (Suunto export) (6)', done => {
// Given FIT Source (Suunto export): https://connect.garmin.com/modern/activity/6914538454 OR https://www.strava.com/activities/5428064152
const path = __dirname + '/fixtures/runs/fit/6914538454.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
expect(activity.creator.name).toEqual('Suunto 9 Baroless');
expect(activity.creator.manufacturer).toEqual('suunto');
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(4330);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toEqual(0);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(354);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(87);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(109);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '04:52', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '18:51');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '21:04');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '21:04');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running FIT file (Coros export) (7)', done => {
// Given FIT Source (Coros export): https://connect.garmin.com/modern/activity/6916663933 OR https://www.strava.com/activities/5429996380
const path = __dirname + '/fixtures/runs/fit/6916663933.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
expect(activity.creator.name).toEqual('Coros APEX Pro');
expect(activity.creator.productId).toEqual(841);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(10743.64);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toEqual(142);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(685);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(92);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(106);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(160);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(173);
expect(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue()).toEqual(332);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toEqual(518);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '04:31', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '04:24', 1);
const stanceTimeBalanceLeft = activity.getStat(data_stance_time_balance_left_1.DataStanceTimeBalanceLeft.type).getValue();
const stanceTimeBalanceRight = activity.getStat(data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type).getValue();
expect(stanceTimeBalanceLeft).toEqual(49.25);
expect(stanceTimeBalanceRight).toEqual(50.75);
expect(stanceTimeBalanceLeft).toEqual(activity.getStat(data_ground_contact_time_balance_left_1.DataGroundContactTimeBalanceLeft.type).getValue());
expect(stanceTimeBalanceRight).toEqual(activity.getStat(data_ground_contact_time_balance_right_1.DataGroundContactTimeBalanceRight.type).getValue());
expect(activity.getStat(data_ground_contact_time_avg_1.DataGroundContactTimeAvg.type).getValue()).toEqual(206);
expect(activity.getStat(data_vertical_oscillation_avg_1.DataVerticalOscillationAvg.type).getValue()).toEqual(94);
expect(activity.getStat(data_vertical_ratio_avg_1.DataVerticalRatioAvg.type).getValue()).toEqual(7.9);
expect(activity.getStat(data_avg_stride_length_1.DataAvgStrideLength.type).getValue()).toEqual(1.2);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '48:35');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '48:42');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '48:42');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running FIT file (Coros export) (8)', done => {
// Given FIT Source (Coros export): https://connect.garmin.com/modern/activity/6916728382 OR https://www.strava.com/activities/5430055225
const path = __dirname + '/fixtures/runs/fit/6916728382.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
expect(activity.creator.name).toEqual('Coros APEX Pro');
expect(activity.creator.manufacturer).toEqual('coros');
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(21524.3);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toEqual(539);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(1661);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(71);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(88);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(131);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(164);
expect(activity.getStat(data_temperature_avg_1.DataTemperatureAvg.type).getValue()).toEqual(22);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '07:31', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '02:39:59');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '02:41:39');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '02:41:39');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running treadmill FIT file (1)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/6860622783 or https://www.strava.com/activities/5375834427
const path = __dirname + '/fixtures/runs/fit/6860622783.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Treadmill);
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.isTrainer()).toBeTruthy();
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(10630.37);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(82);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(128);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(145);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(177);
expect(activity.getStat(data_stance_time_balance_left_1.DataStanceTimeBalanceLeft.type).getValue()).toEqual(49.91);
expect(activity.getStat(data_stance_time_balance_right_1.DataStanceTimeBalanceRight.type).getValue()).toEqual(50.09);
expect(activity.getStat(data_ground_contact_time_avg_1.DataGroundContactTimeAvg.type).getValue()).toEqual(271.3);
expect(activity.getStat(data_vertical_oscillation_avg_1.DataVerticalOscillationAvg.type).getValue()).toEqual(97.2);
expect(activity.getStat(data_vertical_ratio_avg_1.DataVerticalRatioAvg.type).getValue()).toEqual(9.57);
expect(activity.getStat(data_avg_stride_length_1.DataAvgStrideLength.type).getValue()).toEqual(1.04);
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(4242);
expect(activity.getStat(data_aerobic_training_effect_1.DataAerobicTrainingEffect.type).getValue()).toEqual(2.2);
expect(activity.getStat(data_anaerobic_training_effect_1.DataAnaerobicTrainingEffect.type).getValue()).toEqual(1.8);
expect(activity.getStat(data_power_work_1.DataPowerWork.type).getValue()).toEqual(692);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '04:51', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '48:43');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '51:38');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:02:44');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
describe('TCX', () => {
it('should parse running TCX file (1)', done => {
// Given Recorded with "Strava Android App" => https://connect.garmin.com/modern/activity/1604524853 OR https://www.strava.com/activities/708752345
const path = __dirname + '/fixtures/runs/tcx/1604524853.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.run);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '09:15', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '07:07', 1);
spec_utils_1.SpecUtils.assertEqual(event.getFirstActivity().getStat(data_ascent_1.DataAscent.type).getValue(), 432);
spec_utils_1.SpecUtils.assertEqual(event.getFirstActivity().getStat(data_descent_1.DataDescent.type).getValue(), 440);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(6106.6);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(69);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(99);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(158);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(191);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '58:59', 3);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:07:47');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:07:47');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running TCX file (2)', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/6851149716 OR https://www.strava.com/activities/5366592367
// Given FIT Source: https://connect.garmin.com/modern/activity/6782987395 OR https://www.strava.com/activities/2451375851
const path = __dirname + '/fixtures/runs/tcx/6782987395.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.run);
expect(activity.creator.name).toEqual('Forerunner 945');
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '05:41');
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '05:19', 1);
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_ascent_1.DataAscent.type).getValue(), 363);
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_descent_1.DataDescent.type).getValue(), 386);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(14004.85);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(982);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(80);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(111);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(137);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(167);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:19:38');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:23:09');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:23:09');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running TCX file (3)', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/6860622916 or https://www.strava.com/activities/5375754773
const path = __dirname + '/fixtures/runs/tcx/6860622916.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.run);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '04:25');
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '04:24');
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_ascent_1.DataAscent.type).getValue(), 85);
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_descent_1.DataDescent.type).getValue(), 74);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(13973.09);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(739);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(89);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(98);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(143);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(168);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:02:17');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:02:17');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:02:17');
expect(Math.abs(pauseTime - (elapsedTime - timerTime))).toBeLessThanOrEqual(1);
done();
})
.catch(e => done(e));
});
it('should parse running TCX file (4)', done => {
// Given TCX Source: (garmin connect upload impossible) or https://www.strava.com/activities/6281425038
const path = __dirname + '/fixtures/runs/tcx/6281425038.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.run);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '06:19');
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '05:58');
const laps = activity.getLaps();
expect(laps.length).toEqual(5);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '25:45');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '25:45');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '36:02');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running treadmill TCX file (3)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/6860622783 or https://www.strava.com/activities/5375834427
const path = __dirname + '/fixtures/runs/tcx/6860622783.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.IndoorRunning);
expect(activity.isTrainer()).toBeTruthy();
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(10630.37);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(82);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(128);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(145);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(177);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '04:51', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '48:43');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '51:38');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:02:44');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
describe('GPX', () => {
it('should parse running GPX file (1)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6870534840 OR https://www.strava.com/activities/5385528846
// Given FIT Source: https://connect.garmin.com/modern/activity/2067489619 OR https://www.strava.com/activities/1221143311
const path = __dirname + '/fixtures/runs/gpx/2067489619.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
// Swim
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '09:18', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '09:18', 1);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(4400, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(35, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(67);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(88);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(134);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(178);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '44:48');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '47:00');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '47:04');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running GPX file (2)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6870566668 OR https://www.strava.com/activities/5385561485
// Given FIT Source: https://connect.garmin.com/modern/activity/6782987395 OR https://www.strava.com/activities/2451375851
const path = __dirname + '/fixtures/runs/gpx/6782987395.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Running);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '05:53', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '05:18', 1);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(14170.5, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(363, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(82);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(111);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(137);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(167);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:17:11');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:23:09');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:23:09');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running GPX file (3)', done => {
// Given Recorded with "Strava Android App" => https://connect.garmin.com/modern/activity/1681613317 OR https://www.strava.com/activities/906581465
const path = __dirname + '/fixtures/runs/gpx/1681613317.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.run);
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.isTrainer()).toBeFalsy();
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '06:27', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '06:23', 1);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(4593, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(39, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(79);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(96);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(146);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(176);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '32:09');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '32:38');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '32:38');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running GPX file (4)', done => {
// Given Recorded with "MyTracks" => https://connect.garmin.com/modern/activity/6909258631 OR https://www.strava.com/activities/5422847134
const path = __dirname + '/fixtures/runs/gpx/6909258631.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
// expect(activity.type).toEqual(ActivityTypes.run); // TODO To be auto-detected by sports-data-science library (work in progress)
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(40878.5, 1);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(2780, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(2780, 0);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '07:22', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '04:51:49');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '06:30:22');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '06:30:22');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running GPX file (5)', done => {
// Given https://connect.garmin.com/modern/activity/6048438275 OR https://www.strava.com/activities/3497177564
const path = __dirname + '/fixtures/runs/gpx/6048438275.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.run);
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.isTrainer()).toBeFalsy();
spec_utils_1.SpecUtils.assertNearEqual(activity.getStat(data_distance_1.DataDistance.type).getValue(), 16771.3, 1, 1);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(579, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(86);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(123);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '04:48', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '04:17', 1);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
expect(activity.getSquashedStreamData('Distance').length).toEqual(activity.getSquashedStreamData('Speed').length);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:04:19');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:22:50');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:22:50');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse running GPX file (6)', done => {
// Given https://www.strava.com/activities/3632493456 or https://connect.garmin.com/modern/activity/6037744996
const path = __dirname + '/fixtures/runs/gpx/6037744996.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.run);
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.isTrainer()).toBeFalsy();
spec_utils_1.SpecUtils.assertNearEqual(activity.getStat(data_distance_1.DataDistance.type).getValue(), 5115.1, 1, 1);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(516, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(68);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(124);
expect(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue()).toBeGreaterThanOrEqual(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue());
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '13:10', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type).getValue(), '09:07', 1);
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '28:35');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:07:19');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:07:19');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
});
describe('Cycling', () => {
describe('FIT', () => {
it('should parse cycling FIT file (1)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/3939576645 OR https://www.strava.com/activities/2610176355
const path = __dirname + '/fixtures/rides/fit/3939576645.fit';
const buffer = fs.readFileSync(path);
const expectedSamplesLength = 2547;
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
// Verify streams consistency
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Edge 1000');
expect(activity.generateTimeStream([data_distance_1.DataDistance.type]).getData(true).length).toEqual(expectedSamplesLength);
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.isTrainer()).toBeFalsy();
expect(activity.getSquashedStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_distance_1.DataDistance.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_altitude_1.DataAltitude.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_altitude_smooth_1.DataAltitudeSmooth.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_speed_1.DataSpeed.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_cadence_1.DataCadence.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_heart_rate_1.DataHeartRate.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_grade_1.DataGrade.type).length).toEqual(expectedSamplesLength);
const missingStreamCall = () => {
activity.getSquashedStreamData(data_power_1.DataPower.type);
};
expect(missingStreamCall).toThrow();
// Verify global activity stats
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(42220.87);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toEqual(668);
expect(activity.getStat(data_descent_1.DataDescent.type).getValue()).toEqual(664);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(1497);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(68);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(109);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(155);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(186);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toEqual(5.812); // Or 20.9 kph
expect(activity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(6550);
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '02:00:42', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '02:01:04');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '02:04:36');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (2)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/828989227 OR https://www.strava.com/activities/343080886
const path = __dirname + '/fixtures/rides/fit/828989227.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.creator.name).toEqual('Garmin Edge 810');
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(141944.2);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toEqual(2052);
expect(activity.getStat(data_descent_1.DataDescent.type).getValue()).toEqual(2028);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(2629);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(79);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(118);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(148);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(174);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toEqual(7.219); // Or 26 kph
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '05:26:49');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '05:27:44');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '06:07:08');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (3)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/614953343 OR https://www.strava.com/activities/208748758
const path = __dirname + '/fixtures/rides/fit/614953343.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Edge 800');
expect(activity.hasPowerMeter()).toBeFalsy();
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '05:52:28', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '05:54:34');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '07:04:31');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (4)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/6783622686 OR https://www.strava.com/activities/5300988022
const path = __dirname + '/fixtures/rides/fit/6783622686.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Edge 1030');
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '05:07:53');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '05:08:22');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '07:26:55');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (5)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/7386755164 OR https://www.strava.com/activities/5952147686
const path = __dirname + '/fixtures/rides/fit/7386755164.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Edge 1030');
// Power extended stats. FIT invalid L/R balance samples (0xFF) are excluded.
expect(activity.getStat(data_power_balance_left_1.DataPowerBalanceLeft.type).getValue()).toEqual(51.66);
expect(activity.getStat(data_power_balance_right_1.DataPowerBalanceRight.type).getValue()).toEqual(48.34);
expect(activity.getStat(data_power_torque_effectiveness_left_1.DataPowerTorqueEffectivenessLeft.type).getValue()).toEqual(80);
expect(activity.getStat(data_power_torque_effectiveness_right_1.DataPowerTorqueEffectivenessRight.type).getValue()).toEqual(76.5);
expect(activity.getStat(data_power_pedal_smoothness_left_1.DataPowerPedalSmoothnessLeft.type).getValue()).toEqual(22);
expect(activity.getStat(data_power_pedal_smoothness_right_1.DataPowerPedalSmoothnessRight.type).getValue()).toEqual(20.5);
expect(activity.getStat(data_power_normalized_1.DataPowerNormalized.type).getValue()).toEqual(179);
expect(activity.getStat(data_power_intensity_factor_1.DataPowerIntensityFactor.type).getValue()).toEqual(0.786);
expect(activity.getStat(data_training_stress_score_1.DataTrainingStressScore.type).getValue()).toEqual(105.4);
expect(activity.getStat(data_power_work_1.DataPowerWork.type).getValue()).toEqual(832);
// Cycling dynamics: Power "position" stats
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_cycling_standing_time_1.DataCyclingStandingTime.type).getValue(), '17:53', 0);
spec_utils_1.SpecUtils.assertNearEqualTime(activity.getStat(data_cycling_seated_time_1.DataCyclingSeatedTime.type).getValue(), '01:25:02', 0);
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (6)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/7445393868 OR https://www.strava.com/activities/5921768617
const path = __dirname + '/fixtures/rides/fit/7445393868.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Edge 1000');
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.getStat(data_power_balance_left_1.DataPowerBalanceLeft.type).getValue()).toEqual(45.91);
expect(activity.getStat(data_power_balance_right_1.DataPowerBalanceRight.type).getValue()).toEqual(54.09);
expect(activity.getStat(data_power_torque_effectiveness_left_1.DataPowerTorqueEffectivenessLeft.type).getValue()).toEqual(71);
expect(activity.getStat(data_power_torque_effectiveness_right_1.DataPowerTorqueEffectivenessRight.type).getValue()).toEqual(73);
expect(activity.getStat(data_power_pedal_smoothness_left_1.DataPowerPedalSmoothnessLeft.type).getValue()).toEqual(20);
expect(activity.getStat(data_power_pedal_smoothness_right_1.DataPowerPedalSmoothnessRight.type).getValue()).toEqual(21.5);
expect(activity.getStat(data_power_normalized_1.DataPowerNormalized.type).getValue()).toEqual(147);
expect(activity.getStat(data_power_intensity_factor_1.DataPowerIntensityFactor.type).getValue()).toEqual(0.733);
expect(activity.getStat(data_training_stress_score_1.DataTrainingStressScore.type).getValue()).toEqual(48.3);
expect(activity.getStat(data_power_work_1.DataPowerWork.type).getValue()).toEqual(417);
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (7)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/7432332116 OR https://www.strava.com/activities/5910143591
const path = __dirname + '/fixtures/rides/fit/7432332116.fit';
const buffer = fs.readFileSync(path);
const expectedSamplesLength = 10063;
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Edge 1000');
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue()).toEqual(152);
expect(activity.getStat(data_power_normalized_1.DataPowerNormalized.type).getValue()).toEqual(172);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toEqual(564);
expect(activity.getSquashedStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_distance_1.DataDistance.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_altitude_1.DataAltitude.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_speed_1.DataSpeed.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_cadence_1.DataCadence.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_heart_rate_1.DataHeartRate.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_power_1.DataPower.type).length).toEqual(expectedSamplesLength);
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (8)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/7739869618 OR https://www.strava.com/activities/2380240532
const path = __dirname + '/fixtures/rides/fit/7739869618.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Wahoo ELEMNT BOLT');
expect(activity.creator.manufacturer).toEqual('wahoo_fitness');
expect(activity.creator.isRecognized).toBeTruthy();
done();
})
.catch(e => done(e));
});
it('should parse cycling FIT file (9)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/5319808632 OR https://www.strava.com/activities/3849490840
const path = __dirname + '/fixtures/rides/fit/5319808632.fit';
const buffer = fs.readFileSync(path);
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.startDate.toISOString()).toEqual('2020-07-26T15:28:41.000Z');
expect(activity.endDate.toISOString()).toEqual('2020-07-26T16:31:26.063Z');
done();
})
.catch(e => done(e));
});
it('should parse virtual cycling FIT file (1)', done => {
// Given FIT Source: https://connect.garmin.com/modern/activity/971150603 OR https://www.strava.com/activities/442080924
const path = __dirname + '/fixtures/rides/fit/971150603.fit';
const buffer = fs.readFileSync(path);
const expectedSamplesLength = 3806;
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
// Verify streams consistency
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Zwift');
expect(activity.creator.productId).toEqual(15706);
expect(activity.creator.isRecognized).toBeTruthy();
expect(activity.generateTimeStream([data_distance_1.DataDistance.type]).getData(true).length).toEqual(expectedSamplesLength);
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.isTrainer()).toBeFalsy();
expect(activity.getSquashedStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_distance_1.DataDistance.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_altitude_1.DataAltitude.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_speed_1.DataSpeed.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_cadence_1.DataCadence.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_heart_rate_1.DataHeartRate.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_power_1.DataPower.type).length).toEqual(expectedSamplesLength);
// Verify global activity stats
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(32227.5);
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_ascent_1.DataAscent.type).getValue(), 242, 0);
expect(activity.getStat(data_energy_1.DataEnergy.type)).toBeUndefined(); // Zwift didn't give the calories
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(76);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(114);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(148);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(180);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toEqual(8.476); // Or 30.5 kph
expect(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue()).toEqual(143);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toEqual(381);
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:02:53', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:03:35');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:03:42');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should generate grade adjusted speed for a cycling FIT file (samples)', done => {
const path = __dirname + '/../../samples/fit/2026-02-10_14-28.fit';
const buffer = fs.readFileSync(path);
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.hasStreamData(data_speed_1.DataSpeed.type)).toBeTruthy();
expect(activity.hasStreamData(data_grade_adjusted_speed_1.DataGradeAdjustedSpeed.type)).toBeTruthy();
expect(activity.getStat(data_grade_adjusted_pace_avg_1.DataGradeAdjustedPaceAvg.type)).toBeDefined();
done();
})
.catch(e => done(e));
});
});
describe('TCX', () => {
it('should parse cycling TCX file (1)', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/6791039036 OR https://www.strava.com/activities/5308417342
// Given FIT Source: https://connect.garmin.com/modern/activity/3939576645 OR https://www.strava.com/activities/2610176355
const path = __dirname + '/fixtures/rides/tcx/6791039036.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
// Verify global activity stats
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.creator.name).toEqual('Garmin Edge 1000');
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_ascent_1.DataAscent.type).getValue(), 672, 0);
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_descent_1.DataDescent.type).getValue(), 664);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(42220.87);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(1497);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(69);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(108);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(153);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(186);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(6.536); // Or 23.5 kph
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '02:01:59', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '02:01:04');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '02:04:36');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling TCX file (2)', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/6838497277 or https://www.strava.com/activities/5354278524
// Given FIT Source: https://connect.garmin.com/modern/activity/3953195468 or https://www.strava.com/activities/2621275265
const path = __dirname + '/fixtures/rides/tcx/6838497277.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.isTrainer()).toBeFalsy();
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_ascent_1.DataAscent.type).getValue(), 685, 0);
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_descent_1.DataDescent.type).getValue(), 670);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(59853.31);
expect(activity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(2206);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(68);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(109);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(160);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(192);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(6.278); // 22.6 kph
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '02:52:10', 1.5);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '02:55:15');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '02:55:15');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling TCX file (3)', done => {
// Given TCX Source: https://connect.garmin.com/modern/activity/7555170032 or https://www.strava.com/activities/6020442882
// Given FIT Source: https://connect.garmin.com/modern/activity/7432332116 OR https://www.strava.com/activities/5910143591
const path = __dirname + '/fixtures/rides/tcx/7555170032.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
const expectedSamplesLength = 10063;
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.creator.name).toEqual('Garmin Edge 1000');
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue()).toBeCloseTo(152, 0);
expect(activity.getStat(data_power_normalized_1.DataPowerNormalized.type).getValue()).toBeCloseTo(171, 0);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toEqual(564);
expect(activity.getSquashedStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_distance_1.DataDistance.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_altitude_1.DataAltitude.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_speed_1.DataSpeed.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_cadence_1.DataCadence.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_heart_rate_1.DataHeartRate.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_power_1.DataPower.type).length).toEqual(expectedSamplesLength);
done();
})
.catch(e => done(e));
});
it('should parse virtual cycling TCX file (1)', done => {
// Given https://connect.garmin.com/modern/activity/6823905801 OR https://www.strava.com/activities/5340305665
const path = __dirname + '/fixtures/rides/tcx/6823905801.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
// When
const eventInterfacePromise = index_1.SportsLib.importFromTCX(doc);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.isTrainer()).toBeFalsy();
// Verify global activity stats
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(32227.5);
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_ascent_1.DataAscent.type).getValue(), 242);
expect(activity.getStat(data_energy_1.DataEnergy.type)).toBeUndefined(); // Zwift didn't give the calories
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(76);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(114);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(148);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(180);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(8.47, 3); // Or 30.5 kph
spec_utils_1.SpecUtils.assertEqual(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue(), 143);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toEqual(381);
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:03:10', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:03:35');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:03:42');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
describe('GPX', () => {
it('should parse cycling GPX file (1)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6870609105 OR https://www.strava.com/activities/5385601807
// Given FIT Source: https://connect.garmin.com/modern/activity/3939576645 OR https://www.strava.com/activities/2610176355
const path = __dirname + '/fixtures/rides/gpx/3939576645.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(41829, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(672, 0);
expect(activity.getStat(data_descent_1.DataDescent.type).getValue()).toBeCloseTo(664, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(69);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(108);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(153);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(186);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(6.479); // Or 23.32 kph
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '02:00:42', 1.5);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '02:04:35');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '02:04:36');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling GPX file (2)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6870692308 OR https://www.strava.com/activities/5385683390
// Given FIT Source: https://connect.garmin.com/modern/activity/828989227 OR https://www.strava.com/activities/343080886
const path = __dirname + '/fixtures/rides/gpx/828989227.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.hasPowerMeter()).toBeFalsy();
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(141975, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(2052, 0);
expect(activity.getStat(data_descent_1.DataDescent.type).getValue()).toBeCloseTo(2034, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(79);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(118);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(148);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(174);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(7.329); // Or 26.3 kph
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '05:26:49', 2);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '06:07:13');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '06:07:13');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling GPX file (3)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6905455978 OR https://www.strava.com/activities/5419099678
// Given FIT Source: https://connect.garmin.com/modern/activity/4108490848 OR https://www.strava.com/activities/2749884297
const path = __dirname + '/fixtures/rides/gpx/4108490848.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.name).toEqual('Meylan Road Cycling');
expect(activity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(49909, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(292, 0);
expect(activity.getStat(data_descent_1.DataDescent.type).getValue()).toBeCloseTo(283, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(76);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(117);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(165);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(186);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(7.903); // Or 28.4 kph
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:47:13', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:49:20');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:49:20');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
it('should parse cycling GPX file (4)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/7555261629 or https://www.strava.com/activities/6020530554
// Given FIT Source: https://connect.garmin.com/modern/activity/7432332116 OR https://www.strava.com/activities/5910143591
const path = __dirname + '/fixtures/rides/gpx/7555261629.gpx';
const gpxString = fs.readFileSync(path).toString();
const expectedSamplesLength = 10063;
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue()).toBeCloseTo(152, 0);
expect(activity.getStat(data_power_normalized_1.DataPowerNormalized.type).getValue()).toBeCloseTo(171, 0);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toEqual(564);
expect(activity.getSquashedStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_distance_1.DataDistance.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_altitude_1.DataAltitude.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_speed_1.DataSpeed.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_cadence_1.DataCadence.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_heart_rate_1.DataHeartRate.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_power_1.DataPower.type).length).toEqual(expectedSamplesLength);
done();
})
.catch(e => done(e));
});
it('should parse virtual cycling GPX file (1)', done => {
// Given GPX Source: https://connect.garmin.com/modern/activity/6909869014 OR https://www.strava.com/activities/5423473093
// Given FIT Source: https://connect.garmin.com/modern/activity/1137672073 OR https://www.strava.com/activities/553573871
const path = __dirname + '/fixtures/rides/gpx/6909869014.gpx';
const gpxString = fs.readFileSync(path).toString();
// When
const eventInterfacePromise = index_1.SportsLib.importFromGPX(gpxString, xmldom_1.default.DOMParser);
// Then
eventInterfacePromise
.then((event) => {
const activity = event.getFirstActivity();
expect(activity.type).toEqual(activity_types_1.ActivityTypes.VirtualCycling);
expect(activity.hasPowerMeter()).toBeTruthy();
expect(activity.isTrainer()).toBeTruthy();
expect(activity.getStat(data_distance_1.DataDistance.type).getValue()).toBeCloseTo(28620, 0);
expect(activity.getStat(data_ascent_1.DataAscent.type).getValue()).toBeCloseTo(271, 0);
expect(activity.getStat(data_descent_1.DataDescent.type).getValue()).toBeCloseTo(257, 0);
expect(activity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(75);
expect(activity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(120);
expect(activity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(161);
expect(activity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(184);
expect(activity.getStat(data_power_avg_1.DataPowerAvg.type).getValue()).toBeCloseTo(142, 0);
expect(activity.getStat(data_power_max_1.DataPowerMax.type).getValue()).toBeCloseTo(330);
expect(activity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(7.55); // Or 27.2 kph
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const pauseTime = activity.getStat(data_pause_1.DataPause.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:02:58', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:03:09');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:03:09');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
});
describe('Triathlon', () => {
describe('FIT', () => {
it('should parse a .FIT Triathlon IronMan w/ 3 activities (swim + ride + run) ', done => {
// Given https://connect.garmin.com/modern/activity/4623204522 ; https://www.strava.com/activities/5352030147 + https://www.strava.com/activities/5352030778 + https://www.strava.com/activities/5352030764
const path = __dirname + '/fixtures/triathlons/fit/4623204522.fit';
const buffer = fs.readFileSync(path);
const activitiesCount = 5;
// When
const eventInterfacePromise = index_1.SportsLib.importFromFit(buffer);
// Then
eventInterfacePromise
.then((event) => {
expect(event.getActivities().length).toEqual(activitiesCount);
// Swim
const swimActivity = event.getFirstActivity();
expect(swimActivity.type).toEqual(activity_types_1.ActivityTypes.OpenWaterSwimming);
expect(swimActivity.getStreamData(data_speed_1.DataSpeed.type).length).toEqual(3689);
expect(swimActivity.startDate.toISOString()).toEqual('2018-10-13T17:05:01.000Z');
expect(swimActivity.endDate.toISOString()).toEqual('2018-10-13T18:06:29.000Z');
expect(swimActivity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(3933.3);
expect(swimActivity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(869);
expect(swimActivity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(37);
expect(swimActivity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(48);
expect(swimActivity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(93);
expect(swimActivity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(112);
expect(swimActivity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(2294);
spec_utils_1.SpecUtils.assertNearEqualTime(spec_utils_1.SpecUtils.speedToSwimPace(swimActivity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()), '01:34');
// Verifying time data
let movingTime = swimActivity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
let timerTime = swimActivity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
let pauseTime = swimActivity.getStat(data_pause_1.DataPause.type).getValue();
let elapsedTime = swimActivity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '01:01:16', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '01:01:29');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '01:01:29');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
// Transition
let transition = event.getActivities()[1];
expect(transition.type).toEqual(activity_types_1.ActivityTypes.Transition);
// Cycling
const cyclingActivity = event.getActivities()[2];
expect(cyclingActivity.type).toEqual(activity_types_1.ActivityTypes.Cycling);
expect(cyclingActivity.getStreamData(data_speed_1.DataSpeed.type).length).toEqual(17250);
expect(cyclingActivity.startDate.toISOString()).toEqual('2018-10-13T18:10:15.000Z');
expect(cyclingActivity.endDate.toISOString()).toEqual('2018-10-13T22:57:44.000Z');
expect(cyclingActivity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(180301.58);
expect(cyclingActivity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(3804);
expect(cyclingActivity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(89);
expect(cyclingActivity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(117);
expect(cyclingActivity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(140);
expect(cyclingActivity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(153);
expect(cyclingActivity.getStat(data_total_cycles_1.DataTotalCycles.type).getValue()).toEqual(24373);
expect(cyclingActivity.getStat(data_speed_avg_1.DataSpeedAvg.type).getValue()).toBeCloseTo(10.453, 2); // Or 37.6 kph
// Verifying time data
movingTime = cyclingActivity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
timerTime = cyclingActivity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
pauseTime = cyclingActivity.getStat(data_pause_1.DataPause.type).getValue();
elapsedTime = cyclingActivity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '04:42:28', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '04:47:29');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '04:47:29');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime);
transition = event.getActivities()[3];
expect(transition.type).toEqual(activity_types_1.ActivityTypes.Transition);
const runningActivity = event.getActivities()[4];
expect(runningActivity.type).toEqual(activity_types_1.ActivityTypes.Running);
expect(runningActivity.startDate.toISOString()).toEqual('2018-10-13T22:57:45.000Z');
expect(runningActivity.endDate.toISOString()).toEqual('2018-10-14T02:42:23.000Z');
expect(runningActivity.getStat(data_distance_1.DataDistance.type).getValue()).toEqual(42563.91);
expect(runningActivity.getStat(data_energy_1.DataEnergy.type).getValue()).toEqual(2056);
expect(runningActivity.getStat(data_cadence_avg_1.DataCadenceAvg.type).getValue()).toEqual(84);
expect(runningActivity.getStat(data_cadence_max_1.DataCadenceMax.type).getValue()).toEqual(116);
expect(runningActivity.getStat(data_heart_rate_avg_1.DataHeartRateAvg.type).getValue()).toEqual(142);
expect(runningActivity.getStat(data_heart_rate_max_1.DataHeartRateMax.type).getValue()).toEqual(151);
spec_utils_1.SpecUtils.assertNearEqualTime(runningActivity.getStat(data_pace_avg_1.DataPaceAvg.type).getValue(), '05:08', 1);
// Verifying time data
movingTime = runningActivity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
timerTime = runningActivity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
pauseTime = runningActivity.getStat(data_pause_1.DataPause.type).getValue();
elapsedTime = runningActivity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '03:36:57', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '03:38:13');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '03:39:01');
spec_utils_1.SpecUtils.assertEqual(pauseTime, elapsedTime - timerTime, 1);
done();
})
.catch(e => done(e));
});
});
});
describe('Others', () => {
it('should reject parsing of broken fit file (empty activities)', () => __awaiter(void 0, void 0, void 0, function* () {
// Given
const path = __dirname + '/fixtures/others/empty-activities.fit';
const buffer = fs.readFileSync(path);
const expectedErrorMessage = 'No activities found';
const expectedErrorClassName = 'EmptyEventLibError';
const expectedCode = 'EVENT_EMPTY_ERROR';
// When & Then
try {
yield index_1.SportsLib.importFromFit(buffer);
throw new Error('Should not be resolved...');
}
catch (err) {
expect(err.constructor.name).toEqual(expectedErrorClassName);
expect(err.message).toEqual(expectedErrorMessage);
expect(err.code).toEqual(expectedCode);
expect(err.event).toBeNull();
}
}));
it('should reject parsing of broken fit file (empty sessions)', () => __awaiter(void 0, void 0, void 0, function* () {
// Given
const path = __dirname + '/fixtures/others/empty-sessions.fit';
const buffer = fs.readFileSync(path);
const expectedErrorMessage = 'No activities found';
const expectedErrorClassName = 'EmptyEventLibError';
const expectedCode = 'EVENT_EMPTY_ERROR';
// When & Then
try {
yield index_1.SportsLib.importFromFit(buffer);
throw new Error('Should not be resolved...');
}
catch (err) {
expect(err.constructor.name).toEqual(expectedErrorClassName);
expect(err.message).toEqual(expectedErrorMessage);
expect(err.code).toEqual(expectedCode);
expect(err.event).toBeNull();
}
}));
it('should parse fit file with broken sessionObject.start_time date', () => __awaiter(void 0, void 0, void 0, function* () {
// Given
const path = __dirname + '/fixtures/others/broken-dates.fit';
const buffer = fs.readFileSync(path);
// When
const event = yield index_1.SportsLib.importFromFit(buffer);
// Then
expect(event.startDate.toISOString()).toEqual('2017-03-20T19:09:28.000Z');
expect(event.endDate.toISOString()).toEqual('2017-03-20T19:20:26.083Z');
}));
it('should handle activity with broken start lat/lng', () => __awaiter(void 0, void 0, void 0, function* () {
// Given FIT Source: https://connect.garmin.com/modern/activity/7606651718 OR https://www.strava.com/activities/6066984530
const path = __dirname + '/fixtures/others/broken-start-latlng.fit';
const buffer = fs.readFileSync(path);
// When
const event = yield index_1.SportsLib.importFromFit(buffer);
// Then
const activity = event.getFirstActivity();
const longStream = activity.getSquashedStreamData(data_longitude_degrees_1.DataLongitudeDegrees.type);
const latStream = activity.getSquashedStreamData(data_latitude_degrees_1.DataLatitudeDegrees.type);
expect(longStream[0]).not.toEqual(0);
expect(latStream[0]).not.toEqual(0);
}));
it('should reject parsing of a too long activity (31 days)', () => __awaiter(void 0, void 0, void 0, function* () {
// Given
const path = __dirname + '/fixtures/others/31-days-activity.fit';
const buffer = fs.readFileSync(path);
const expectedErrorMessage = 'Activity duration exceeds 14 days. That is not supported';
const expectedErrorClassName = 'DurationExceededEventLibError';
const expectedCode = 'DURATION_EXCEEDED_EVENT_ERROR';
// When & Then
try {
yield index_1.SportsLib.importFromFit(buffer);
throw new Error('Should not be resolved...');
}
catch (err) {
expect(err.constructor.name).toEqual(expectedErrorClassName);
expect(err.message).toEqual(expectedErrorMessage);
expect(err.code).toEqual(expectedCode);
expect(err.event).toBeNull();
}
}));
it.each([
{
fileName: 'garmin-corrupt-session-duration-record-range-1.fit',
startDate: '2024-01-01T00:00:00.000Z',
endDate: '2024-01-01T00:01:58.000Z',
elapsedTime: 118,
timerTime: 117
},
{
fileName: 'garmin-corrupt-session-duration-record-range-2.fit',
startDate: '2024-02-01T00:00:00.000Z',
endDate: '2024-02-01T00:01:27.000Z',
elapsedTime: 87,
timerTime: 85
}
])('should parse Garmin FIT file with corrupt session duration: $fileName', (expected) => __awaiter(void 0, void 0, void 0, function* () {
// Given
const path = process.cwd() + `/samples/fit/${expected.fileName}`;
const buffer = fs.readFileSync(path);
// When
const event = yield index_1.SportsLib.importFromFit(buffer);
// Then
const activity = event.getFirstActivity();
const lap = activity.getLaps()[0];
expect(event.startDate.toISOString()).toEqual(expected.startDate);
expect(event.endDate.toISOString()).toEqual(expected.endDate);
expect(activity.startDate.toISOString()).toEqual(expected.startDate);
expect(activity.endDate.toISOString()).toEqual(expected.endDate);
expect(lap.startDate.toISOString()).toEqual(expected.startDate);
expect(lap.endDate.toISOString()).toEqual(expected.endDate);
expect(activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue()).toEqual(expected.elapsedTime);
expect(activity.getStat(data_timer_time_1.DataTimerTime.type).getValue()).toEqual(expected.timerTime);
expect(lap.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue()).toEqual(expected.elapsedTime);
expect(lap.getStat(data_timer_time_1.DataTimerTime.type).getValue()).toEqual(expected.timerTime);
}));
it('should handle inverted elapsed and timer time', () => __awaiter(void 0, void 0, void 0, function* () {
// Given FIT Source: https://connect.garmin.com/modern/activity/7852903753 OR https://www.strava.com/activities/6288405028
const path = __dirname + '/fixtures/others/inverted-times.fit';
const buffer = fs.readFileSync(path);
// When
const event = yield index_1.SportsLib.importFromFit(buffer);
// Then
const activity = event.getFirstActivity();
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '47:20', 1);
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '47:20');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '06:01:20');
}));
it('should handle calculation on a ultra long activity (27 days) with acceptable human time', () => __awaiter(void 0, void 0, void 0, function* () {
// Given FIT Source: https://connect.garmin.com/modern/activity/7769719668 OR https://www.strava.com/activities/6197356353
const path = __dirname + '/fixtures/others/27-days-activity.fit';
const buffer = fs.readFileSync(path);
const options = activity_parsing_options_1.ActivityParsingOptions.DEFAULT;
options.maxActivityDurationDays = 30;
const expectedSamplesLength = 3699;
// When
const event = yield index_1.SportsLib.importFromFit(buffer, options);
// Then
const activity = event.getFirstActivity();
expect(activity.getSquashedStreamData(data_distance_1.DataDistance.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_speed_1.DataSpeed.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_cadence_1.DataCadence.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_heart_rate_1.DataHeartRate.type).length).toEqual(expectedSamplesLength);
expect(activity.getSquashedStreamData(data_grade_1.DataGrade.type).length).toEqual(expectedSamplesLength);
}));
it('should handle and detect an activity with broken speed data', () => __awaiter(void 0, void 0, void 0, function* () {
// Given TCX Source: (unable to upload on garmin connect) OR https://www.strava.com/activities/3197023890
const path = __dirname + '/fixtures/others/broken-speed-activity.tcx';
const doc = domParser.parseFromString(fs.readFileSync(path).toString(), 'application/xml');
const options = activity_parsing_options_1.ActivityParsingOptions.DEFAULT;
options.streams.fixAbnormal.speed = true;
// When
const event = yield index_1.SportsLib.importFromTCX(doc);
// Then
const activity = event.getFirstActivity();
const speedStdDev = (0, helpers_1.standardDeviation)(activity.getSquashedStreamData(data_speed_1.DataSpeed.type));
expect(speedStdDev).toBeCloseTo(0.55, 2);
// Verifying time data
const movingTime = activity.getStat(data_moving_time_1.DataMovingTime.type).getValue();
const timerTime = activity.getStat(data_timer_time_1.DataTimerTime.type).getValue();
const elapsedTime = activity.getStat(data_elapsed_time_1.DataElapsedTime.type).getValue();
spec_utils_1.SpecUtils.assertNearEqualTime(movingTime, '05:04:20');
spec_utils_1.SpecUtils.assertNearEqualTime(timerTime, '5:07:54');
spec_utils_1.SpecUtils.assertNearEqualTime(elapsedTime, '5:07:54');
}));
});
});