commit 3ebace2a5a49947c003efcea22c3e8be79b0e031
parent ab7065f6464966d494328bb514b3327cdebb4f64
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Sat, 5 Jul 2025 20:37:26 -0700
Move *-target-set settings into *-options setting
Diffstat:
13 files changed, 571 insertions(+), 308 deletions(-)
diff --git a/src/utils/calculators.ts b/src/utils/calculators.ts
@@ -7,48 +7,54 @@ import { DistanceUnits, DistanceUnitData, UnitSystems, convertDistance,
getDefaultDistanceUnit } from '@/utils/units';
import type { DistanceTime } from '@/utils/units';
-/*
- * The two possible result fields of a target result: "key" and "value"
- */
-export enum ResultType {
- Key = 'key',
- Value = 'value',
-};
+export enum Calculators {
+ Pace,
+ Race,
+ Split,
+ Workout,
+}
/*
- * The type for target results
+ * The type for the available race statistics
*/
-export interface TargetResult {
- key: string,
- value: string,
- pace: string,
- result: ResultType,
- sort: number,
+export interface RaceStats {
+ purdyPoints: number,
+ vo2Max: number,
+ vo2: number,
+ vo2MaxPercentage: number,
};
/*
- * The type for the options specific to the race calculator
+ * The type for the options specific to each calculator
*/
-export interface RaceOptions {
+export interface StandardOptions {
+ selectedTargetSet: string,
+}
+export interface RaceOptions extends StandardOptions {
model: raceUtils.RacePredictionModel,
riegelExponent: number,
};
+export interface WorkoutOptions extends RaceOptions {
+ customTargetNames: boolean,
+};
/*
- * The type for the available race statistics
+ * The two possible result fields of a target result: "key" and "value"
*/
-export interface RaceStats {
- purdyPoints: number,
- vo2Max: number,
- vo2: number,
- vo2MaxPercentage: number,
+export enum ResultType {
+ Key = 'key',
+ Value = 'value',
};
/*
- * The type for the options specific to the workout calculator
+ * The type for target results
*/
-export interface WorkoutOptions extends RaceOptions {
- customTargetNames: boolean,
+export interface TargetResult {
+ key: string,
+ value: string,
+ pace: string,
+ result: ResultType,
+ sort: number,
};
/**
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
@@ -1,3 +1,5 @@
+import { RacePredictionModel } from '@/utils/races';
+
// The global localStorage prefix
const prefix = 'running-tools';
@@ -24,14 +26,67 @@ export function set<Type>(key: string, value: Type) {
}
/**
+ * Delete a localStorage item
+ * @param {string} key The localStorage item's key
+ */
+export function unset(key: string) {
+ localStorage.removeItem(`${prefix}.${key}`);
+}
+
+/**
* Migrate outdated localStorage options
*/
export function migrate() {
- // Add customTargetNames property to workout options (>1.4.1)
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const workoutOptions = get('workout-calculator-options') as any; // TODO: update types
- if (workoutOptions !== null && workoutOptions.customTargetNames === undefined) {
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+
+ // Move pace-calculator-target-set into new pace-calculator-options (>1.4.1)
+ const paceSelectedTargetSet = get<string>('pace-calculator-target-set');
+ if (paceSelectedTargetSet !== null) {
+ const paceOptions = { selectedTargetSet: paceSelectedTargetSet };
+ set('pace-calculator-options', paceOptions);
+ unset('pace-calculator-target-set');
+ }
+
+ // Move race-calculator-target-set into race-calculator-options (>1.4.1)
+ const raceSelectedTargetSet = get<string>('race-calculator-target-set');
+ const raceOptions = get<any>('race-calculator-options') || {
+ model: RacePredictionModel.AverageModel,
+ riegelExponent: 1.06,
+ selectedTargetSet: '_race_targets',
+ };
+ if (raceSelectedTargetSet !== null) {
+ raceOptions.selectedTargetSet = raceSelectedTargetSet;
+ set('race-calculator-options', raceOptions);
+ unset('race-calculator-target-set');
+ }
+
+ // Move split-calculator-target-set into new split-calculator-options (>1.4.1)
+ const splitSelectedTargetSet = get<string>('split-calculator-target-set');
+ if (splitSelectedTargetSet !== null) {
+ const splitOptions = { selectedTargetSet: splitSelectedTargetSet };
+ set('split-calculator-options', splitOptions);
+ unset('split-calculator-target-set');
+ }
+
+ // Move workout-calculator-target-set into workout-calculator-options (>1.4.1)
+ const workoutSelectedTargetSet = get<string>('workout-calculator-target-set');
+ const workoutOptions = get<any>('workout-calculator-options') || {
+ customTargetNames: false,
+ model: RacePredictionModel.AverageModel,
+ riegelExponent: 1.06,
+ selectedTargetSet: '_workout_targets',
+ };
+ if (workoutSelectedTargetSet !== null) {
+ workoutOptions.selectedTargetSet = workoutSelectedTargetSet;
+ set('workout-calculator-options', workoutOptions);
+ unset('workout-calculator-target-set');
+ }
+
+ // Add customTargetNames property to workout-calculator-options (>1.4.1)
+ if (workoutOptions.customTargetNames === undefined) {
workoutOptions.customTargetNames = false;
set('workout-calculator-options', workoutOptions);
}
+
+ /* eslint-enable @typescript-eslint/no-explicit-any */
}
diff --git a/src/views/BatchCalculator.vue b/src/views/BatchCalculator.vue
@@ -36,29 +36,30 @@
</div>
<div>
Target Set:
- <target-set-selector v-model:selectedTargetSet="selectedTargetSet"
+ <target-set-selector v-model:selectedTargetSet="calcOptions.selectedTargetSet"
:set-type="options.calculator === BatchCompatableCalculators.Workout ?
targetUtils.TargetSetTypes.Workout : targetUtils.TargetSetTypes.Standard"
v-model:targetSets="targetSets" :default-unit-system="defaultUnitSystem"
:customWorkoutNames="options.calculator === BatchCompatableCalculators.Workout ?
- (advancedOptions as WorkoutOptions).customTargetNames : false"/>
+ (calcOptions as WorkoutOptions).customTargetNames : false"/>
</div>
<div v-if="options.calculator === 'workout'">
Target Name Customization:
- <select v-model="(advancedOptions as WorkoutOptions).customTargetNames"
+ <select v-model="(calcOptions as WorkoutOptions).customTargetNames"
aria-label="Target name customization">
<option :value="false">Disabled</option>
<option :value="true">Enabled</option>
</select>
</div>
<race-options-input v-if="options.calculator !== BatchCompatableCalculators.Pace"
- v-model="advancedOptions as RaceOptions"/>
+ v-model="calcOptions as RaceOptions"/>
</details>
<h2>Batch Results</h2>
<double-output-table class="output" :input-times="inputTimes" :input-distance="inputDistance"
:calculate-result="calculateResult"
- :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []"/>
+ :targets="targetSets[calcOptions.selectedTargetSet] ?
+ targetSets[calcOptions.selectedTargetSet].targets : []"/>
</div>
</template>
@@ -66,7 +67,8 @@
import { computed } from 'vue';
import * as calcUtils from '@/utils/calculators';
-import type { RaceOptions, TargetResult, WorkoutOptions } from '@/utils/calculators';
+import type { RaceOptions, StandardOptions, TargetResult,
+ WorkoutOptions } from '@/utils/calculators';
import { RacePredictionModel } from '@/utils/races';
import * as targetUtils from '@/utils/targets';
import { DistanceUnits, UnitSystems, detectDefaultUnitSystem } from '@/utils/units';
@@ -123,14 +125,6 @@ const options = useStorage<BatchCalculatorOptions>('batch-calculator-options', {
const defaultUnitSystem = useStorage<UnitSystems>('default-unit-system', detectDefaultUnitSystem());
/*
- * The current selected target sets for each calculator
- */
-const selectedPaceTargetSet = useStorage<string>('pace-calculator-target-set', '_pace_targets');
-const selectedRaceTargetSet = useStorage<string>('race-calculator-target-set', '_race_targets');
-const selectedWorkoutTargetSet = useStorage<string>('workout-calculator-target-set',
- '_workout_targets');
-
-/*
* The target sets for each calculator
*/
const paceTargetSets = useStorage<targetUtils.StandardTargetSets>('pace-calculator-target-sets', {
@@ -144,16 +138,21 @@ const workoutTargetSets = useStorage<targetUtils.WorkoutTargetSets>('workout-cal
});
/*
- * The advanced options for each calculator
+ * The options for each calculator
*/
+const paceOptions = useStorage<StandardOptions>('pace-calculator-options', {
+ selectedTargetSet: '_pace_targets',
+});
const raceOptions = useStorage<RaceOptions>('race-calculator-options', {
model: RacePredictionModel.AverageModel,
riegelExponent: 1.06,
+ selectedTargetSet: '_race_targets',
});
const workoutOptions = useStorage<WorkoutOptions>('workout-calculator-options', {
customTargetNames: false,
model: RacePredictionModel.AverageModel,
riegelExponent: 1.06,
+ selectedTargetSet: '_workout_targets',
});
/*
@@ -176,43 +175,6 @@ const inputTimes = computed<Array<number>>(() => {
});
/*
- * The selected target set for the current calculator
- */
-const selectedTargetSet = computed<string>({
- get: (): string => {
- switch (options.value.calculator) {
- case (BatchCompatableCalculators.Pace): {
- return selectedPaceTargetSet.value;
- }
- case (BatchCompatableCalculators.Race): {
- return selectedRaceTargetSet.value;
- }
- default:
- case (BatchCompatableCalculators.Workout): {
- return selectedWorkoutTargetSet.value;
- }
- }
- },
- set: (newValue: string) => {
- switch (options.value.calculator) {
- case (BatchCompatableCalculators.Pace): {
- selectedPaceTargetSet.value = newValue;
- break;
- }
- case (BatchCompatableCalculators.Race): {
- selectedRaceTargetSet.value = newValue;
- break;
- }
- default:
- case (BatchCompatableCalculators.Workout): {
- selectedWorkoutTargetSet.value = newValue;
- break;
- }
- }
- },
-});
-
-/*
* The target sets for the current calculator
*/
const targetSets = computed<targetUtils.TargetSets>({
@@ -250,13 +212,13 @@ const targetSets = computed<targetUtils.TargetSets>({
});
/*
- * The advanced options for the current calculator
+ * The options for the current calculator
*/
-const advancedOptions = computed<null | RaceOptions | WorkoutOptions>({
+const calcOptions = computed<StandardOptions | RaceOptions | WorkoutOptions>({
get: () => {
switch (options.value.calculator) {
case (BatchCompatableCalculators.Pace): {
- return null;
+ return paceOptions.value;
}
case (BatchCompatableCalculators.Race): {
return raceOptions.value;
@@ -267,10 +229,10 @@ const advancedOptions = computed<null | RaceOptions | WorkoutOptions>({
}
}
},
- set: (newValue: null | RaceOptions | WorkoutOptions) => {
+ set: (newValue: StandardOptions | RaceOptions | WorkoutOptions) => {
switch(options.value.calculator) {
case (BatchCompatableCalculators.Pace): {
- // do nothing
+ paceOptions.value = newValue as StandardOptions;
break;
}
case (BatchCompatableCalculators.Race): {
diff --git a/src/views/PaceCalculator.vue b/src/views/PaceCalculator.vue
@@ -18,7 +18,7 @@
</div>
<div>
Target Set:
- <target-set-selector v-model:selectedTargetSet="selectedTargetSet"
+ <target-set-selector v-model:selectedTargetSet="options.selectedTargetSet"
v-model:targetSets="targetSets" :default-unit-system="defaultUnitSystem"/>
</div>
</details>
@@ -26,12 +26,14 @@
<h2>Equivalent Paces</h2>
<single-output-table class="output" :calculate-result="x =>
calculatePaceResults(input, x, defaultUnitSystem, true)"
- :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []"/>
+ :targets="targetSets[options.selectedTargetSet] ?
+ targetSets[options.selectedTargetSet].targets : []"/>
</div>
</template>
<script setup lang="ts">
import { calculatePaceResults } from '@/utils/calculators';
+import type { StandardOptions } from '@/utils/calculators';
import { defaultTargetSets } from '@/utils/targets';
import type { StandardTargetSets } from '@/utils/targets';
import { DistanceUnits, UnitSystems, detectDefaultUnitSystem } from '@/utils/units';
@@ -60,7 +62,9 @@ const defaultUnitSystem = useStorage<UnitSystems>('default-unit-system', detectD
/*
* The current selected target set
*/
-const selectedTargetSet = useStorage<string>('pace-calculator-target-set', '_pace_targets');
+const options = useStorage<StandardOptions>('pace-calculator-options', {
+ selectedTargetSet: '_pace_targets',
+});
/*
* The target sets
diff --git a/src/views/RaceCalculator.vue b/src/views/RaceCalculator.vue
@@ -35,7 +35,7 @@
</div>
<div>
Target Set:
- <target-set-selector v-model:selectedTargetSet="selectedTargetSet"
+ <target-set-selector v-model:selectedTargetSet="options.selectedTargetSet"
v-model:targetSets="targetSets" :default-unit-system="defaultUnitSystem"/>
</div>
<race-options-input v-model="options"/>
@@ -44,7 +44,8 @@
<h2>Equivalent Race Results</h2>
<single-output-table class="output" show-pace
:calculate-result="x => calculateRaceResults(input, x, options, defaultUnitSystem, true)"
- :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []"/>
+ :targets="targetSets[options.selectedTargetSet] ?
+ targetSets[options.selectedTargetSet].targets : []"/>
</div>
</template>
@@ -82,19 +83,15 @@ const input = useStorage<DistanceTime>('race-calculator-input', {
const defaultUnitSystem = useStorage<UnitSystems>('default-unit-system', detectDefaultUnitSystem());
/*
-* The race prediction options
+* The race calculator options
*/
const options = useStorage<RaceOptions>('race-calculator-options', {
model: RacePredictionModel.AverageModel,
riegelExponent: 1.06,
+ selectedTargetSet: '_race_targets',
});
/*
- * The current selected target set
- */
-const selectedTargetSet = useStorage<string>('race-calculator-target-set', '_race_targets');
-
-/*
* The target sets
*/
const targetSets = useStorage<StandardTargetSets>('race-calculator-target-sets', {
diff --git a/src/views/SplitCalculator.vue b/src/views/SplitCalculator.vue
@@ -11,7 +11,7 @@
<div class="target-set">
Target Set:
- <target-set-selector v-model:selectedTargetSet="selectedTargetSet"
+ <target-set-selector v-model:selectedTargetSet="options.selectedTargetSet"
:set-type="TargetSetTypes.Split" v-model:targetSets="targetSets"
:default-unit-system="defaultUnitSystem"/>
</div>
@@ -26,6 +26,7 @@
<script setup lang="ts">
import { computed } from 'vue';
+import type { StandardOptions } from '@/utils/calculators';
import { defaultTargetSets } from '@/utils/targets';
import { TargetSetTypes } from '@/utils/targets';
import type { SplitTargetSet, SplitTargetSets } from '@/utils/targets';
@@ -42,9 +43,11 @@ import useStorage from '@/composables/useStorage';
const defaultUnitSystem = useStorage<UnitSystems>('default-unit-system', detectDefaultUnitSystem());
/*
- * The current selected target set
+ * The split calculator options
*/
-const selectedTargetSet = useStorage<string>('split-calculator-target-set', '_split_targets');
+const options = useStorage<StandardOptions>('split-calculator-options', {
+ selectedTargetSet: '_split_targets'
+});
/*
* The default output targets
@@ -58,15 +61,15 @@ const targetSets = useStorage<SplitTargetSets>('split-calculator-target-sets', {
*/
const targetSet = computed({
get: () => {
- if (targetSets.value[selectedTargetSet.value]) {
- return targetSets.value[selectedTargetSet.value].targets
+ if (targetSets.value[options.value.selectedTargetSet]) {
+ return targetSets.value[options.value.selectedTargetSet].targets
} else {
return []
}
},
set: (newValue) => {
- if (targetSets.value[selectedTargetSet.value]) {
- targetSets.value[selectedTargetSet.value].targets = newValue;
+ if (targetSets.value[options.value.selectedTargetSet]) {
+ targetSets.value[options.value.selectedTargetSet].targets = newValue;
}
},
});
diff --git a/src/views/WorkoutCalculator.vue b/src/views/WorkoutCalculator.vue
@@ -18,7 +18,7 @@
</div>
<div>
Target Set:
- <target-set-selector v-model:selectedTargetSet="selectedTargetSet"
+ <target-set-selector v-model:selectedTargetSet="options.selectedTargetSet"
:set-type="TargetSetTypes.Workout" :customWorkoutNames="options.customTargetNames"
v-model:targetSets="targetSets" :default-unit-system="defaultUnitSystem"/>
</div>
@@ -35,7 +35,8 @@
<h2>Workout Splits</h2>
<single-output-table class="output"
:calculate-result="x => calculateWorkoutResults(input, x as WorkoutTarget, options, true)"
- :targets="targetSets[selectedTargetSet] ? targetSets[selectedTargetSet].targets : []"/>
+ :targets="targetSets[options.selectedTargetSet] ?
+ targetSets[options.selectedTargetSet].targets : []"/>
</div>
</template>
@@ -76,14 +77,10 @@ const options = useStorage<WorkoutOptions>('workout-calculator-options', {
customTargetNames: false,
model: RacePredictionModel.AverageModel,
riegelExponent: 1.06,
+ selectedTargetSet: '_workout_targets',
});
/*
- * The current selected target set
- */
-const selectedTargetSet = useStorage<string>('workout-calculator-target-set', '_workout_targets');
-
-/*
* The target sets
*/
const targetSets = useStorage<WorkoutTargetSets>('workout-calculator-target-sets', {
diff --git a/tests/e2e/cross-calculator.spec.js b/tests/e2e/cross-calculator.spec.js
@@ -216,7 +216,7 @@ test('Cross-calculator', async ({ page }) => {
await expect(page.getByRole('row')).toHaveCount(5);
// Assert general localStorage entries are correct
- expect(await page.evaluate(() => localStorage.length)).toEqual(18);
+ expect(await page.evaluate(() => localStorage.length)).toEqual(16);
expect(await page.evaluate(() => localStorage.getItem('running-tools.default-unit-system')))
.toEqual(JSON.stringify('metric'));
@@ -242,54 +242,54 @@ test('Cross-calculator', async ({ page }) => {
time: 930,
}));
const paceCalculatorKey = parseInt(JSON.parse(await page.evaluate(() =>
- localStorage.getItem('running-tools.pace-calculator-target-set'))));
+ localStorage.getItem('running-tools.pace-calculator-options'))).selectedTargetSet);
expect(paceCalculatorKey - parseInt(Date.now().toString())).toBeLessThan(100000);
- expect(await page.evaluate(() => localStorage.getItem('running-tools.pace-calculator-target-sets')))
- .toEqual(JSON.stringify({
- _pace_targets: {
- name: 'Common Pace Targets',
- targets: [
- { type: 'distance', distanceValue: 100, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 200, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 300, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 400, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 600, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 800, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 1000, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 1200, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 1500, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 1600, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 2, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 3, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 3200, distanceUnit: 'meters' },
- { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 4, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 3, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 6, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 8, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 5, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 6, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 10, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 8, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 10, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 0.5, distanceUnit: 'marathons' },
- { type: 'distance', distanceValue: 1, distanceUnit: 'marathons' },
- { type: 'time', time: 600 },
- { type: 'time', time: 1800 },
- { type: 'time', time: 3600 },
- ],
- },
- [paceCalculatorKey.toString()]: {
- name: '800m Splits',
- targets: [
- { type: 'distance', distanceValue: 0.4, distanceUnit: 'kilometers' },
- { type: 'distance', distanceValue: 800, distanceUnit: 'meters' },
- { type: 'time', time: 600 },
- ],
- },
- }));
+ expect(await page.evaluate(() =>
+ localStorage.getItem('running-tools.pace-calculator-target-sets'))).toEqual(JSON.stringify({
+ _pace_targets: {
+ name: 'Common Pace Targets',
+ targets: [
+ { type: 'distance', distanceValue: 100, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 200, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 300, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 400, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 600, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 800, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1000, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1200, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1500, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1600, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 3, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 3200, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 4, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 3, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 6, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 8, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 6, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 10, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 8, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 10, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 0.5, distanceUnit: 'marathons' },
+ { type: 'distance', distanceValue: 1, distanceUnit: 'marathons' },
+ { type: 'time', time: 600 },
+ { type: 'time', time: 1800 },
+ { type: 'time', time: 3600 },
+ ],
+ },
+ [paceCalculatorKey.toString()]: {
+ name: '800m Splits',
+ targets: [
+ { type: 'distance', distanceValue: 0.4, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 800, distanceUnit: 'meters' },
+ { type: 'time', time: 600 },
+ ],
+ },
+ }));
// Assert localStorage entries for the race calculator are correct
expect(await page.evaluate(() =>
@@ -302,11 +302,9 @@ test('Cross-calculator', async ({ page }) => {
localStorage.getItem('running-tools.race-calculator-options'))).toEqual(JSON.stringify({
model: 'RiegelModel',
riegelExponent: 1.06,
+ selectedTargetSet: '_race_targets',
}));
expect(await page.evaluate(() =>
- localStorage.getItem('running-tools.race-calculator-target-set')))
- .toEqual(JSON.stringify('_race_targets'));
- expect(await page.evaluate(() =>
localStorage.getItem('running-tools.race-calculator-target-sets'))).toEqual(JSON.stringify({
_race_targets: {
name: 'Common Race Targets',
@@ -333,8 +331,10 @@ test('Cross-calculator', async ({ page }) => {
// Assert localStorage entries for the split calculator are correct
expect(await page.evaluate(() =>
- localStorage.getItem('running-tools.split-calculator-target-set')))
- .toEqual(JSON.stringify('_split_targets'));
+ localStorage.getItem('running-tools.split-calculator-options')))
+ .toEqual(JSON.stringify({
+ selectedTargetSet: '_split_targets',
+ }));
expect(await page.evaluate(() =>
localStorage.getItem('running-tools.split-calculator-target-sets'))).toEqual(JSON.stringify({
_split_targets: {
@@ -381,11 +381,9 @@ test('Cross-calculator', async ({ page }) => {
customTargetNames: true,
model: 'VO2MaxModel',
riegelExponent: 1.06,
+ selectedTargetSet: '_workout_targets',
}));
expect(await page.evaluate(() =>
- localStorage.getItem('running-tools.workout-calculator-target-set')))
- .toEqual(JSON.stringify('_workout_targets'));
- expect(await page.evaluate(() =>
localStorage.getItem('running-tools.workout-calculator-target-sets'))).toEqual(JSON.stringify({
_workout_targets: {
name: 'Common Workout Targets',
@@ -508,27 +506,250 @@ test('Cross-calculator', async ({ page }) => {
test('v1.4.1 Migration', async ({ page }) => {
// Structure:
// - Set v1.4.1 localStorage entries
- // - Reload app and assert the proper migrations were performed
+ // - Reload app
+ // - Assert the proper localStorage migrations were performed
+ // - Assert UI options are up to date
- // Set v1.4.1 localStorage options
+ // Set general localStorage entries
await page.goto('/');
- await page.evaluate(() =>
- localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({
- // No customTargetNames property
+ await page.evaluate(() => localStorage.setItem('running-tools.default-unit-system',
+ JSON.stringify('metric')));
+
+ // Set batch calculator localStorage entries
+ await page.evaluate(() => localStorage.setItem('running-tools.batch-calculator-input',
+ JSON.stringify({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 630,
+ })
+ ));
+ await page.evaluate(() => localStorage.setItem('running-tools.batch-calculator-options',
+ JSON.stringify({
+ calculator: 'race',
+ increment: 10,
+ rows: 15,
+ })
+ ));
+
+ // Set pace calculator localStorage entries
+ await page.evaluate(() => localStorage.setItem('running-tools.pace-calculator-input',
+ JSON.stringify({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 930,
+ })
+ ));
+ await page.evaluate(() => localStorage.setItem('running-tools.pace-calculator-target-set',
+ JSON.stringify('123456789'))); // Property moved after v1.4.1
+ await page.evaluate(() => localStorage.setItem('running-tools.pace-calculator-target-sets',
+ JSON.stringify({
+ _pace_targets: {
+ name: 'Common Pace Targets',
+ targets: [
+ { type: 'distance', distanceValue: 100, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 200, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 300, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 400, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 600, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 800, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1000, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1200, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1500, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1600, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 3, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 3200, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 4, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 3, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 6, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 8, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 6, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 10, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 8, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 10, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 0.5, distanceUnit: 'marathons' },
+ { type: 'distance', distanceValue: 1, distanceUnit: 'marathons' },
+ { type: 'time', time: 600 },
+ { type: 'time', time: 1800 },
+ { type: 'time', time: 3600 },
+ ],
+ },
+ '123456789': {
+ name: '800m Splits',
+ targets: [
+ { type: 'distance', distanceValue: 0.4, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 800, distanceUnit: 'meters' },
+ { type: 'time', time: 600 },
+ ],
+ },
+ })
+ ));
+
+ // Set race calculator localStorage entries
+ await page.evaluate(() => localStorage.setItem('running-tools.race-calculator-input',
+ JSON.stringify({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 630,
+ })
+ ));
+ await page.evaluate(() => localStorage.setItem('running-tools.race-calculator-options',
+ JSON.stringify({
+ model: 'RiegelModel',
+ riegelExponent: 1.06,
+ })
+ ));
+ await page.evaluate(() => localStorage.setItem('running-tools.race-calculator-target-set',
+ JSON.stringify('_race_targets'))); // Property moved after v1.4.1
+ await page.evaluate(() => localStorage.setItem('running-tools.race-calculator-target-sets',
+ JSON.stringify({
+ _race_targets: {
+ name: 'Common Race Targets',
+ targets: [
+ { type: 'distance', distanceValue: 400, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 800, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1500, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1600, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 3000, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 3200, distanceUnit: 'meters' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 3, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 6, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 8, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 10, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 15, distanceUnit: 'kilometers' },
+ { type: 'distance', distanceValue: 0.5, distanceUnit: 'marathons' },
+ { type: 'distance', distanceValue: 1, distanceUnit: 'marathons' },
+ ],
+ },
+ })
+ ));
+
+ // Set split calculator localStorage entries
+ await page.evaluate(() => localStorage.setItem('running-tools.split-calculator-target-set',
+ JSON.stringify('_split_targets'))); // Property moved after v1.4.1
+ await page.evaluate(() => localStorage.setItem('running-tools.split-calculator-target-sets',
+ JSON.stringify({
+ _split_targets: {
+ name: '5K 1600m Splits',
+ targets: [
+ { type: 'distance', distanceValue: 1.6, distanceUnit: 'kilometers', splitTime: 420 },
+ { type: 'distance', distanceValue: 3.2, distanceUnit: 'kilometers', splitTime: 390 },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers', splitTime: 390 },
+ ],
+ },
+ })
+ ));
+
+ // Set unit calculator localStorage entries
+ await page.evaluate(() => localStorage.setItem('running-tools.unit-calculator-category',
+ JSON.stringify('speed_and_pace')));
+ await page.evaluate(() => localStorage.setItem('running-tools.unit-calculator-inputs',
+ JSON.stringify({
+ distance: {
+ inputValue: 1,
+ inputUnit: 'miles',
+ outputUnit: 'kilometers',
+ },
+ time: {
+ inputValue: 1,
+ inputUnit: 'seconds',
+ outputUnit: 'hh:mm:ss',
+ },
+ speed_and_pace: {
+ inputValue: 10,
+ inputUnit: 'kilometers_per_hour',
+ outputUnit: 'seconds_per_mile',
+ },
+ })
+ ));
+
+ // Set workout calculator localStorage entries
+ await page.evaluate(() => localStorage.setItem('running-tools.workout-calculator-input',
+ JSON.stringify({
+ distanceValue: 1,
+ distanceUnit: 'miles',
+ time: 301,
+ })
+ ));
+ await page.evaluate(() => localStorage.setItem('running-tools.workout-calculator-options',
+ JSON.stringify({
+ // customTargetNames property added after v1.4.1
model: 'VO2MaxModel',
riegelExponent: 1.06,
- })));
+ })
+ ));
+ await page.evaluate(() => localStorage.setItem('running-tools.workout-calculator-target-set',
+ JSON.stringify('_workout_targets'))); // Property moved after v1.4.1
+ await page.evaluate(() => localStorage.setItem('running-tools.workout-calculator-target-sets',
+ JSON.stringify({
+ _workout_targets: {
+ name: 'Common Workout Targets',
+ targets: [
+ {
+ splitValue: 400, splitUnit: 'meters',
+ type: 'distance', distanceValue: 1, distanceUnit: 'miles',
+ },
+ {
+ splitValue: 800, splitUnit: 'meters',
+ type: 'distance', distanceValue: 5, distanceUnit: 'kilometers',
+ },
+ {
+ splitValue: 1600, splitUnit: 'meters',
+ type: 'time', time: 3600,
+ },
+ {
+ splitValue: 1, splitUnit: 'miles',
+ type: 'distance', distanceValue: 1, distanceUnit: 'marathons',
+ },
+ ],
+ },
+ })
+ ));
// Reload the app and assert localStorage is updated
await page.goto('/');
+ expect(await page.evaluate(() => localStorage.getItem('running-tools.pace-calculator-options')))
+ .toEqual(JSON.stringify({
+ selectedTargetSet: '123456789',
+ }));
+ expect(await page.evaluate(() =>
+ localStorage.getItem('running-tools.pace-calculator-target-set'))).toBeNull();
+ expect(await page.evaluate(() => localStorage.getItem('running-tools.race-calculator-options')))
+ .toEqual(JSON.stringify({
+ model: 'RiegelModel',
+ riegelExponent: 1.06,
+ selectedTargetSet: '_race_targets',
+ }));
+ expect(await page.evaluate(() =>
+ localStorage.getItem('running-tools.race-calculator-target-set'))).toBeNull();
+ expect(await page.evaluate(() => localStorage.getItem('running-tools.split-calculator-options')))
+ .toEqual(JSON.stringify({
+ selectedTargetSet: '_split_targets',
+ }));
+ expect(await page.evaluate(() =>
+ localStorage.getItem('running-tools.split-calculator-target-set'))).toBeNull();
expect(await page.evaluate(() =>
localStorage.getItem('running-tools.workout-calculator-options'))).toEqual(JSON.stringify({
model: 'VO2MaxModel',
riegelExponent: 1.06,
+ selectedTargetSet: '_workout_targets',
customTargetNames: false,
}));
+ expect(await page.evaluate(() =>
+ localStorage.getItem('running-tools.workout-calculator-target-set'))).toBeNull();
- // Assert target name customization is disabled by default
+ // Assert migrated settings are correctly initialized and/or loaded
+ await page.getByRole('button', { name: 'Pace Calculator' }).click();
+ await page.getByText('Advanced Options').click();
+ await expect(page.getByLabel('Selected target set')).toHaveValue('123456789');
+ await page.getByRole('link', { name: 'Back' }).click();
await page.getByRole('button', { name: 'Workout Calculator' }).click();
await page.getByText('Advanced Options').click();
await expect(page.getByLabel('Target name customization')).toHaveValue('false');
diff --git a/tests/unit/views/BatchCalculator.spec.js b/tests/unit/views/BatchCalculator.spec.js
@@ -123,7 +123,7 @@ test('should save default units setting from localStorage when modified', async
expect(localStorage.getItem('running-tools.default-unit-system')).to.equal('"imperial"');
});
-test('should load selected target set from localStorage', async () => {
+test('should load calculator options from localStorage', async () => {
// Initialize localStorage
const selectedTargetSets = [
{
@@ -157,7 +157,6 @@ test('should load selected target set from localStorage', async () => {
],
}
}));
- localStorage.setItem('running-tools.pace-calculator-target-set', '"A"');
localStorage.setItem('running-tools.race-calculator-target-sets', JSON.stringify({
'C': selectedTargetSets[1],
'D': {
@@ -167,7 +166,6 @@ test('should load selected target set from localStorage', async () => {
],
}
}));
- localStorage.setItem('running-tools.race-calculator-target-set', '"C"');
localStorage.setItem('running-tools.workout-calculator-target-sets', JSON.stringify({
'E': selectedTargetSets[2],
'F': {
@@ -177,31 +175,57 @@ test('should load selected target set from localStorage', async () => {
],
}
}));
- localStorage.setItem('running-tools.workout-calculator-target-set', '"E"');
+ localStorage.setItem('running-tools.pace-calculator-options', JSON.stringify({
+ selectedTargetSet: 'A',
+ }));
+ localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({
+ model: 'PurdyPointsModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: 'C',
+ }));
+ localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({
+ customTargetNames: true,
+ model: 'RiegelModel',
+ riegelExponent: 1.1,
+ selectedTargetSet: 'E',
+ }));
// Initialize component
const wrapper = shallowMount(BatchCalculator);
- // Assert selected pace target set is loaded
+ // Assert pace calculator options are loaded
await wrapper.find('select[aria-label="Calculator"]').setValue('pace');
expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('A');
expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets)
.to.deep.equal(selectedTargetSets[0].targets);
- // Assert selected race target set is loaded
+ // Assert race calculator options are loaded
await wrapper.find('select[aria-label="Calculator"]').setValue('race');
+ expect(wrapper.findComponent({ name: 'RaceOptionsInput' }).vm.modelValue).to.deep.equal({
+ model: 'PurdyPointsModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: 'C',
+ });
expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('C');
expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets)
.to.deep.equal(selectedTargetSets[1].targets);
- // Assert selected workout target set is loaded
+ // Assert workout calculator options are loaded
await wrapper.find('select[aria-label="Calculator"]').setValue('workout');
+ expect(wrapper.findComponent({ name: 'RaceOptionsInput' }).vm.modelValue).to.deep.equal({
+ customTargetNames: true,
+ model: 'RiegelModel',
+ riegelExponent: 1.1,
+ selectedTargetSet: 'E',
+ });
+ expect(wrapper.find('select[aria-label="Target name customization"]').element.value)
+ .to.equal('true');
expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('E');
expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets)
.to.deep.equal(selectedTargetSets[2].targets);
});
-test('should save selected target set to localStorage when modified', async () => {
+test('should save calculator options to localStorage when modified', async () => {
// Initialize localStorage
const selectedTargetSets = [
{
@@ -235,7 +259,9 @@ test('should save selected target set to localStorage when modified', async () =
],
}
}));
- localStorage.setItem('running-tools.pace-calculator-target-set', '"B"');
+ localStorage.setItem('running-tools.pace-calculator-options', JSON.stringify({
+ selectedTargetSet: 'B',
+ }));
localStorage.setItem('running-tools.race-calculator-target-sets', JSON.stringify({
'C': selectedTargetSets[1],
'D': {
@@ -245,7 +271,11 @@ test('should save selected target set to localStorage when modified', async () =
],
}
}));
- localStorage.setItem('running-tools.race-calculator-target-set', '"D"');
+ localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({
+ model: 'AverageModel',
+ riegelExponent: 1.06,
+ selectedTargetSet: 'D',
+ }));
localStorage.setItem('running-tools.workout-calculator-target-sets', JSON.stringify({
'E': selectedTargetSets[2],
'F': {
@@ -255,60 +285,61 @@ test('should save selected target set to localStorage when modified', async () =
],
}
}));
- localStorage.setItem('running-tools.workout-calculator-target-set', '"F"');
+ localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({
+ customWorkoutNames: false,
+ model: 'AverageModel',
+ riegelExponent: 1.06,
+ selectedTargetSet: 'F',
+ }));
// Initialize component
const wrapper = shallowMount(BatchCalculator);
- // Update selected pace target set
+ // Update pace calculator options X
await wrapper.find('select[aria-label="Calculator"]').setValue('pace');
await wrapper.findComponent({ name: 'target-set-selector' }).setValue('A', 'selectedTargetSet');
expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets)
.to.deep.equal(selectedTargetSets[0].targets);
- expect(localStorage.getItem('running-tools.pace-calculator-target-set')).to.equal('"A"');
- // Assert selected race target set is loaded
+ // Update race calculator options
await wrapper.find('select[aria-label="Calculator"]').setValue('race');
+ await wrapper.findComponent({ name: 'RaceOptionsInput' }).setValue({
+ model: 'PurdyPointsModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: 'D',
+ });
await wrapper.findComponent({ name: 'target-set-selector' }).setValue('C', 'selectedTargetSet');
expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets)
.to.deep.equal(selectedTargetSets[1].targets);
- expect(localStorage.getItem('running-tools.race-calculator-target-set')).to.equal('"C"');
- // Assert selected workout target set is loaded
+ // Update workout calculator options
await wrapper.find('select[aria-label="Calculator"]').setValue('workout');
+ await wrapper.findComponent({ name: 'RaceOptionsInput' }).setValue({
+ customTargetNames: false,
+ model: 'RiegelModel',
+ riegelExponent: 1.1,
+ selectedTargetSet: 'F',
+ });
+ wrapper.find('select[aria-label="Target name customization"]').setValue('true');
await wrapper.findComponent({ name: 'target-set-selector' }).setValue('E', 'selectedTargetSet');
expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets)
.to.deep.equal(selectedTargetSets[2].targets);
- expect(localStorage.getItem('running-tools.workout-calculator-target-set')).to.equal('"E"');
-});
-test('should load advanced model options from localStorage', async () => {
- // Initialize localStorage
- localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({
+ // Assert options saved to localStorage
+ expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(JSON.stringify({
+ selectedTargetSet: 'A',
+ }));
+ expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(JSON.stringify({
model: 'PurdyPointsModel',
riegelExponent: 1.2,
+ selectedTargetSet: 'C',
}));
- localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({
+ expect(localStorage.getItem('running-tools.workout-calculator-options')).to.equal(JSON.stringify({
+ customTargetNames: true,
model: 'RiegelModel',
riegelExponent: 1.1,
+ selectedTargetSet: 'E',
}));
-
- // Initialize component
- const wrapper = shallowMount(BatchCalculator);
-
- // Assert race prediction options are loaded
- await wrapper.find('select[aria-label="Calculator"]').setValue('race');
- expect(wrapper.findComponent({ name: 'RaceOptionsInput' }).vm.modelValue).to.deep.equal({
- model: 'PurdyPointsModel',
- riegelExponent: 1.2,
- });
-
- // Assert workout prediction options are loaded
- await wrapper.find('select[aria-label="Calculator"]').setValue('workout');
- expect(wrapper.findComponent({ name: 'RaceOptionsInput' }).vm.modelValue).to.deep.equal({
- model: 'RiegelModel',
- riegelExponent: 1.1,
- });
});
test('should pass correct input props to DoubleOutputTable', async () => {
@@ -371,10 +402,12 @@ test('should pass correct input props to DoubleOutputTable', async () => {
test('should correctly calculate outputs', async () => {
// Initialize localStorage
localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({
+ selectedTargetSet: '_race_targets',
model: 'PurdyPointsModel',
riegelExponent: 1.2,
}));
localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({
+ selectedTargetSet: '_workout_targets',
model: 'RiegelModel',
riegelExponent: 1.1,
}));
diff --git a/tests/unit/views/PaceCalculator.spec.js b/tests/unit/views/PaceCalculator.spec.js
@@ -132,7 +132,7 @@ test('should save input pace to localStorage', async () => {
}));
});
-test('should load selected target set from localStorage', async () => {
+test('should load options from localStorage', async () => {
// Initialize localStorage
const targetSet2 = {
name: 'Pace targets #2',
@@ -153,7 +153,9 @@ test('should load selected target set from localStorage', async () => {
},
'B': targetSet2,
}));
- localStorage.setItem('running-tools.pace-calculator-target-set', '"B"');
+ localStorage.setItem('running-tools.pace-calculator-options', JSON.stringify({
+ selectedTargetSet: 'B',
+ }));
// Initialize component
const wrapper = shallowMount(PaceCalculator);
@@ -165,7 +167,7 @@ test('should load selected target set from localStorage', async () => {
.to.deep.equal(targetSet2.targets);
});
-test('should save selected target set to localStorage when modified', async () => {
+test('should save options to localStorage when modified', async () => {
// Initialize component
const wrapper = shallowMount(PaceCalculator);
@@ -174,8 +176,9 @@ test('should save selected target set to localStorage when modified', async () =
.setValue('B', 'selectedTargetSet');
// New selected target set should be saved to localStorage
- expect(localStorage.getItem('running-tools.pace-calculator-target-set'))
- .to.equal('"B"');
+ expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(JSON.stringify({
+ selectedTargetSet: 'B',
+ }));
});
test('should save default units setting to localStorage when modified', async () => {
diff --git a/tests/unit/views/RaceCalculator.spec.js b/tests/unit/views/RaceCalculator.spec.js
@@ -123,7 +123,7 @@ test('should correctly calculate race statistics', async () => {
expect(vo2Max).to.equal('V̇O₂ Max: 49.8 ml/kg/min')
});
-test('should correctly calculate results according to advanced model options', async () => {
+test('should correctly calculate results according to model options', async () => {
// Initialize component
const wrapper = shallowMount(RaceCalculator);
@@ -138,6 +138,7 @@ test('should correctly calculate results according to advanced model options', a
await wrapper.findComponent({ name: 'RaceOptionsInput' }).setValue({
model: 'RiegelModel',
riegelExponent: 1.06, // default value
+ selectedTargetSet: '_race_targets',
});
// Calculate result
@@ -155,6 +156,7 @@ test('should correctly calculate results according to advanced model options', a
await wrapper.findComponent({ name: 'RaceOptionsInput' }).setValue({
model: 'RiegelModel', // existing value
riegelExponent: 1,
+ selectedTargetSet: '_race_targets',
});
// Calculate result
@@ -206,7 +208,19 @@ test('should save input race to localStorage', async () => {
}));
});
-test('should load selected target set from localStorage', async () => {
+test('should save default units setting to localStorage when modified', async () => {
+ // Initialize component
+ const wrapper = shallowMount(RaceCalculator);
+
+ // Change default units
+ await wrapper.find('select[aria-label="Default units"]').setValue('metric');
+ await wrapper.find('select[aria-label="Default units"]').setValue('imperial');
+
+ // New default units should be saved to localStorage
+ expect(localStorage.getItem('running-tools.default-unit-system')).to.equal('"imperial"');
+});
+
+test('should load options from localStorage', async () => {
// Initialize localStorage
const targetSet2 = {
name: 'Race targets #2',
@@ -227,48 +241,10 @@ test('should load selected target set from localStorage', async () => {
},
'B': targetSet2,
}));
- localStorage.setItem('running-tools.race-calculator-target-set', '"B"');
-
- // Initialize component
- const wrapper = shallowMount(RaceCalculator);
-
- // Assert selection is loaded
- expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
- .to.equal('B');
- expect(wrapper.findComponent({ name: 'single-output-table' }).vm.targets)
- .to.deep.equal(targetSet2.targets);
-});
-
-test('should save selected target set to localStorage when modified', async () => {
- // Initialize component
- const wrapper = shallowMount(RaceCalculator);
-
- // Select a new target set
- await wrapper.findComponent({ name: 'target-set-selector' })
- .setValue('B', 'selectedTargetSet');
-
- // New selected target set should be saved to localStorage
- expect(localStorage.getItem('running-tools.race-calculator-target-set'))
- .to.equal('"B"');
-});
-
-test('should save default units setting to localStorage when modified', async () => {
- // Initialize component
- const wrapper = shallowMount(RaceCalculator);
-
- // Change default units
- await wrapper.find('select[aria-label="Default units"]').setValue('metric');
- await wrapper.find('select[aria-label="Default units"]').setValue('imperial');
-
- // New default units should be saved to localStorage
- expect(localStorage.getItem('running-tools.default-unit-system')).to.equal('"imperial"');
-});
-
-test('should load advanced model options from localStorage', async () => {
- // Initialize localStorage
localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({
model: 'PurdyPointsModel',
riegelExponent: 1.2,
+ selectedTargetSet: 'B',
}));
// Initialize component
@@ -278,23 +254,32 @@ test('should load advanced model options from localStorage', async () => {
expect(wrapper.findComponent({ name: 'RaceOptionsInput' }).vm.modelValue).to.deep.equal({
model: 'PurdyPointsModel',
riegelExponent: 1.2,
+ selectedTargetSet: 'B',
});
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
+ .to.equal('B');
+ expect(wrapper.findComponent({ name: 'single-output-table' }).vm.targets)
+ .to.deep.equal(targetSet2.targets);
});
-test('should save advanced model options to localStorage when modified', async () => {
+test('should save options to localStorage when modified', async () => {
// Initialize component
const wrapper = shallowMount(RaceCalculator);
- // Update advanced model options
+ // Update options
await wrapper.findComponent({ name: 'RaceOptionsInput' }).setValue({
model: 'CameronModel',
riegelExponent: 1.30,
+ selectedTargetSet: '_race_targets',
});
+ await wrapper.findComponent({ name: 'target-set-selector' })
+ .setValue('B', 'selectedTargetSet');
// Assert data saved to localStorage
expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(JSON.stringify({
model: 'CameronModel',
riegelExponent: 1.3,
+ selectedTargetSet: 'B',
}));
});
diff --git a/tests/unit/views/SplitCalculator.spec.js b/tests/unit/views/SplitCalculator.spec.js
@@ -8,7 +8,9 @@ beforeEach(() => {
test('should load selected target set from localStorage', async () => {
// Initialize localStorage
- localStorage.setItem('running-tools.split-calculator-target-set', '"B"');
+ localStorage.setItem('running-tools.split-calculator-options', JSON.stringify({
+ selectedTargetSet: 'B',
+ }));
// Initialize component
const wrapper = shallowMount(SplitCalculator);
@@ -65,13 +67,16 @@ test('should load targets from localStorage and pass to splitOutputTable', async
test('should correctly handle null target set', async () => {
// Initialize localStorage
- localStorage.setItem('running-tools.split-calculator-target-set', '"does_not_exist"');
+ localStorage.setItem('running-tools.split-calculator-options', JSON.stringify({
+ selectedTargetSet: 'does_not_exist',
+ }));
// Initialize component
const wrapper = shallowMount(SplitCalculator);
// Assert selection is loaded
- expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('does_not_exist');
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
+ .to.equal('does_not_exist');
// Assert empty array passed to SplitOutputTable
expect(wrapper.findComponent({ name: 'split-output-table' }).vm.modelValue).to.deep.equal([]);
@@ -133,8 +138,9 @@ test('should save selected target set to localStorage when modified', async () =
.setValue('_race_targets', 'selectedTargetSet');
// New selected target set should be saved to localStorage
- expect(localStorage.getItem('running-tools.split-calculator-target-set'))
- .to.equal('"_race_targets"');
+ expect(localStorage.getItem('running-tools.split-calculator-options')).to.equal(JSON.stringify({
+ selectedTargetSet: '_race_targets',
+ }));
});
test('should load default units from localStorage and pass to splitOutputTable', async () => {
diff --git a/tests/unit/views/WorkoutCalculator.spec.js b/tests/unit/views/WorkoutCalculator.spec.js
@@ -68,6 +68,7 @@ test('should correctly calculate results according to advanced model options', a
await wrapper.findComponent({ name: 'RaceOptionsInput' }).setValue({
model: 'RiegelModel',
riegelExponent: 1.10,
+ selectedTargetSet: '_workout_targets',
});
// Calculate result
@@ -121,7 +122,19 @@ test('should save input race to localStorage', async () => {
}));
});
-test('should load selected target set from localStorage', async () => {
+test('should save default units setting to localStorage when modified', async () => {
+ // Initialize component
+ const wrapper = shallowMount(WorkoutCalculator);
+
+ // Change default units
+ await wrapper.find('select[aria-label="Default units"]').setValue('metric');
+ await wrapper.find('select[aria-label="Default units"]').setValue('imperial');
+
+ // New default units should be saved to localStorage
+ expect(localStorage.getItem('running-tools.default-unit-system')).to.equal('"imperial"');
+});
+
+test('should load options from localStorage', async () => {
// Initialize localStorage
const targetSet2 = {
name: 'Workout targets #2',
@@ -167,48 +180,11 @@ test('should load selected target set from localStorage', async () => {
},
'B': targetSet2,
}));
- localStorage.setItem('running-tools.workout-calculator-target-set', '"B"');
-
- // Initialize component
- const wrapper = shallowMount(WorkoutCalculator);
-
- // Assert selection is loaded
- expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
- .to.equal('B');
- expect(wrapper.findComponent({ name: 'single-output-table' }).vm.targets)
- .to.deep.equal(targetSet2.targets);
-});
-
-test('should save selected target set to localStorage when modified', async () => {
- // Initialize component
- const wrapper = shallowMount(WorkoutCalculator);
-
- // Select a new target set
- await wrapper.findComponent({ name: 'target-set-selector' })
- .setValue('B', 'selectedTargetSet');
-
- // New selected target set should be saved to localStorage
- expect(localStorage.getItem('running-tools.workout-calculator-target-set'))
- .to.equal('"B"');
-});
-
-test('should save default units setting to localStorage when modified', async () => {
- // Initialize component
- const wrapper = shallowMount(WorkoutCalculator);
-
- // Change default units
- await wrapper.find('select[aria-label="Default units"]').setValue('metric');
- await wrapper.find('select[aria-label="Default units"]').setValue('imperial');
-
- // New default units should be saved to localStorage
- expect(localStorage.getItem('running-tools.default-unit-system')).to.equal('"imperial"');
-});
-
-test('should load advanced model options from localStorage', async () => {
- // Initialize localStorage
localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({
+ customTargetNames: true,
model: 'PurdyPointsModel',
riegelExponent: 1.2,
+ selectedTargetSet: 'B',
}));
// Initialize component
@@ -216,24 +192,39 @@ test('should load advanced model options from localStorage', async () => {
// Assert data loaded
expect(wrapper.findComponent({ name: 'RaceOptionsInput' }).vm.modelValue).to.deep.equal({
+ customTargetNames: true,
model: 'PurdyPointsModel',
riegelExponent: 1.2,
+ selectedTargetSet: 'B',
});
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
+ .to.equal('B');
+ expect(wrapper.findComponent({ name: 'single-output-table' }).vm.targets)
+ .to.deep.equal(targetSet2.targets);
+ expect(wrapper.find('select[aria-label="Target name customization"]').element.value)
+ .to.equal('true');
});
-test('should save advanced model options to localStorage when modified', async () => {
+test('should save options to localStorage when modified', async () => {
// Initialize component
const wrapper = shallowMount(WorkoutCalculator);
- // Update advanced model options
+ // Update options
await wrapper.findComponent({ name: 'RaceOptionsInput' }).setValue({
+ customTargetNames: false,
model: 'CameronModel',
riegelExponent: 1.30,
+ selectedTargetSet: '_workout_targets',
});
+ wrapper.find('select[aria-label="Target name customization"]').setValue('true');
+ await wrapper.findComponent({ name: 'target-set-selector' })
+ .setValue('B', 'selectedTargetSet');
// Assert data saved to localStorage
expect(localStorage.getItem('running-tools.workout-calculator-options')).to.equal(JSON.stringify({
+ customTargetNames: true,
model: 'CameronModel',
riegelExponent: 1.3,
+ selectedTargetSet: 'B',
}));
});