commit f8903eefac949b7eebf6e2c3a33bb6d55e2af521
parent 033fc052c51ba689bd8e44ccf370f9262f951760
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Sun, 13 Jul 2025 12:38:41 -0700
Hide riegel exponent option when not applicable
Also rename RacePredictionModel enum to RacePredictionModels.
Diffstat:
4 files changed, 61 insertions(+), 21 deletions(-)
diff --git a/src/components/AdvancedOptionsInput.vue b/src/components/AdvancedOptionsInput.vue
@@ -27,15 +27,17 @@
<div v-if="props.type === Calculators.Race || props.type === Calculators.Workout">
Prediction Model:
<select v-model="(options as RaceOptions).model" aria-label="Prediction model">
- <option value="AverageModel">Average</option>
- <option value="PurdyPointsModel">Purdy Points Model</option>
- <option value="VO2MaxModel">V̇O₂ Max Model</option>
- <option value="CameronModel">Cameron's Model</option>
- <option value="RiegelModel">Riegel's Model</option>
+ <option :value="RacePredictionModels.AverageModel">Average</option>
+ <option :value="RacePredictionModels.PurdyPointsModel">Purdy Points Model</option>
+ <option :value="RacePredictionModels.VO2MaxModel">V̇O₂ Max Model</option>
+ <option :value="RacePredictionModels.CameronModel">Cameron's Model</option>
+ <option :value="RacePredictionModels.RiegelModel">Riegel's Model</option>
</select>
</div>
- <div v-if="props.type === Calculators.Race || props.type === Calculators.Workout">
+ <div v-if="props.type === Calculators.Race || props.type === Calculators.Workout"
+ v-show="(options as RaceOptions).model == RacePredictionModels.AverageModel ||
+ (options as RaceOptions).model == RacePredictionModels.RiegelModel">
Riegel Exponent:
<decimal-input v-model="(options as RaceOptions).riegelExponent"
aria-label="Riegel exponent" :min="1" :max="1.3" :digits="2" :step="0.01"/>
@@ -46,6 +48,7 @@
<script setup lang="ts">
import { Calculators } from '@/core/calculators';
import type { StandardOptions, RaceOptions, WorkoutOptions } from '@/core/calculators';
+import { RacePredictionModels } from '@/core/racePrediction';
import type { TargetSets } from '@/core/targets';
import { UnitSystems } from '@/core/units';
diff --git a/src/core/calculators.ts b/src/core/calculators.ts
@@ -38,7 +38,7 @@ export interface StandardOptions {
selectedTargetSet: string,
}
export interface RaceOptions extends StandardOptions {
- model: racePrediction.RacePredictionModel,
+ model: racePrediction.RacePredictionModels,
riegelExponent: number,
};
export interface WorkoutOptions extends RaceOptions {
@@ -86,7 +86,7 @@ export const defaultPaceOptions: StandardOptions = {
selectedTargetSet: '_pace_targets',
};
export const defaultRaceOptions: RaceOptions = {
- model: racePrediction.RacePredictionModel.AverageModel,
+ model: racePrediction.RacePredictionModels.AverageModel,
riegelExponent: 1.06,
selectedTargetSet: '_race_targets',
};
diff --git a/src/core/racePrediction.ts b/src/core/racePrediction.ts
@@ -5,7 +5,7 @@
/*
* The available race prediction models
*/
-export enum RacePredictionModel {
+export enum RacePredictionModels {
AverageModel = 'AverageModel',
PurdyPointsModel = 'PurdyPointsModel',
VO2MaxModel = 'VO2MaxModel',
@@ -442,19 +442,19 @@ const AverageModel = {
* @param {number} c The value of the exponent in Pete Riegel's Model
*/
export function predictTime(d1: number, t1: number, d2: number,
- model: RacePredictionModel = RacePredictionModel.AverageModel,
+ model: RacePredictionModels = RacePredictionModels.AverageModel,
c: number = 1.06): number {
switch (model) {
default:
- case RacePredictionModel.AverageModel:
+ case RacePredictionModels.AverageModel:
return AverageModel.predictTime(d1, t1, d2, c);
- case RacePredictionModel.PurdyPointsModel:
+ case RacePredictionModels.PurdyPointsModel:
return PurdyPointsModel.predictTime(d1, t1, d2);
- case RacePredictionModel.VO2MaxModel:
+ case RacePredictionModels.VO2MaxModel:
return VO2MaxModel.predictTime(d1, t1, d2);
- case RacePredictionModel.RiegelModel:
+ case RacePredictionModels.RiegelModel:
return RiegelModel.predictTime(d1, t1, d2, c);
- case RacePredictionModel.CameronModel:
+ case RacePredictionModels.CameronModel:
return CameronModel.predictTime(d1, t1, d2);
}
}
@@ -468,19 +468,19 @@ export function predictTime(d1: number, t1: number, d2: number,
* @param {number} c The value of the exponent in Pete Riegel's Model
*/
export function predictDistance(t1: number, d1: number, t2: number,
- model: RacePredictionModel = RacePredictionModel.AverageModel,
+ model: RacePredictionModels = RacePredictionModels.AverageModel,
c: number = 1.06) {
switch (model) {
default:
- case RacePredictionModel.AverageModel:
+ case RacePredictionModels.AverageModel:
return AverageModel.predictDistance(t1, d1, t2, c);
- case RacePredictionModel.PurdyPointsModel:
+ case RacePredictionModels.PurdyPointsModel:
return PurdyPointsModel.predictDistance(t1, d1, t2);
- case RacePredictionModel.VO2MaxModel:
+ case RacePredictionModels.VO2MaxModel:
return VO2MaxModel.predictDistance(t1, d1, t2);
- case RacePredictionModel.RiegelModel:
+ case RacePredictionModels.RiegelModel:
return RiegelModel.predictDistance(t1, d1, t2, c);
- case RacePredictionModel.CameronModel:
+ case RacePredictionModels.CameronModel:
return CameronModel.predictDistance(t1, d1, t2);
}
}
diff --git a/tests/unit/components/AdvancedOptionsInput.spec.js b/tests/unit/components/AdvancedOptionsInput.spec.js
@@ -85,6 +85,43 @@ test('should be correctly render race options according to props', () => {
expect(wrapper.find('select[aria-label="Prediction model"]').element.value).to
.equal('PurdyPointsModel');
expect(wrapper.findComponent({ name: 'decimal-input' }).vm.modelValue).to.equal(1.2);
+ expect(wrapper.findComponent({ name: 'decimal-input' }).isVisible()).to.equal(false);
+});
+
+test('should render riegel exponent field only for supported race prediction models', async () => {
+ // Initialize component
+ const wrapper = shallowMount(AdvancedOptionsInput, {
+ propsData: {
+ defaultUnitSystem: 'metric',
+ options: {
+ model: 'AverageModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: '_new',
+ },
+ type: 'race',
+ targetSets: {},
+ },
+ attachTo: document.body,
+ });
+
+ // Assert field is visible for Average model
+ expect(wrapper.findComponent({ name: 'decimal-input' }).isVisible()).to.equal(true);
+
+ // Assert field is not visible for Purdy Points model
+ await wrapper.find('select[aria-label="Prediction model"]').setValue('PurdyPointsModel');
+ expect(wrapper.findComponent({ name: 'decimal-input' }).isVisible()).to.equal(false);
+
+ // Assert field is not visible for VO2 Max model
+ await wrapper.find('select[aria-label="Prediction model"]').setValue('VO2MaxModel');
+ expect(wrapper.findComponent({ name: 'decimal-input' }).isVisible()).to.equal(false);
+
+ // Assert field is not visible for Cameron model
+ await wrapper.find('select[aria-label="Prediction model"]').setValue('CameronModel');
+ expect(wrapper.findComponent({ name: 'decimal-input' }).isVisible()).to.equal(false);
+
+ // Assert field is not visible for Riegel model
+ await wrapper.find('select[aria-label="Prediction model"]').setValue('RiegelModel');
+ expect(wrapper.findComponent({ name: 'decimal-input' }).isVisible()).to.equal(true);
});
test('should be correctly render split options according to props', () => {