running-tools

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

commit 0ad27a6907f79aaebb3cf7a8c7d9c5e2c71a79a8
parent cd745cb3c2383acfad03df9d1d576bb3278540b9
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Fri, 22 Aug 2025 19:42:05 -0700

Reorganize localStorage items

Move *-calculator-input items to *-calculator-options.input. Migration
scripts and corresponding end-to-end tests not yet updated.

Diffstat:
Msrc/components/AdvancedOptionsInput.vue | 14++++----------
Msrc/core/calculators.ts | 18+++++++++++++-----
Msrc/views/BatchCalculator.vue | 73++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/views/PaceCalculator.vue | 24+++++++++---------------
Msrc/views/RaceCalculator.vue | 26++++++++++----------------
Msrc/views/SplitCalculator.vue | 14+++++++-------
Msrc/views/WorkoutCalculator.vue | 24+++++++++---------------
Mtests/e2e/cross-calculator.spec.js | 50+++++++++++++++++++++++++-------------------------
Mtests/unit/components/AdvancedOptionsInput.spec.js | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mtests/unit/views/BatchCalculator.spec.js | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mtests/unit/views/PaceCalculator.spec.js | 104++++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mtests/unit/views/RaceCalculator.spec.js | 92+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mtests/unit/views/SplitCalculator.spec.js | 4++--
Mtests/unit/views/WorkoutCalculator.spec.js | 119++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
14 files changed, 542 insertions(+), 329 deletions(-)

diff --git a/src/components/AdvancedOptionsInput.vue b/src/components/AdvancedOptionsInput.vue @@ -25,10 +25,10 @@ </select> </div> - <div v-if="batchOptions && props.batchInput && props.type === Calculators.Workout" + <div v-if="batchOptions && props.type === Calculators.Workout" v-show="(options as WorkoutOptions).customTargetNames"> Batch column label: - <input v-model="batchOptions.label" :placeholder="formatDistance(props.batchInput, false)" + <input v-model="batchOptions.label" :placeholder="formatDistance(batchOptions.input, false)" aria-label="Batch column label"/> </div> @@ -56,27 +56,21 @@ <script setup lang="ts"> import { Calculators } from '@/core/calculators'; -import type { BatchOptions, GlobalOptions, StandardOptions, RaceOptions, +import type { BatchOptions, GlobalOptions, PaceOptions, RaceOptions, SplitOptions, WorkoutOptions } from '@/core/calculators'; import { RacePredictionModels } from '@/core/racePrediction'; import type { TargetSets } from '@/core/targets'; import { formatDistance } from '@/core/units'; -import type { DistanceTime } from '@/core/units'; import DecimalInput from '@/components/DecimalInput.vue'; import TargetSetSelector from '@/components/TargetSetSelector.vue'; import useObjectModel from '@/composables/useObjectModel'; -type CalculatorOptions = StandardOptions | RaceOptions | WorkoutOptions; +type CalculatorOptions = PaceOptions | RaceOptions | SplitOptions | WorkoutOptions; const props = defineProps<{ /* - * The batch calculator input (if applicable, used to generate custom batch label placeholder) - */ - batchInput?: DistanceTime, - - /* * The batch calculator options (if applicable) */ batchOptions?: BatchOptions, diff --git a/src/core/calculators.ts b/src/core/calculators.ts @@ -39,16 +39,20 @@ export interface GlobalOptions { defaultUnitSystem: UnitSystems, racePredictionOptions: RacePredictionOptions, }; -export interface StandardOptions { +export interface SplitOptions { selectedTargetSet: string, }; -export type RaceOptions = StandardOptions; -export interface WorkoutOptions extends RaceOptions { +export interface PaceOptions extends SplitOptions { + input: DistanceTime, +}; +export type RaceOptions = PaceOptions; +export interface WorkoutOptions extends PaceOptions { customTargetNames: boolean, }; export interface BatchOptions { calculator: Calculators.Pace | Calculators.Race | Calculators.Workout, increment: number, + input: DistanceTime, label: string, rows: number, }; @@ -87,20 +91,24 @@ export const defaultInput: DistanceTime = { export const defaultBatchOptions: BatchOptions = { calculator: Calculators.Workout, increment: 15, + input: defaultInput, label: '', rows: 20, }; -export const defaultPaceOptions: StandardOptions = { +export const defaultPaceOptions: PaceOptions = { + input: defaultInput, selectedTargetSet: '_pace_targets', }; export const defaultRaceOptions: RaceOptions = { + input: defaultInput, selectedTargetSet: '_race_targets', }; -export const defaultSplitOptions: StandardOptions = { +export const defaultSplitOptions: SplitOptions = { selectedTargetSet: '_split_targets', }; export const defaultWorkoutOptions: WorkoutOptions = { customTargetNames: false, + input: defaultInput, selectedTargetSet: '_workout_targets', }; diff --git a/src/views/BatchCalculator.vue b/src/views/BatchCalculator.vue @@ -2,20 +2,20 @@ <div class="calculator"> <h2>Batch Input</h2> <div class="input"> - <pace-input v-model="input" aria-label="Input"/> + <pace-input v-model="batchOptions.input" aria-label="Input"/> </div> <h2>Batch Options</h2> <div class="input"> <div> Increment: - <time-input v-model="options.increment" label="Duration increment" :show-hours="false"/> + <time-input v-model="batchOptions.increment" label="Duration increment" :show-hours="false"/> &times; - <integer-input v-model="options.rows" min="1" aria-label="Number of rows"/> + <integer-input v-model="batchOptions.rows" min="1" aria-label="Number of rows"/> </div> <div> Calculator: - <select aria-label="Calculator" v-model="options.calculator"> + <select aria-label="Calculator" v-model="batchOptions.calculator"> <option :value="calculators.Calculators.Pace">Pace Calculator</option> <option :value="calculators.Calculators.Race">Race Calculator</option> <option :value="calculators.Calculators.Workout">Workout Calculator</option> @@ -27,9 +27,9 @@ <summary> <h2>Advanced Options</h2> </summary> - <advanced-options-input :batch-input="input" v-model:batch-options="options" + <advanced-options-input v-model:batch-options="batchOptions" v-model:globalOptions="globalOptions" v-model:options="calcOptions" - v-model:targetSets="targetSets" :type="options.calculator"/> + v-model:targetSets="targetSets" :type="batchOptions.calculator"/> </details> <h2>Batch Results</h2> @@ -44,7 +44,7 @@ import { computed } from 'vue'; import * as calculators from '@/core/calculators'; -import type { BatchOptions, GlobalOptions, RaceOptions, StandardOptions, TargetResult, +import type { BatchOptions, GlobalOptions, PaceOptions, RaceOptions, TargetResult, WorkoutOptions } from '@/core/calculators'; import * as targetUtils from '@/core/targets'; import { formatDistance } from '@/core/units'; @@ -59,20 +59,25 @@ import TimeInput from '@/components/TimeInput.vue'; import useStorage from '@/composables/useStorage'; /* - * The input pace + * The global options */ -const input = useStorage<DistanceTime>('batch-calculator-input', calculators.defaultInput); +const globalOptions = useStorage<GlobalOptions>('global-options', calculators.defaultGlobalOptions); /* - * The batch input options + * The batch calculator options */ -const options = useStorage<BatchOptions>('batch-calculator-options', +const batchOptions = useStorage<BatchOptions>('batch-calculator-options', calculators.defaultBatchOptions); /* - * The global options + * The options for each calculator */ -const globalOptions = useStorage<GlobalOptions>('global-options', calculators.defaultGlobalOptions); +const paceOptions = useStorage<PaceOptions>('pace-calculator-options', + calculators.defaultPaceOptions); +const raceOptions = useStorage<RaceOptions>('race-calculator-options', + calculators.defaultRaceOptions); +const workoutOptions = useStorage<WorkoutOptions>('workout-calculator-options', + calculators.defaultWorkoutOptions); /* * The target sets for each calculator @@ -85,21 +90,11 @@ const workoutTargetSets = useStorage<targetUtils.WorkoutTargetSets>( 'workout-calculator-target-sets', targetUtils.defaultWorkoutTargetSets); /* - * The options for each calculator - */ -const paceOptions = useStorage<StandardOptions>('pace-calculator-options', - calculators.defaultPaceOptions); -const raceOptions = useStorage<RaceOptions>('race-calculator-options', - calculators.defaultRaceOptions); -const workoutOptions = useStorage<WorkoutOptions>('workout-calculator-options', - calculators.defaultWorkoutOptions); - -/* * The input distance */ const inputDistance = computed<Distance>(() => ({ - distanceValue: input.value.distanceValue, - distanceUnit: input.value.distanceUnit, + distanceValue: batchOptions.value.input.distanceValue, + distanceUnit: batchOptions.value.input.distanceUnit, })); /* @@ -107,8 +102,8 @@ const inputDistance = computed<Distance>(() => ({ */ const inputTimes = computed<Array<number>>(() => { const results = []; - for (let i = 0; i < options.value.rows; i++) { - results.push(input.value.time + options.value.increment * i); + for (let i = 0; i < batchOptions.value.rows; i++) { + results.push(batchOptions.value.input.time + batchOptions.value.increment * i); } return results; }); @@ -118,7 +113,7 @@ const inputTimes = computed<Array<number>>(() => { */ const targetSets = computed<targetUtils.TargetSets>({ get: () => { - switch (options.value.calculator) { + switch (batchOptions.value.calculator) { case (calculators.Calculators.Pace): { return paceTargetSets.value; } @@ -132,7 +127,7 @@ const targetSets = computed<targetUtils.TargetSets>({ } }, set: (newValue: targetUtils.TargetSets) => { - switch (options.value.calculator) { + switch (batchOptions.value.calculator) { case (calculators.Calculators.Pace): { paceTargetSets.value = newValue as targetUtils.StandardTargetSets; break; @@ -153,9 +148,9 @@ const targetSets = computed<targetUtils.TargetSets>({ /* * The options for the current calculator */ -const calcOptions = computed<StandardOptions | RaceOptions | WorkoutOptions>({ +const calcOptions = computed<PaceOptions | RaceOptions | WorkoutOptions>({ get: () => { - switch (options.value.calculator) { + switch (batchOptions.value.calculator) { case (calculators.Calculators.Pace): { return paceOptions.value; } @@ -168,10 +163,10 @@ const calcOptions = computed<StandardOptions | RaceOptions | WorkoutOptions>({ } } }, - set: (newValue: StandardOptions | RaceOptions | WorkoutOptions) => { - switch(options.value.calculator) { + set: (newValue: PaceOptions | RaceOptions | WorkoutOptions) => { + switch(batchOptions.value.calculator) { case (calculators.Calculators.Pace): { - paceOptions.value = newValue as StandardOptions; + paceOptions.value = newValue as PaceOptions; break; } case (calculators.Calculators.Race): { @@ -191,7 +186,7 @@ const calcOptions = computed<StandardOptions | RaceOptions | WorkoutOptions>({ * The appropriate calculate_results function for the current calculator */ const calculateResult = computed<(x: DistanceTime, y: targetUtils.Target) => TargetResult>(() => { - switch(options.value.calculator) { + switch(batchOptions.value.calculator) { case (calculators.Calculators.Pace): { return (x,y) => calculators.calculatePaceResults(x, y, globalOptions.value.defaultUnitSystem, false); @@ -212,11 +207,11 @@ const calculateResult = computed<(x: DistanceTime, y: targetUtils.Target) => Tar * The label to render for the batch column */ const batchColumnLabel = computed<string>(() => { - if (options.value.calculator == calculators.Calculators.Workout && - (calcOptions.value as WorkoutOptions).customTargetNames && options.value.label) { - return options.value.label; + if (batchOptions.value.calculator == calculators.Calculators.Workout && + (calcOptions.value as WorkoutOptions).customTargetNames && batchOptions.value.label) { + return batchOptions.value.label; } else { - return formatDistance(input.value, false); + return formatDistance(batchOptions.value.input, false); } }); </script> diff --git a/src/views/PaceCalculator.vue b/src/views/PaceCalculator.vue @@ -2,7 +2,7 @@ <div class="calculator"> <h2>Input Pace</h2> <div class="input"> - <pace-input v-model="input"/> + <pace-input v-model="paceOptions.input"/> </div> <details> @@ -10,24 +10,23 @@ <h2>Advanced Options</h2> </summary> <advanced-options-input v-model:globalOptions="globalOptions" - v-model:options="options" v-model:targetSets="targetSets" :type="Calculators.Pace"/> + v-model:options="paceOptions" v-model:targetSets="targetSets" :type="Calculators.Pace"/> </details> <h2>Equivalent Paces</h2> <single-output-table class="output" :calculate-result="x => - calculatePaceResults(input, x, globalOptions.defaultUnitSystem, true)" - :targets="targetSets[options.selectedTargetSet] ? - targetSets[options.selectedTargetSet].targets : []"/> + calculatePaceResults(paceOptions.input, x, globalOptions.defaultUnitSystem, true)" + :targets="targetSets[paceOptions.selectedTargetSet] ? + targetSets[paceOptions.selectedTargetSet].targets : []"/> </div> </template> <script setup lang="ts"> -import { Calculators, calculatePaceResults, defaultGlobalOptions, defaultInput, +import { Calculators, calculatePaceResults, defaultGlobalOptions, defaultPaceOptions } from '@/core/calculators'; -import type { GlobalOptions, StandardOptions } from '@/core/calculators'; +import type { GlobalOptions, PaceOptions } from '@/core/calculators'; import { defaultPaceTargetSets } from '@/core/targets'; import type { StandardTargetSets } from '@/core/targets'; -import type { DistanceTime } from '@/core/units'; import AdvancedOptionsInput from '@/components/AdvancedOptionsInput.vue'; import PaceInput from '@/components/PaceInput.vue'; @@ -36,19 +35,14 @@ import SingleOutputTable from '@/components/SingleOutputTable.vue'; import useStorage from '@/composables/useStorage'; /* - * The input pace - */ -const input = useStorage<DistanceTime>('pace-calculator-input', defaultInput); - -/* * The global options */ const globalOptions = useStorage<GlobalOptions>('global-options', defaultGlobalOptions); /* - * The current selected target set + * The pace calculator options */ -const options = useStorage<StandardOptions>('pace-calculator-options', defaultPaceOptions); +const paceOptions = useStorage<PaceOptions>('pace-calculator-options', defaultPaceOptions); /* * The target sets diff --git a/src/views/RaceCalculator.vue b/src/views/RaceCalculator.vue @@ -2,7 +2,7 @@ <div class="calculator"> <h2>Input Race Result</h2> <div class="input"> - <pace-input v-model="input" label="Input race"/> + <pace-input v-model="raceOptions.input" label="Input race"/> </div> <details> @@ -27,28 +27,27 @@ <h2>Advanced Options</h2> </summary> <advanced-options-input v-model:globalOptions="globalOptions" - v-model:options="options" v-model:targetSets="targetSets" :type="Calculators.Race"/> + v-model:options="raceOptions" v-model:targetSets="targetSets" :type="Calculators.Race"/> </details> <h2>Equivalent Race Results</h2> - <single-output-table class="output" show-pace - :calculate-result="x => calculateRaceResults(input, x, globalOptions.racePredictionOptions, - globalOptions.defaultUnitSystem, true)" - :targets="targetSets[options.selectedTargetSet] ? - targetSets[options.selectedTargetSet].targets : []"/> + <single-output-table class="output" show-pace :calculate-result="x => + calculateRaceResults(raceOptions.input, x, globalOptions.racePredictionOptions, + globalOptions.defaultUnitSystem, true)" + :targets="targetSets[raceOptions.selectedTargetSet] ? + targetSets[raceOptions.selectedTargetSet].targets : []"/> </div> </template> <script setup lang="ts"> import { computed } from 'vue'; -import { Calculators, calculateRaceResults, calculateRaceStats, defaultGlobalOptions, defaultInput, +import { Calculators, calculateRaceResults, calculateRaceStats, defaultGlobalOptions, defaultRaceOptions } from '@/core/calculators'; import type { GlobalOptions, RaceOptions, RaceStats } from '@/core/calculators'; import { defaultRaceTargetSets } from '@/core/targets'; import type { StandardTargetSets } from '@/core/targets'; import { formatNumber } from '@/core/units'; -import type { DistanceTime } from '@/core/units'; import AdvancedOptionsInput from '@/components/AdvancedOptionsInput.vue'; import PaceInput from '@/components/PaceInput.vue'; @@ -57,11 +56,6 @@ import SingleOutputTable from '@/components/SingleOutputTable.vue'; import useStorage from '@/composables/useStorage'; /* - * The input race - */ -const input = useStorage<DistanceTime>('race-calculator-input', defaultInput); - -/* * The global options */ const globalOptions = useStorage<GlobalOptions>('global-options', defaultGlobalOptions); @@ -69,7 +63,7 @@ const globalOptions = useStorage<GlobalOptions>('global-options', defaultGlobalO /* * The race calculator options */ -const options = useStorage<RaceOptions>('race-calculator-options', defaultRaceOptions); +const raceOptions = useStorage<RaceOptions>('race-calculator-options', defaultRaceOptions); /* * The race calculator target sets @@ -80,7 +74,7 @@ const targetSets = useStorage<StandardTargetSets>('race-calculator-target-sets', /* * The statistics for the current input race */ -const raceStats = computed<RaceStats>(() => calculateRaceStats(input.value)); +const raceStats = computed<RaceStats>(() => calculateRaceStats(raceOptions.value.input)); </script> <style scoped> diff --git a/src/views/SplitCalculator.vue b/src/views/SplitCalculator.vue @@ -2,7 +2,7 @@ <div class="calculator"> <div class="input"> <advanced-options-input v-model:globalOptions="globalOptions" - v-model:options="options" v-model:targetSets="targetSets" :type="Calculators.Split"/> + v-model:options="splitOptions" v-model:targetSets="targetSets" :type="Calculators.Split"/> </div> <div class="output"> @@ -16,7 +16,7 @@ import { computed } from 'vue'; import { Calculators, defaultGlobalOptions, defaultSplitOptions } from '@/core/calculators'; -import type { GlobalOptions, StandardOptions } from '@/core/calculators'; +import type { GlobalOptions, SplitOptions } from '@/core/calculators'; import { defaultSplitTargetSets } from '@/core/targets'; import type { SplitTargetSets } from '@/core/targets'; @@ -33,7 +33,7 @@ const globalOptions = useStorage<GlobalOptions>('global-options', defaultGlobalO /* * The split calculator options */ -const options = useStorage<StandardOptions>('split-calculator-options', defaultSplitOptions); +const splitOptions = useStorage<SplitOptions>('split-calculator-options', defaultSplitOptions); /* * The split calculator target sets @@ -46,15 +46,15 @@ const targetSets = useStorage<SplitTargetSets>('split-calculator-target-sets', */ const targetSet = computed({ get: () => { - if (targetSets.value[options.value.selectedTargetSet]) { - return targetSets.value[options.value.selectedTargetSet].targets + if (targetSets.value[splitOptions.value.selectedTargetSet]) { + return targetSets.value[splitOptions.value.selectedTargetSet].targets } else { return [] } }, set: (newValue) => { - if (targetSets.value[options.value.selectedTargetSet]) { - targetSets.value[options.value.selectedTargetSet].targets = newValue; + if (targetSets.value[splitOptions.value.selectedTargetSet]) { + targetSets.value[splitOptions.value.selectedTargetSet].targets = newValue; } }, }); diff --git a/src/views/WorkoutCalculator.vue b/src/views/WorkoutCalculator.vue @@ -2,7 +2,7 @@ <div class="calculator"> <h2>Input Race Result</h2> <div class="input"> - <pace-input v-model="input" label="Input race"/> + <pace-input v-model="workoutOptions.input" label="Input race"/> </div> <details> @@ -10,25 +10,24 @@ <h2>Advanced Options</h2> </summary> <advanced-options-input v-model:globalOptions="globalOptions" - v-model:options="options" v-model:targetSets="targetSets" :type="Calculators.Workout"/> + v-model:options="workoutOptions" v-model:targetSets="targetSets" :type="Calculators.Workout"/> </details> <h2>Workout Splits</h2> - <single-output-table class="output" - :calculate-result="x => calculateWorkoutResults(input, x as WorkoutTarget, - globalOptions.racePredictionOptions, options.customTargetNames, true)" - :targets="targetSets[options.selectedTargetSet] ? - targetSets[options.selectedTargetSet].targets : []"/> + <single-output-table class="output" :calculate-result="x => + calculateWorkoutResults(workoutOptions.input, x as WorkoutTarget, + globalOptions.racePredictionOptions, workoutOptions.customTargetNames, true)" + :targets="targetSets[workoutOptions.selectedTargetSet] ? + targetSets[workoutOptions.selectedTargetSet].targets : []"/> </div> </template> <script setup lang="ts"> -import { Calculators, calculateWorkoutResults, defaultGlobalOptions, defaultInput, +import { Calculators, calculateWorkoutResults, defaultGlobalOptions, defaultWorkoutOptions } from '@/core/calculators'; import type { GlobalOptions, WorkoutOptions } from '@/core/calculators'; import { defaultWorkoutTargetSets } from '@/core/targets'; import type { WorkoutTarget, WorkoutTargetSets } from '@/core/targets'; -import type { DistanceTime } from '@/core/units'; import AdvancedOptionsInput from '@/components/AdvancedOptionsInput.vue'; import PaceInput from '@/components/PaceInput.vue'; @@ -37,11 +36,6 @@ import SingleOutputTable from '@/components/SingleOutputTable.vue'; import useStorage from '@/composables/useStorage'; /* - * The input race - */ -const input = useStorage<DistanceTime>('workout-calculator-input', defaultInput); - -/* * The global options */ const globalOptions = useStorage<GlobalOptions>('global-options', defaultGlobalOptions); @@ -49,7 +43,7 @@ const globalOptions = useStorage<GlobalOptions>('global-options', defaultGlobalO /* * The race prediction options */ -const options = useStorage<WorkoutOptions>('workout-calculator-options', defaultWorkoutOptions); +const workoutOptions = useStorage<WorkoutOptions>('workout-calculator-options', defaultWorkoutOptions); /* * The target sets diff --git a/tests/e2e/cross-calculator.spec.js b/tests/e2e/cross-calculator.spec.js @@ -219,7 +219,7 @@ test('Cross-calculator', async ({ page }) => { // Assert localStorage entries are correct { // Assert global localStorage entries are correct - expect(await page.evaluate(() => localStorage.length)).toEqual(16); + expect(await page.evaluate(() => localStorage.length)).toEqual(12); expect(await page.evaluate(() => localStorage.getItem('running-tools.global-options'))) .toEqual(JSON.stringify({ defaultUnitSystem: 'metric', @@ -231,30 +231,32 @@ test('Cross-calculator', async ({ page }) => { // Assert localStorage entries for the batch calculator are correct expect(await page.evaluate(() => - localStorage.getItem('running-tools.batch-calculator-input'))).toEqual(JSON.stringify({ - distanceValue: 2, - distanceUnit: 'miles', - time: 630, - })); - expect(await page.evaluate(() => localStorage.getItem('running-tools.batch-calculator-options'))).toEqual(JSON.stringify({ calculator: 'race', increment: 10, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 630, + }, label: '', rows: 15, })); // Assert localStorage entries for the pace calculator are correct - expect(await page.evaluate(() => - localStorage.getItem('running-tools.pace-calculator-input'))).toEqual(JSON.stringify({ - distanceValue: 2, - distanceUnit: 'miles', - time: 930, - })); const paceCalculatorKey = parseInt(JSON.parse(await page.evaluate(() => 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-options'))).toEqual(JSON.stringify({ + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 930, + }, + selectedTargetSet: paceCalculatorKey.toString(), + })); + expect(await page.evaluate(() => localStorage.getItem('running-tools.pace-calculator-target-sets'))).toEqual(JSON.stringify({ _pace_targets: { name: 'Common Pace Targets', @@ -303,13 +305,12 @@ test('Cross-calculator', async ({ page }) => { // Assert localStorage entries for the race calculator are correct expect(await page.evaluate(() => - localStorage.getItem('running-tools.race-calculator-input'))).toEqual(JSON.stringify({ - distanceValue: 2, - distanceUnit: 'miles', - time: 630, - })); - expect(await page.evaluate(() => localStorage.getItem('running-tools.race-calculator-options'))).toEqual(JSON.stringify({ + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 630, + }, selectedTargetSet: '_race_targets', })); expect(await page.evaluate(() => @@ -379,14 +380,13 @@ test('Cross-calculator', async ({ page }) => { // Assert localStorage entries for the workout calculator are correct expect(await page.evaluate(() => - localStorage.getItem('running-tools.workout-calculator-input'))).toEqual(JSON.stringify({ - distanceValue: 1, - distanceUnit: 'miles', - time: 301, - })); - expect(await page.evaluate(() => localStorage.getItem('running-tools.workout-calculator-options'))).toEqual(JSON.stringify({ customTargetNames: true, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 301, + }, selectedTargetSet: '_workout_targets', })); expect(await page.evaluate(() => diff --git a/tests/unit/components/AdvancedOptionsInput.spec.js b/tests/unit/components/AdvancedOptionsInput.spec.js @@ -14,6 +14,11 @@ test('should be correctly render pace options according to props', () => { }, }, options: { + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: 'B', }, targetSets: { @@ -80,6 +85,11 @@ test('should be correctly render race options according to props', () => { }, }, options: { + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_new', }, type: 'race', @@ -114,6 +124,11 @@ test('should render riegel exponent field only for supported race prediction mod }, }, options: { + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_new', }, type: 'race', @@ -187,6 +202,11 @@ test('should be correctly render workout options according to props', () => { }, options: { customTargetNames: true, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_new', }, targetSets: {}, @@ -220,9 +240,10 @@ test('should only show batch column label field when applicable', async () => { }, options: { customTargetNames: true, - predictionOptions: { - model: 'PurdyPointsModel', - riegelExponent: 1.2, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, }, selectedTargetSet: '_new', }, @@ -236,16 +257,16 @@ test('should only show batch column label field when applicable', async () => { expect(wrapper.findAll('input[aria-label="Batch column label"]')).to.have .length(0); - // Add batchInput and batchOptions but disable workout target name customization + // Add batchOptions but disable workout target name customization await wrapper.setProps({ - batchInput: { // added - distanceValue: 2, - distanceUnit: 'miles', - time: 600, - }, batchOptions: { // added calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, }, @@ -258,9 +279,10 @@ test('should only show batch column label field when applicable', async () => { }, options: { customTargetNames: false, // disabled - predictionOptions: { - model: 'PurdyPointsModel', - riegelExponent: 1.2, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, }, selectedTargetSet: '_new', }, @@ -273,14 +295,14 @@ test('should only show batch column label field when applicable', async () => { // Enable workout target name customization await wrapper.setProps({ - batchInput: { - distanceValue: 2, - distanceUnit: 'miles', - time: 600, - }, batchOptions: { calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, }, @@ -293,9 +315,10 @@ test('should only show batch column label field when applicable', async () => { }, options: { customTargetNames: true, // enabled - predictionOptions: { - model: 'PurdyPointsModel', - riegelExponent: 1.2, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, }, selectedTargetSet: '_new', }, @@ -310,14 +333,14 @@ test('should only show batch column label field when applicable', async () => { // Switch to race calculator await wrapper.setProps({ - batchInput: { - distanceValue: 2, - distanceUnit: 'miles', - time: 600, - }, batchOptions: { calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, }, @@ -329,9 +352,10 @@ test('should only show batch column label field when applicable', async () => { }, }, options: { - predictionOptions: { - model: 'PurdyPointsModel', - riegelExponent: 1.2, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, }, selectedTargetSet: '_new', }, @@ -357,9 +381,10 @@ test('should pass correct props to TargetSetSelector', async () => { }, options: { customTargetNames: false, - predictionOptions: { - model: 'AverageModel', - riegelExponent: 1.06, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, }, selectedTargetSet: 'B', }, @@ -429,6 +454,11 @@ test('should emit input events when options are modified', async () => { }, options: { customTargetNames: false, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_new', }, targetSets: {}, @@ -506,10 +536,20 @@ test('should emit input events when options are modified', async () => { expect(wrapper.emitted()['update:options']).to.deep.equal([ [{ customTargetNames: false, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: 'B', }], [{ customTargetNames: true, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: 'B', }], ]); diff --git a/tests/unit/views/BatchCalculator.spec.js b/tests/unit/views/BatchCalculator.spec.js @@ -29,35 +29,16 @@ test('should load global options from localStorage', async () => { }); }); -test('should load input from localStorage', async () => { - // Initialize localStorage - localStorage.setItem('running-tools.batch-calculator-input', JSON.stringify({ - distanceValue: 2, - distanceUnit: 'miles', - time: 600, - })); - - // Initialize component - const wrapper = shallowMount(BatchCalculator); - - // Assert options loaded - expect(wrapper.findComponent({ name: 'pace-input' }).vm.modelValue).to.deep.equal({ - distanceValue: 2, - distanceUnit: 'miles', - time: 600, - }); - expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.batchInput).to.deep.equal({ - distanceValue: 2, - distanceUnit: 'miles', - time: 600, - }); -}); - test('should load batch options from localStorage', async () => { // Initialize localStorage localStorage.setItem('running-tools.batch-calculator-options', JSON.stringify({ calculator: 'race', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, })); @@ -67,10 +48,20 @@ test('should load batch options from localStorage', async () => { // Assert options loaded expect(wrapper.find('select[aria-label="Calculator"]').element.value).to.equal('race'); + expect(wrapper.findComponent({ name: 'pace-input' }).vm.modelValue).to.deep.equal({ + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }); expect(wrapper.findComponent({ name: 'time-input' }).vm.modelValue).to.equal(32); expect(wrapper.findComponent({ name: 'integer-input' }).vm.modelValue).to.equal(15); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.batchOptions).to.deep.equal({ calculator: 'race', + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, increment: 32, label: 'foo', rows: 15, @@ -130,13 +121,28 @@ test('should load calculator options from localStorage', async () => { } })); localStorage.setItem('running-tools.pace-calculator-options', JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 300, + }, selectedTargetSet: 'A', })); localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({ + input: { + distanceValue: 1.1, + distanceUnit: 'miles', + time: 310, + }, selectedTargetSet: 'C', })); localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({ customTargetNames: true, + input: { + distanceValue: 1.2, + distanceUnit: 'miles', + time: 320, + }, selectedTargetSet: 'E', })); @@ -146,6 +152,11 @@ test('should load calculator options from localStorage', async () => { // Assert pace calculator options are loaded await wrapper.find('select[aria-label="Calculator"]').setValue('pace'); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ + input: { + distanceValue: 1.0, + distanceUnit: 'miles', + time: 300, + }, selectedTargetSet: 'A', }); expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets) @@ -154,6 +165,11 @@ test('should load calculator options from localStorage', async () => { // Assert race calculator options are loaded await wrapper.find('select[aria-label="Calculator"]').setValue('race'); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ + input: { + distanceValue: 1.1, + distanceUnit: 'miles', + time: 310, + }, selectedTargetSet: 'C', }); expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets) @@ -163,6 +179,11 @@ test('should load calculator options from localStorage', async () => { await wrapper.find('select[aria-label="Calculator"]').setValue('workout'); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ customTargetNames: true, + input: { + distanceValue: 1.2, + distanceUnit: 'miles', + time: 320, + }, selectedTargetSet: 'E', }); expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets) @@ -195,7 +216,7 @@ test('should save global options to localStorage when modified', async () => { })); }); -test('should save input to localStorage when modified', async () => { +test('should save batch options to localStorage when modified', async () => { // Initialize component const wrapper = shallowMount(BatchCalculator); @@ -206,17 +227,18 @@ test('should save input to localStorage when modified', async () => { time: 600, }); - // Assert input saved - expect(localStorage.getItem('running-tools.batch-calculator-input')).to.equal(JSON.stringify({ - distanceValue: 2, - distanceUnit: 'miles', - time: 600, + // Assert options saved + expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({ + calculator: 'workout', + increment: 15, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, + label: '', + rows: 20, })); -}); - -test('should save batch options to localStorage when modified', async () => { - // Initialize component - const wrapper = shallowMount(BatchCalculator); // Update increment value await wrapper.findComponent({ name: 'time-input' }).setValue(32); @@ -225,6 +247,11 @@ test('should save batch options to localStorage when modified', async () => { expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({ calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: '', rows: 20, })); @@ -236,6 +263,11 @@ test('should save batch options to localStorage when modified', async () => { expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({ calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: '', rows: 15, })); @@ -244,6 +276,11 @@ test('should save batch options to localStorage when modified', async () => { await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, }, 'batch-options'); @@ -252,6 +289,11 @@ test('should save batch options to localStorage when modified', async () => { expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({ calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, })); @@ -263,6 +305,11 @@ test('should save batch options to localStorage when modified', async () => { expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({ calculator: 'race', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, })); @@ -303,6 +350,11 @@ test('should save calculator options to localStorage when modified', async () => } })); localStorage.setItem('running-tools.pace-calculator-options', JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 300, + }, selectedTargetSet: 'B', })); localStorage.setItem('running-tools.race-calculator-target-sets', JSON.stringify({ @@ -315,6 +367,11 @@ test('should save calculator options to localStorage when modified', async () => } })); localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({ + input: { + distanceValue: 1.1, + distanceUnit: 'miles', + time: 310, + }, selectedTargetSet: 'D', })); localStorage.setItem('running-tools.workout-calculator-target-sets', JSON.stringify({ @@ -328,6 +385,11 @@ test('should save calculator options to localStorage when modified', async () => })); localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({ customWorkoutNames: false, + input: { + distanceValue: 1.2, + distanceUnit: 'miles', + time: 320, + }, selectedTargetSet: 'F', })); @@ -337,6 +399,11 @@ test('should save calculator options to localStorage when modified', async () => // Update pace calculator options X await wrapper.find('select[aria-label="Calculator"]').setValue('pace'); await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 1.0, + distanceUnit: 'miles', + time: 300, + }, selectedTargetSet: 'A', }, 'options'); expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets) @@ -345,6 +412,11 @@ test('should save calculator options to localStorage when modified', async () => // Update race calculator options await wrapper.find('select[aria-label="Calculator"]').setValue('race'); await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 1.1, + distanceUnit: 'miles', + time: 310, + }, selectedTargetSet: 'C', }, 'options'); expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets) @@ -354,6 +426,11 @@ test('should save calculator options to localStorage when modified', async () => await wrapper.find('select[aria-label="Calculator"]').setValue('workout'); await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ customTargetNames: true, + input: { + distanceValue: 1.2, + distanceUnit: 'miles', + time: 320, + }, selectedTargetSet: 'E', }, 'options'); expect(wrapper.findComponent({ name: 'double-output-table' }).vm.targets) @@ -361,13 +438,28 @@ test('should save calculator options to localStorage when modified', async () => // Assert options saved to localStorage expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(JSON.stringify({ + input: { + distanceValue: 1.0, + distanceUnit: 'miles', + time: 300, + }, selectedTargetSet: 'A', })); expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(JSON.stringify({ + input: { + distanceValue: 1.1, + distanceUnit: 'miles', + time: 310, + }, selectedTargetSet: 'C', })); expect(localStorage.getItem('running-tools.workout-calculator-options')).to.equal(JSON.stringify({ customTargetNames: true, + input: { + distanceValue: 1.2, + distanceUnit: 'miles', + time: 320, + }, selectedTargetSet: 'E', })); }); @@ -436,6 +528,11 @@ test('should pass correct input props to DoubleOutputTable', async () => { await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ calculator: 'workout', increment: 10, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: 'foo', rows: 15, }, 'batchOptions'); @@ -453,6 +550,11 @@ test('should pass correct input props to DoubleOutputTable', async () => { // Enable target name customization await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ customTargetNames: true, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_workout_targets', }, 'options'); @@ -494,14 +596,14 @@ test('should correctly set AdvancedOptionsInput props', async () => { await wrapper.findComponent({ name: 'integer-input' }).setValue(15); // Assert batch props are correct - expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.batchInput).to.deep.equal({ - distanceValue: 2, - distanceUnit: 'miles', - time: 600, - }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.batchOptions).to.deep.equal({ calculator: 'workout', increment: 32, + input: { + distanceValue: 2, + distanceUnit: 'miles', + time: 600, + }, label: '', rows: 15, }); @@ -517,6 +619,11 @@ test('should correctly set AdvancedOptionsInput props', async () => { }, }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_pace_targets', }); @@ -531,6 +638,11 @@ test('should correctly set AdvancedOptionsInput props', async () => { }, }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_race_targets', }); @@ -546,6 +658,11 @@ test('should correctly set AdvancedOptionsInput props', async () => { }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ customTargetNames: false, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_workout_targets', }); }); @@ -560,10 +677,20 @@ test('should correctly calculate outputs', async () => { }, })); localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({ + input: { + distanceValue: 1.1, + distanceUnit: 'miles', + time: 310, + }, selectedTargetSet: '_race_targets', })); localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({ customTargetNames: false, + input: { + distanceValue: 1.2, + distanceUnit: 'miles', + time: 320, + }, selectedTargetSet: '_workout_targets', })); diff --git a/tests/unit/views/PaceCalculator.spec.js b/tests/unit/views/PaceCalculator.spec.js @@ -25,26 +25,7 @@ test('should load global options from localStorage', async () => { .defaultUnitSystem).to.equal('imperial'); }); -test('should load input pace from localStorage', async () => { - // Initialize localStorage - localStorage.setItem('running-tools.pace-calculator-input', JSON.stringify({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - })); - - // Initialize component - const wrapper = shallowMount(PaceCalculator); - - // Assert data loaded - expect(wrapper.findComponent({ name: 'pace-input' }).vm.modelValue).to.deep.equal({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - }); -}); - -test('should load local options and target sets from localStorage', async () => { +test('should load pace options and target sets from localStorage', async () => { // Initialize localStorage const targetSets = { '_pace_targets': { @@ -66,6 +47,11 @@ test('should load local options and target sets from localStorage', async () => }; localStorage.setItem('running-tools.pace-calculator-target-sets', JSON.stringify(targetSets)); localStorage.setItem('running-tools.pace-calculator-options', JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', })); @@ -73,7 +59,17 @@ test('should load local options and target sets from localStorage', async () => const wrapper = shallowMount(PaceCalculator); // Assert selection is loaded + expect(wrapper.findComponent({ name: 'pace-input' }).vm.modelValue).to.deep.equal({ + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.targetSets) @@ -105,26 +101,7 @@ test('should save global options to localStorage when modified', async () => { })); }); -test('should save input pace to localStorage', async () => { - // Initialize component - const wrapper = shallowMount(PaceCalculator); - - // Enter input pace data - await wrapper.findComponent({ name: 'pace-input' }).setValue({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - }); - - // Assert data saved to localStorage - expect(localStorage.getItem('running-tools.pace-calculator-input')).to.equal(JSON.stringify({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - })); -}); - -test('should save local options and target sets to localStorage when modified', async () => { +test('should save pace options and target sets to localStorage when modified', async () => { const targetSets = { '_pace_targets': { name: 'Pace targets #1', @@ -147,10 +124,32 @@ test('should save local options and target sets to localStorage when modified', // Initialize component const wrapper = shallowMount(PaceCalculator); + // Update input pace + await wrapper.findComponent({ name: 'pace-input' }).setValue({ + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }); + + // New input pace should be saved to localStorage + expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, + selectedTargetSet: '_pace_targets', + })); + // Update target sets and selected target set await wrapper.findComponent({ name: 'advanced-options-input' }).setValue(targetSets, 'targetSets'); await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', }, 'options'); @@ -158,6 +157,11 @@ test('should save local options and target sets to localStorage when modified', expect(localStorage.getItem('running-tools.pace-calculator-target-sets')) .to.equal(JSON.stringify(targetSets)); expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', })); }); @@ -245,15 +249,27 @@ test('should correctly handle null target set', async () => { const wrapper = shallowMount(PaceCalculator); // Switch to invalid target set - await wrapper.findComponent({ name: 'advanced-options-input' }) - .setValue({ selectedTargetSet: 'does_not_exist' }, 'options'); + await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, + selectedTargetSet: 'does_not_exist' + }, 'options'); // Assert empty array passed to SingleOutputTable component expect(wrapper.findComponent({ name: 'single-output-table' }).vm.targets).to.deep.equal([]); // Switch to valid target set - await wrapper.findComponent({ name: 'advanced-options-input' }) - .setValue({ selectedTargetSet: '_pace_targets' }, 'options'); + await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, + selectedTargetSet: '_pace_targets' + }, 'options'); // Assert valid targets passed to SingleOutputTable component const paceTargets = defaultTargetSets._pace_targets.targets; diff --git a/tests/unit/views/RaceCalculator.spec.js b/tests/unit/views/RaceCalculator.spec.js @@ -30,26 +30,7 @@ test('should load global options from localStorage', async () => { }); }); -test('should load input race from localStorage', async () => { - // Initialize localStorage - localStorage.setItem('running-tools.race-calculator-input', JSON.stringify({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - })); - - // Initialize component - const wrapper = shallowMount(RaceCalculator); - - // Assert data loaded - expect(wrapper.findComponent({ name: 'pace-input' }).vm.modelValue).to.deep.equal({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - }); -}); - -test('should load local options and target sets from localStorage', async () => { +test('should load race options and target sets from localStorage', async () => { // Initialize localStorage const targetSets = { '_race_targets': { @@ -71,6 +52,11 @@ test('should load local options and target sets from localStorage', async () => }; localStorage.setItem('running-tools.race-calculator-target-sets', JSON.stringify(targetSets)); localStorage.setItem('running-tools.race-calculator-options', JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', })); @@ -78,7 +64,17 @@ test('should load local options and target sets from localStorage', async () => const wrapper = shallowMount(RaceCalculator); // Assert data loaded + expect(wrapper.findComponent({ name: 'pace-input' }).vm.modelValue).to.deep.equal({ + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.targetSets) @@ -110,25 +106,6 @@ test('should save global options to localStorage when modified', async () => { })); }); -test('should save input race to localStorage', async () => { - // Initialize component - const wrapper = shallowMount(RaceCalculator); - - // Enter input race data - await wrapper.findComponent({ name: 'pace-input' }).setValue({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - }); - - // Assert data saved to localStorage - expect(localStorage.getItem('running-tools.race-calculator-input')).to.equal(JSON.stringify({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - })); -}); - test('should save local options and target sets to localStorage when modified', async () => { const targetSets = { '_race_targets': { @@ -152,10 +129,32 @@ test('should save local options and target sets to localStorage when modified', // Initialize component const wrapper = shallowMount(RaceCalculator); + // Update input race + await wrapper.findComponent({ name: 'pace-input' }).setValue({ + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }); + + // Assert data saved to localStorage + expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, + selectedTargetSet: '_race_targets', + })); + // Update target sets and selected target set await wrapper.findComponent({ name: 'advanced-options-input' }).setValue(targetSets, 'targetSets'); await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', }, 'options'); @@ -163,6 +162,11 @@ test('should save local options and target sets to localStorage when modified', expect(localStorage.getItem('running-tools.race-calculator-target-sets')) .to.equal(JSON.stringify(targetSets)); expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(JSON.stringify({ + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', })); }); @@ -208,6 +212,11 @@ test('should correctly handle null target set', async () => { // Switch to invalid target set await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: 'does_not_exist', }, 'options'); @@ -216,6 +225,11 @@ test('should correctly handle null target set', async () => { // Switch to valid target set await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_race_targets', }, 'options'); diff --git a/tests/unit/views/SplitCalculator.spec.js b/tests/unit/views/SplitCalculator.spec.js @@ -26,7 +26,7 @@ test('should load global options from localStorage', async () => { .to.equal('imperial'); }); -test('should load local options and target sets from localStorage', async () => { +test('should load split options and target sets from localStorage', async () => { // Initialize localStorage const targetSets = { '_split_targets': { @@ -105,7 +105,7 @@ test('should save global options to localStorage when modified', async () => { })); }); -test('should save local options and target sets to localStorage when modified', async () => { +test('should save split options and target sets to localStorage when modified', async () => { const targetSets1 = { '_split_targets': { name: 'Split targets', diff --git a/tests/unit/views/WorkoutCalculator.spec.js b/tests/unit/views/WorkoutCalculator.spec.js @@ -30,26 +30,7 @@ test('should load global options from localStorage', async () => { }); }); -test('should load input race from localStorage', async () => { - // Initialize localStorage - localStorage.setItem('running-tools.workout-calculator-input', JSON.stringify({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - })); - - // Initialize component - const wrapper = shallowMount(WorkoutCalculator); - - // Assert data loaded - expect(wrapper.findComponent({ name: 'pace-input' }).vm.modelValue).to.deep.equal({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - }); -}); - -test('should load local options and target sets from localStorage', async () => { +test('should load workout options and target sets from localStorage', async () => { // Initialize localStorage const targetSets = { '_workout_targets': { @@ -97,6 +78,11 @@ test('should load local options and target sets from localStorage', async () => localStorage.setItem('running-tools.workout-calculator-target-sets', JSON.stringify(targetSets)); localStorage.setItem('running-tools.workout-calculator-options', JSON.stringify({ customTargetNames: true, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', })); @@ -106,6 +92,11 @@ test('should load local options and target sets from localStorage', async () => // Assert data loaded expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({ customTargetNames: true, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', }); expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.targetSets) @@ -137,26 +128,7 @@ test('should save global options to localStorage when modified', async () => { })); }); -test('should save input race to localStorage', async () => { - // Initialize component - const wrapper = shallowMount(WorkoutCalculator); - - // Enter input race data - await wrapper.findComponent({ name: 'pace-input' }).setValue({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - }); - - // Assert data saved to localStorage - expect(localStorage.getItem('running-tools.workout-calculator-input')).to.equal(JSON.stringify({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - })); -}); - -test('should save local options and target sets to localStorage when modified', async () => { +test('should save workout options and target sets to localStorage when modified', async () => { const targetSets = { '_workout_targets': { name: 'Workout targets #1', @@ -204,11 +176,56 @@ test('should save local options and target sets to localStorage when modified', // Initialize component const wrapper = shallowMount(WorkoutCalculator); - // Update target sets, selected target set, and target name customization + // Update input race + await wrapper.findComponent({ name: 'pace-input' }).setValue({ + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }); + + // Assert data saved to localStorage + expect(localStorage.getItem('running-tools.workout-calculator-options')).to.equal(JSON.stringify({ + customTargetNames: false, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, + selectedTargetSet: '_workout_targets', + })); + + // Update target name customization + await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ + customTargetNames: true, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, + selectedTargetSet: '_workout_targets', + }, 'options'); + + // Assert data saved to localStorage + expect(localStorage.getItem('running-tools.workout-calculator-options')).to.equal(JSON.stringify({ + customTargetNames: true, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, + selectedTargetSet: '_workout_targets', + })); + + // Update target sets and selected target set await wrapper.findComponent({ name: 'advanced-options-input' }).setValue(targetSets, 'targetSets'); await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ customTargetNames: true, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', }, 'options'); @@ -217,6 +234,11 @@ test('should save local options and target sets to localStorage when modified', .to.equal(JSON.stringify(targetSets)); expect(localStorage.getItem('running-tools.workout-calculator-options')).to.equal(JSON.stringify({ customTargetNames: true, + input: { + distanceValue: 1, + distanceUnit: 'miles', + time: 600, + }, selectedTargetSet: 'B', })); }); @@ -253,6 +275,11 @@ test('should correctly handle null target set', async () => { // Switch to invalid target set await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ customTargetNames: false, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: 'does_not_exist', }, 'options'); @@ -262,6 +289,11 @@ test('should correctly handle null target set', async () => { // Switch to valid target set await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ customTargetNames: false, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_workout_targets', }, 'options'); @@ -306,6 +338,11 @@ test('should correctly calculate results according to options', async () => { // Update target name customization await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({ customTargetNames: true, + input: { + distanceValue: 5, + distanceUnit: 'kilometers', + time: 1200, + }, selectedTargetSet: '_workout_targets', }, 'options');