running-tools

A collection of tools for runners and their coaches
git clone https://git.ashermorgan.net/running-tools/
Log | Files | Refs | README

calculators.spec.js (7746B)


      1 import { describe, test, expect } from 'vitest';
      2 import * as calculators from '@/core/calculators';
      3 
      4 describe('calculatePaceResults method', () => {
      5   test('should correctly calculate pace times', () => {
      6     const input = {
      7       distanceValue: 1,
      8       distanceUnit: 'kilometers',
      9       time: 100,
     10     };
     11     const target = {
     12       distanceValue: 20,
     13       distanceUnit: 'meters',
     14       type: 'distance',
     15     };
     16 
     17     const result = calculators.calculatePaceResults(input, target, 'metric', true);
     18 
     19     expect(result).to.deep.equal({
     20       key: '20 m',
     21       value: '0:02.00',
     22       pace: '1:40 / km',
     23       result: 'value',
     24       sort: 2,
     25     });
     26   });
     27 
     28   test('should correctly calculate pace distances according to default units setting', () => {
     29     const input = {
     30       distanceValue: 2,
     31       distanceUnit: 'miles',
     32       time: 1200,
     33     };
     34     const target = {
     35       time: 600,
     36       type: 'time',
     37     };
     38 
     39     const result1 = calculators.calculatePaceResults(input, target, 'metric', true);
     40     const result2 = calculators.calculatePaceResults(input, target, 'imperial', true);
     41 
     42     expect(result1.key).to.equal('1.61 km');
     43     expect(result1.value).to.equal('10:00');
     44     expect(result1.pace).to.equal('6:13 / km');
     45     expect(result1.result).to.equal('key');
     46     expect(result1.sort).to.be.closeTo(600, 0.01);
     47 
     48     expect(result2.key).to.equal('1.00 mi');
     49     expect(result2.value).to.equal('10:00');
     50     expect(result2.pace).to.equal('10:00 / mi');
     51     expect(result2.result).to.equal('key');
     52     expect(result2.sort).to.be.closeTo(600, 0.01);
     53   });
     54 });
     55 
     56 describe('calculateRaceResults method', () => {
     57   test('should correctly predict race times', () => {
     58     const input = {
     59       distanceValue: 5,
     60       distanceUnit: 'kilometers',
     61       time: 1200,
     62     };
     63     const target = {
     64       distanceValue: 10,
     65       distanceUnit: 'kilometers',
     66       type: 'distance',
     67     };
     68     const racePredictionOptions = {
     69       model: 'AverageModel',
     70       riegelExponent: 1.06,
     71     }
     72 
     73     const result = calculators.calculateRaceResults(input, target, racePredictionOptions,
     74       'imperial', true);
     75 
     76     expect(result.key).to.equal('10 km');
     77     expect(result.value).to.equal('41:34.80');
     78     expect(result.pace).to.equal('6:41 / mi');
     79     expect(result.result).to.equal('value');
     80     expect(result.sort).to.be.closeTo(2494.80, 0.01);
     81   });
     82 
     83   test('should correctly calculate race distances according to default units setting', () => {
     84     const input = {
     85       distanceValue: 5,
     86       distanceUnit: 'kilometers',
     87       time: 1200,
     88     };
     89     const target = {
     90       time: 2495,
     91       type: 'time',
     92     };
     93     const racePredictionOptions = {
     94       model: 'AverageModel',
     95       riegelExponent: 1.06,
     96     };
     97 
     98     const result1 = calculators.calculateRaceResults(input, target, racePredictionOptions,
     99       'metric', true);
    100     const result2 = calculators.calculateRaceResults(input, target, racePredictionOptions,
    101       'imperial', true);
    102 
    103     expect(result1.key).to.equal('10.00 km');
    104     expect(result1.value).to.equal('41:35');
    105     expect(result1.pace).to.equal('4:09 / km');
    106     expect(result1.result).to.equal('key');
    107     expect(result1.sort).to.equal(2495);
    108 
    109     expect(result2.key).to.equal('6.21 mi');
    110     expect(result2.value).to.equal('41:35');
    111     expect(result2.pace).to.equal('6:41 / mi');
    112     expect(result2.result).to.equal('key');
    113     expect(result2.sort).to.equal(2495);
    114   });
    115 
    116   test('should correctly predict race times according to race options', () => {
    117     const input = {
    118       distanceValue: 2,
    119       distanceUnit: 'miles',
    120       time: 630,
    121     };
    122     const target = {
    123       distanceValue: 5,
    124       distanceUnit: 'kilometers',
    125       type: 'distance',
    126     };
    127     const racePredictionOptions = {
    128       model: 'RiegelModel',
    129       riegelExponent: 1.12,
    130     }
    131 
    132     const result = calculators.calculateRaceResults(input, target, racePredictionOptions,
    133       'imperial', true);
    134 
    135     expect(result.key).to.equal('5 km');
    136     expect(result.value).to.equal('17:11.78');
    137     expect(result.pace).to.equal('5:32 / mi');
    138     expect(result.result).to.equal('value');
    139     expect(result.sort).to.be.closeTo(1031.77, 0.01);
    140   });
    141 });
    142 
    143 describe('calculateRaceStats method', () => {
    144   test('should correctly calculate race statistics', () => {
    145     const input = {
    146       distanceValue: 5,
    147       distanceUnit: 'kilometers',
    148       time: 1200,
    149     };
    150 
    151     const results = calculators.calculateRaceStats(input);
    152 
    153     expect(results.purdyPoints).to.be.closeTo(454.5, 0.1);
    154     expect(results.vo2).to.be.closeTo(47.4, 0.1);
    155     expect(results.vo2MaxPercentage).to.be.closeTo(95.3, 0.1);
    156     expect(results.vo2Max).to.be.closeTo(49.8, 0.1);
    157   });
    158 });
    159 
    160 describe('calculateWorkoutResults method', () => {
    161   test('should correctly calculate distance-based workouts according to race options', () => {
    162     const input = {
    163       distanceValue: 2,
    164       distanceUnit: 'miles',
    165       time: 630,
    166     };
    167     const target = {
    168       distanceValue: 5,
    169       distanceUnit: 'kilometers', // 5k split is ~17:11.77
    170       splitValue: 1000,
    171       splitUnit: 'meters',
    172       type: 'distance',
    173     };
    174     const racePredictionOptions = {
    175       model: 'RiegelModel',
    176       riegelExponent: 1.12,
    177     }
    178 
    179     const result = calculators.calculateWorkoutResults(input, target, racePredictionOptions,
    180                                                        false, true);
    181 
    182     expect(result.key).to.equal('1000 m @ 5 km');
    183     expect(result.value).to.equal('3:26.36');
    184     expect(result.pace).to.equal('');
    185     expect(result.result).to.equal('value');
    186     expect(result.sort).to.be.closeTo(206.35, 0.01);
    187   });
    188 
    189   test('should correctly calculate distance-based workouts according to custom names', () => {
    190     const input = {
    191       distanceValue: 2,
    192       distanceUnit: 'miles',
    193       time: 630,
    194     };
    195     const target_1 = {
    196       distanceValue: 5,
    197       distanceUnit: 'kilometers', // 5k split is ~17:11.77
    198       splitValue: 1000,
    199       splitUnit: 'meters',
    200       type: 'distance',
    201       // no custom name
    202     };
    203     const target_2 = {
    204       distanceValue: 5,
    205       distanceUnit: 'kilometers', // 5k split is ~17:11.77
    206       splitValue: 1000,
    207       splitUnit: 'meters',
    208       type: 'distance',
    209       customName: 'my custom name',
    210     };
    211     const racePredictionOptions = {
    212       model: 'RiegelModel',
    213       riegelExponent: 1.12,
    214     };
    215 
    216     const result1a = calculators.calculateWorkoutResults(input, target_1, racePredictionOptions,
    217       false, true);
    218     const result1b = calculators.calculateWorkoutResults(input, target_1, racePredictionOptions,
    219       true, true);
    220     const result2a = calculators.calculateWorkoutResults(input, target_2, racePredictionOptions,
    221       false, true);
    222     const result2b = calculators.calculateWorkoutResults(input, target_2, racePredictionOptions,
    223       true, true);
    224 
    225     expect(result1a.key).to.equal('1000 m @ 5 km');
    226     expect(result1b.key).to.equal('1000 m @ 5 km');
    227     expect(result2a.key).to.equal('1000 m @ 5 km');
    228     expect(result2b.key).to.equal('my custom name');
    229   });
    230 
    231   test('should correctly calculate time-based workouts', () => {
    232     const input = {
    233       distanceValue: 5,
    234       distanceUnit: 'kilometers',
    235       time: 1200,
    236     };
    237     const target = {
    238       time: 2495, // ~10k split is 41:35
    239       splitValue: 1,
    240       splitUnit: 'miles',
    241       type: 'time',
    242     };
    243     const racePredictionOptions = {
    244       model: 'AverageModel',
    245       riegelExponent: 1.06,
    246     }
    247 
    248     const result = calculators.calculateWorkoutResults(input, target, racePredictionOptions, false,
    249       true);
    250 
    251     expect(result.key).to.equal('1 mi @ 41:35');
    252     expect(result.value).to.equal('6:41.50');
    253     expect(result.pace).to.equal('');
    254     expect(result.result).to.equal('value');
    255     expect(result.sort).to.be.closeTo(401.50, 0.01);
    256   });
    257 });