running-tools

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

commit baf7d0840cf02ec30ba13068dc64deb4c28969d9
parent b490ff7f0e90684a1eb94726ed8396527797e16f
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Mon, 27 May 2024 11:08:42 -0700

Implement useStorage composable

Diffstat:
Msrc/components/TargetSetSelector.vue | 3+--
Asrc/composables/useStorage.js | 36++++++++++++++++++++++++++++++++++++
Dsrc/utils/localStorage.js | 40----------------------------------------
Msrc/views/PaceCalculator.vue | 78+++++++++---------------------------------------------------------------------
Msrc/views/RaceCalculator.vue | 96+++++++++----------------------------------------------------------------------
Msrc/views/SplitCalculator.vue | 51++++++---------------------------------------------
Msrc/views/UnitCalculator.vue | 167+++++++++++++++++++++----------------------------------------------------------
Mtests/unit/components/TargetSetSelector.spec.js | 2+-
Mtests/unit/views/PaceCalculator.spec.js | 2--
Mtests/unit/views/RaceCalculator.spec.js | 2--
Mtests/unit/views/SplitCalculator.spec.js | 11-----------
Mtests/unit/views/UnitCalculator.spec.js | 52++++++++++++++++++++++++++++++++++------------------
12 files changed, 142 insertions(+), 398 deletions(-)

diff --git a/src/components/TargetSetSelector.vue b/src/components/TargetSetSelector.vue @@ -21,11 +21,10 @@ </template> <script setup> -import { nextTick, onActivated, ref, watch } from 'vue'; +import { nextTick, ref, watch } from 'vue'; import VueFeather from 'vue-feather'; -import storage from '@/utils/localStorage'; import targetUtils from '@/utils/targets'; import TargetEditor from '@/components/TargetEditor.vue'; diff --git a/src/composables/useStorage.js b/src/composables/useStorage.js @@ -0,0 +1,36 @@ +import { ref, onActivated, watchEffect } from 'vue'; + +// The global localStorage prefix +const prefix = 'running-tools'; + +/* + * Create a reactive value that is synced with a localStorage item + * @param {String} key The localStorage item's key + * @defaultValue {Object} defaultValue The default value + */ +export default function useStorage(key, defaultValue) { + const clonedDefault = JSON.parse(JSON.stringify(defaultValue)); + const value = ref(clonedDefault); + + // (Re)load value from localStorage + function updateValue() { + let parsedValue; + try { + parsedValue = JSON.parse(localStorage.getItem(`${prefix}.${key}`)); + } catch { + parsedValue = null; + } + if (parsedValue !== null) value.value = parsedValue; + } + updateValue(); + onActivated(updateValue); + + // Save value to localStorage when modified + watchEffect(() => { + if (typeof localStorage !== 'undefined') { + localStorage.setItem(`${prefix}.${key}`, JSON.stringify(value.value)); + } + }) + + return value +} diff --git a/src/utils/localStorage.js b/src/utils/localStorage.js @@ -1,40 +0,0 @@ -// The global localStorage prefix -const prefix = 'running-tools'; - -/** - * Get the value of a key from localStorage - * @param {String} key The key - * @param {Object} defaultValue The default value - * @returns {Object} The value - */ -function get(key, defaultValue) { - // Clone defaultValue - const clonedDefault = JSON.parse(JSON.stringify(defaultValue)); - - if (key === null) { - return clonedDefault; - } - let value; - try { - value = JSON.parse(localStorage.getItem(`${prefix}.${key}`)); - } catch { - return clonedDefault; - } - return value === null ? clonedDefault : value; -} - -/** - * Set the value of a key in localStorage - * @param {String} key The key - * @param {Object} value The value - * */ -function set(key, value) { - if (typeof localStorage !== 'undefined') { - localStorage.setItem(`${prefix}.${key}`, JSON.stringify(value)); - } -} - -export default { - get, - set, -}; diff --git a/src/views/PaceCalculator.vue b/src/views/PaceCalculator.vue @@ -43,10 +43,9 @@ </template> <script setup> -import { computed, onActivated, ref, watch } from 'vue'; +import { computed } from 'vue'; import paceUtils from '@/utils/paces'; -import storage from '@/utils/localStorage'; import targetUtils from '@/utils/targets'; import unitUtils from '@/utils/units'; @@ -55,81 +54,37 @@ import SimpleTargetTable from '@/components/SimpleTargetTable.vue'; import TargetSetSelector from '@/components/TargetSetSelector.vue'; import TimeInput from '@/components/TimeInput.vue'; +import useStorage from '@/composables/useStorage'; + /** * The input distance value */ -const inputDistance = ref(storage.get('pace-calculator-input-distance', 5)); +const inputDistance = useStorage('pace-calculator-input-distance', 5); /** * The input distance unit */ -const inputUnit = ref(storage.get('pace-calculator-input-unit', 'kilometers')); +const inputUnit = useStorage('pace-calculator-input-unit', 'kilometers'); /** * The input time value */ -const inputTime = ref(storage.get('pace-calculator-input-time', 20 * 60)); +const inputTime = useStorage('pace-calculator-input-time', 20 * 60); /** * The default unit system - * - * Loaded in onActivated() hook */ -const defaultUnitSystem = ref(null); +const defaultUnitSystem = useStorage('default-unit-system', unitUtils.detectDefaultUnitSystem()); /** * The current selected target set */ -const selectedTargetSet = ref(storage.get('pace-calculator-target-set', '_pace_targets')); +const selectedTargetSet = useStorage('pace-calculator-target-set', '_pace_targets'); /** * The target sets - * - * Loaded in onActivated() hook - */ -const targetSets = ref({}); - -/** - * Save input distance value - */ -watch(inputDistance, (newValue) => { - storage.set('pace-calculator-input-distance', newValue); -}); - -/** - * Save input distance unit - */ -watch(inputUnit, (newValue) => { - storage.set('pace-calculator-input-unit', newValue); -}); - -/** - * Save input time value - */ -watch(inputTime, (newValue) => { - storage.set('pace-calculator-input-time', newValue); -}); - -/** - * Save default unit system */ -watch(defaultUnitSystem, (newValue) => { - storage.set('default-unit-system', newValue); -}); - -/** - * Save the current selected target set - */ -watch(selectedTargetSet, (newValue) => { - storage.set('pace-calculator-target-set', newValue); -}); - -/** - * Save target sets - */ -watch(targetSets, (newValue) => { - storage.set('target-sets', newValue); -}, { deep: true }); +const targetSets = useStorage('target-sets', targetUtils.defaultTargetSets); /** * The input pace (in seconds per meter) @@ -140,13 +95,6 @@ const pace = computed(() => { }); /** - * Reload the target sets - */ -function reloadTargets() { - targetSets.value = storage.get('target-sets', targetUtils.defaultTargetSets); -} - -/** * Calculate paces from a target * @param {Object} target The target * @returns {Object} The result @@ -186,14 +134,6 @@ function calculatePace(target) { // Return result return result; } - -/** - * (Re)load settings used in multiple calculators - */ -onActivated(() => { - reloadTargets(); - defaultUnitSystem.value = storage.get('default-unit-system', unitUtils.detectDefaultUnitSystem()); -}); </script> <style scoped> diff --git a/src/views/RaceCalculator.vue b/src/views/RaceCalculator.vue @@ -74,11 +74,10 @@ </template> <script setup> -import { computed, onActivated, ref, watch } from 'vue'; +import { computed } from 'vue'; import formatUtils from '@/utils/format'; import raceUtils from '@/utils/races'; -import storage from '@/utils/localStorage'; import targetUtils from '@/utils/targets'; import unitUtils from '@/utils/units'; @@ -87,56 +86,47 @@ import SimpleTargetTable from '@/components/SimpleTargetTable.vue'; import TargetSetSelector from '@/components/TargetSetSelector.vue'; import TimeInput from '@/components/TimeInput.vue'; +import useStorage from '@/composables/useStorage'; + /** * The input distance value */ -const inputDistance = ref(storage.get('race-calculator-input-distance', 5)); +const inputDistance = useStorage('race-calculator-input-distance', 5); /** * The input distance unit */ -const inputUnit = ref(storage.get('race-calculator-input-unit', 'kilometers')); +const inputUnit = useStorage('race-calculator-input-unit', 'kilometers'); /** * The input time value */ -const inputTime = ref(storage.get('race-calculator-input-time', 20 * 60)); +const inputTime = useStorage('race-calculator-input-time', 20 * 60); /** * The default unit system - * - * Loaded in onActivated() hook */ -const defaultUnitSystem = ref(null); +const defaultUnitSystem = useStorage('default-unit-system', unitUtils.detectDefaultUnitSystem()); /** * The race prediction model */ -const model = ref(storage.get('race-calculator-model', 'AverageModel')); +const model = useStorage('race-calculator-model', 'AverageModel'); /** * The value of the exponent in Riegel's Model */ -const riegelExponent = ref(storage.get('race-calculator-riegel-exponent', 1.06)); +const riegelExponent = useStorage('race-calculator-riegel-exponent', 1.06); /** * The current selected target set */ -const selectedTargetSet = ref(storage.get('race-calculator-target-set', '_race_targets')); +const selectedTargetSet = useStorage('race-calculator-target-set', '_race_targets'); /** * The target sets - * - * Loaded in onActivated() hook */ -let targetSets = ref({}); - -/** - * Reload the target sets - */ -function reloadTargets() { - targetSets.value = storage.get('target-sets', targetUtils.defaultTargetSets); -} +let targetSets = useStorage('target-sets', targetUtils.defaultTargetSets); /** * Predict race results from a target @@ -258,70 +248,6 @@ const vo2Percentage = computed(() => { const result = raceUtils.VO2MaxModel.getVO2Percentage(inputTime.value) * 100; return result; }); - -/** - * Save input distance value - */ -watch(inputDistance, (newValue) => { - storage.set('race-calculator-input-distance', newValue); -}); - -/** - * Save input distance unit - */ -watch(inputUnit, (newValue) => { - storage.set('race-calculator-input-unit', newValue); -}); - -/** - * Save input time value - */ -watch(inputTime, (newValue) => { - storage.set('race-calculator-input-time', newValue); -}); - -/** - * Save default unit system - */ -watch(defaultUnitSystem, (newValue) => { - storage.set('default-unit-system', newValue); -}); - -/** - * Save prediction model - */ -watch(model, (newValue) => { - storage.set('race-calculator-model', newValue); -}); - -/** - * Save Riegel Model exponent - */ -watch(riegelExponent, (newValue) => { - storage.set('race-calculator-riegel-exponent', newValue); -}); - -/** - * Save the current selected target set - */ -watch(selectedTargetSet, (newValue) => { - storage.set('race-calculator-target-set', newValue); -}); - -/** - * Save target sets - */ -watch(targetSets, (newValue) => { - storage.set('target-sets', newValue); -}, { deep: true }); - -/** -* (Re)load settings used in multiple calculators -*/ -onActivated(() => { - reloadTargets(); - defaultUnitSystem.value = storage.get('default-unit-system', unitUtils.detectDefaultUnitSystem()); -}); </script> <style scoped> diff --git a/src/views/SplitCalculator.vue b/src/views/SplitCalculator.vue @@ -66,55 +66,31 @@ </template> <script setup> -import { computed, onActivated, ref, watch } from 'vue'; +import { computed } from 'vue'; import formatUtils from '@/utils/format'; -import storage from '@/utils/localStorage'; import targetUtils from '@/utils/targets'; import unitUtils from '@/utils/units'; import TargetSetSelector from '@/components/TargetSetSelector.vue'; import TimeInput from '@/components/TimeInput.vue'; +import useStorage from '@/composables/useStorage'; + /** * The default unit system - * - * Loaded in onActivated() hook */ -const defaultUnitSystem = ref(null); +const defaultUnitSystem = useStorage('default-unit-system', unitUtils.detectDefaultUnitSystem()); /** * The current selected target set */ -const selectedTargetSet = ref(storage.get('split-calculator-target-set', '_split_targets')); +const selectedTargetSet = useStorage('split-calculator-target-set', '_split_targets'); /** * The default output targets - * - * Loaded in onActivated() hook - */ -const targetSets = ref({}); - -/** - * Save default unit system - */ -watch(defaultUnitSystem, (newValue) => { - storage.set('default-unit-system', newValue); -}); - -/** - * Save the current selected target set - */ -watch(selectedTargetSet, (newValue) => { - storage.set('split-calculator-target-set', newValue); -}); - -/** - * Save target sets */ -watch(targetSets, (newValue) => { - storage.set('target-sets', newValue); -}, { deep: true }); +const targetSets = useStorage('target-sets', targetUtils.defaultTargetSets); /** * The target table results @@ -159,21 +135,6 @@ const results = computed(() => { // Return results array return results; }); - -/** - * Reload the target sets - */ -function reloadTargets() { - targetSets.value = storage.get('target-sets', targetUtils.defaultTargetSets); -} - -/** - * (Re)load settings used in multiple calculators - */ -onActivated(() => { - reloadTargets(); - defaultUnitSystem.value = storage.get('default-unit-system', unitUtils.detectDefaultUnitSystem()); -}); </script> <style scoped> diff --git a/src/views/UnitCalculator.vue b/src/views/UnitCalculator.vue @@ -6,12 +6,12 @@ <option value="speed_and_pace">Speed &amp; Pace</option> </select> - <time-input v-if="getUnitType(inputUnit) === 'time'" class="input-value" - label="Input time" v-model="inputValue"/> + <time-input v-if="getUnitType(input.inputUnit) === 'time'" class="input-value" + label="Input time" v-model="input.inputValue"/> <decimal-input v-else class="input-value" aria-label="Input value" - v-model="inputValue" :min="0" :digits="2"/> + v-model="input.inputValue" :min="0" :digits="2"/> - <select v-model="inputUnit" class="input-units" aria-label="Input units"> + <select v-model="input.inputUnit" class="input-units" aria-label="Input units"> <option v-for="(value, key) in units" :key="key" :value="key"> {{ value.name }} </option> @@ -19,14 +19,14 @@ <span class="equals"> = </span> - <span v-if="getUnitType(outputUnit) === 'time'" class="output-value" aria-label="Output value"> + <span v-if="getUnitType(input.outputUnit) === 'time'" class="output-value" aria-label="Output value"> {{ formatUtils.formatDuration(outputValue, 6, 3, true) }} </span> <span v-else class="output-value" aria-label="Output value"> {{ formatUtils.formatNumber(outputValue, 0, 3, true) }} </span> - <select v-model="outputUnit" class="output-units" aria-label="Output units"> + <select v-model="input.outputUnit" class="output-units" aria-label="Output units"> <option v-for="(value, key) in units" :key="key" :value="key"> {{ value.name }} </option> @@ -35,34 +35,53 @@ </template> <script setup> - import { computed, ref, watch } from 'vue'; +import { computed, ref } from 'vue'; import formatUtils from '@/utils/format'; -import storage from '@/utils/localStorage'; import unitUtils from '@/utils/units'; import DecimalInput from '@/components/DecimalInput.vue'; import TimeInput from '@/components/TimeInput.vue'; -/** - * The input value - */ -const inputValue = ref(storage.get('unit-calculator-distance-input-value', 1.0)); +import useStorage from '@/composables/useStorage'; /** - * The unit of the input + * The calculator inputs */ -const inputUnit = ref(storage.get('unit-calculator-distance-input-unit', 'miles')); +const inputs = useStorage('unit-calculator-inputs', { + distance: { + inputValue: 1, + inputUnit: 'miles', + outputUnit: 'kilometers', + }, + time: { + inputValue: 1, + inputUnit: 'seconds', + outputUnit: 'hh:mm:ss', + }, + speed_and_pace: { + inputValue: 600, + inputUnit: 'seconds_per_mile', + outputUnit: 'miles_per_hour', + }, +}); /** - * The unit of the output + * The unit category */ -const outputUnit = ref(storage.get('unit-calculator-distance-output-unit', 'kilometers')); +const category = ref('distance'); /** - * The unit category + * The inputs for the current category */ -const category = ref('distance'); +const input = computed({ + get() { + return inputs.value[category.value]; + }, + set(newValue) { + inputs.value[category.value] = newValue; + }, +}); /** * The names of the units in the current category @@ -97,18 +116,20 @@ const units = computed(() => { const outputValue = computed(() => { switch (category.value) { case 'distance': { - return unitUtils.convertDistance(inputValue.value, inputUnit.value, outputUnit.value); + return unitUtils.convertDistance(input.value.inputValue, input.value.inputUnit, + input.value.outputUnit); } case 'time': { // Correct input and output units for 'hh:mm:ss' unit - const realInput = inputUnit.value === 'hh:mm:ss' ? 'seconds' : inputUnit.value; - const realOutput = outputUnit.value === 'hh:mm:ss' ? 'seconds' : outputUnit.value; + const realInput = input.value.inputUnit === 'hh:mm:ss' ? 'seconds' : input.value.inputUnit; + const realOutput = input.value.outputUnit === 'hh:mm:ss' ? 'seconds' : input.value.outputUnit; // Calculate conversion - return unitUtils.convertTime(inputValue.value, realInput, realOutput); + return unitUtils.convertTime(input.value.inputValue, realInput, realOutput); } case 'speed_and_pace': { - return unitUtils.convertSpeedPace(inputValue.value, inputUnit.value, outputUnit.value); + return unitUtils.convertSpeedPace(input.value.inputValue, input.value.inputUnit, + input.value.outputUnit); } default: { return null; @@ -117,106 +138,6 @@ const outputValue = computed(() => { }); /** - * Reset inputValue, inputUnit, and outputUnit - */ -watch(category, (newValue) => { - switch (newValue) { - case 'distance': { - inputValue.value = storage.get('unit-calculator-distance-input-value', 1); - inputUnit.value = storage.get('unit-calculator-distance-input-unit', 'miles'); - outputUnit.value = storage.get('unit-calculator-distance-output-unit', 'kilometers'); - break; - } - case 'time': { - inputValue.value = storage.get('unit-calculator-time-input-value', 1); - inputUnit.value = storage.get('unit-calculator-time-input-unit', 'seconds'); - outputUnit.value = storage.get('unit-calculator-time-output-unit', 'hh:mm:ss'); - break; - } - case 'speed_and_pace': { - inputValue.value = storage.get('unit-calculator-speed-input-value', 600); - inputUnit.value = storage.get('unit-calculator-speed-input-unit', - 'seconds_per_mile'); - outputUnit.value = storage.get('unit-calculator-speed-output-unit', - 'miles_per_hour'); - break; - } - default: { - break; - } - } -}); - -/** - * Save input value - */ -watch(inputValue, (newValue) => { - switch (category.value) { - case 'distance': { - storage.set('unit-calculator-distance-input-value', newValue); - break; - } - case 'time': { - storage.set('unit-calculator-time-input-value', newValue); - break; - } - case 'speed_and_pace': { - storage.set('unit-calculator-speed-input-value', newValue); - break; - } - default: { - break; - } - } -}); - -/** - * Save input unit - */ -watch(inputUnit, (newValue) => { - switch (category.value) { - case 'distance': { - storage.set('unit-calculator-distance-input-unit', newValue); - break; - } - case 'time': { - storage.set('unit-calculator-time-input-unit', newValue); - break; - } - case 'speed_and_pace': { - storage.set('unit-calculator-speed-input-unit', newValue); - break; - } - default: { - break; - } - } -}); - -/** - * Save output unit - */ -watch(outputUnit, (newValue) => { - switch (category.value) { - case 'distance': { - storage.set('unit-calculator-distance-output-unit', newValue); - break; - } - case 'time': { - storage.set('unit-calculator-time-output-unit', newValue); - break; - } - case 'speed_and_pace': { - storage.set('unit-calculator-speed-output-unit', newValue); - break; - } - default: { - break; - } - } -}); - -/** * Get the type of a unit * @param {String} unit The unit * @returns {String} The type ('decimal' or 'time') diff --git a/tests/unit/components/TargetSetSelector.spec.js b/tests/unit/components/TargetSetSelector.spec.js @@ -1,4 +1,4 @@ -import { beforeEach, test, expect, vi } from 'vitest'; +import { test, expect, vi } from 'vitest'; import { shallowMount } from '@vue/test-utils'; import TargetSetSelector from '@/components/TargetSetSelector.vue'; diff --git a/tests/unit/views/PaceCalculator.spec.js b/tests/unit/views/PaceCalculator.spec.js @@ -75,7 +75,6 @@ test('should not show paces in results table', async () => { test('should correctly handle null target set', async () => { // Initialize component const wrapper = shallowMount(PaceCalculator); - await wrapper.vm.reloadTargets(); // onActivated method not called in tests // Switch to invalid target set await wrapper.findComponent({ name: 'target-set-selector' }) @@ -130,7 +129,6 @@ test('should load selected target set from localStorage', async () => { // Initialize component const wrapper = shallowMount(PaceCalculator); - await wrapper.vm.reloadTargets(); // Assert selection is loaded expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet) diff --git a/tests/unit/views/RaceCalculator.spec.js b/tests/unit/views/RaceCalculator.spec.js @@ -75,7 +75,6 @@ test('should show paces in results table', async () => { test('should correctly handle null target set', async () => { // Initialize component const wrapper = shallowMount(RaceCalculator); - await wrapper.vm.reloadTargets(); // onActivated method not called in tests // Switch to invalid target set await wrapper.findComponent({ name: 'target-set-selector' }) @@ -189,7 +188,6 @@ test('should load selected target set from localStorage', async () => { // Initialize component const wrapper = shallowMount(RaceCalculator); - await wrapper.vm.reloadTargets(); // Assert selection is loaded expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet) diff --git a/tests/unit/views/SplitCalculator.spec.js b/tests/unit/views/SplitCalculator.spec.js @@ -9,7 +9,6 @@ beforeEach(() => { test('should initialize undefined splits to 0:00.00', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // onActivated method not called in tests // Assert results are correct const rows = wrapper.findAll('tbody tr'); @@ -46,7 +45,6 @@ test('should correctly load split times from split targets', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Assert results are correct const rows = wrapper.findAll('tbody tr'); @@ -77,7 +75,6 @@ test('should correctly handle null target set', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Assert results are empty let rows = wrapper.findAll('tbody tr'); @@ -102,7 +99,6 @@ test('should correctly handle null target set', async () => { test('should correctly calculate paces and cumulative times from entered split times', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Update split times await wrapper.findAllComponents({ name: 'time-input' })[0].setValue(420); @@ -147,7 +143,6 @@ test('should correctly sort split targets', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator) - await wrapper.vm.reloadTargets(); // Assert results are correct const rows = wrapper.findAll('tbody tr'); @@ -184,7 +179,6 @@ test('should ignore time based targets', async () => { })); // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Assert results are correct const rows = wrapper.findAll('tbody tr'); @@ -221,7 +215,6 @@ test('should correctly save split times with split targets in localStorage', asy // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Update split times await wrapper.findAllComponents({ name: 'time-input' })[1].setValue(190); @@ -263,7 +256,6 @@ test('should update results when a new target set is selected', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Assert default split targets are initially loaded expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet) @@ -321,7 +313,6 @@ test('should load selected target set from localStorage', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Assert selection is loaded expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('B'); @@ -352,7 +343,6 @@ test('should load selected target set from localStorage', async () => { test('should save selected target set to localStorage when modified', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Select a new target set await wrapper.findComponent({ name: 'target-set-selector' }) @@ -366,7 +356,6 @@ test('should save selected target set to localStorage when modified', async () = test('should update paces according to default units setting', async () => { // Initialize component const wrapper = shallowMount(SplitCalculator); - await wrapper.vm.reloadTargets(); // Enter split times await wrapper.findAllComponents({ name: 'time-input' })[0].setValue(300); diff --git a/tests/unit/views/UnitCalculator.spec.js b/tests/unit/views/UnitCalculator.spec.js @@ -91,15 +91,23 @@ test('should correctly convert to and from hh:mm:ss', async () => { test('should correctly load saved inputs', async () => { // Initialize localStorage - localStorage.setItem('running-tools.unit-calculator-distance-input-value', '5'); - localStorage.setItem('running-tools.unit-calculator-distance-input-unit', '"kilometers"'); - localStorage.setItem('running-tools.unit-calculator-distance-output-unit', '"miles"'); - localStorage.setItem('running-tools.unit-calculator-time-input-value', '90'); - localStorage.setItem('running-tools.unit-calculator-time-input-unit', '"hh:mm:ss"'); - localStorage.setItem('running-tools.unit-calculator-time-output-unit', '"minutes"'); - localStorage.setItem('running-tools.unit-calculator-speed-input-value', '15'); - localStorage.setItem('running-tools.unit-calculator-speed-input-unit', '"miles_per_hour"'); - localStorage.setItem('running-tools.unit-calculator-speed-output-unit', '"seconds_per_mile"'); + localStorage.setItem('running-tools.unit-calculator-inputs', JSON.stringify({ + distance: { + inputValue: 5, + inputUnit: 'kilometers', + outputUnit: 'miles', + }, + time: { + inputValue: 90, + inputUnit: 'hh:mm:ss', + outputUnit: 'minutes', + }, + speed_and_pace: { + inputValue: 15, + inputUnit: 'miles_per_hour', + outputUnit: 'seconds_per_mile', + }, + })); // Initialize component const wrapper = shallowMount(UnitCalculator); @@ -152,13 +160,21 @@ test('should correctly save inputs', async () => { await wrapper.find('select[aria-label="Output units"]').setValue('seconds_per_mile'); // Initialize localStorage - expect(localStorage.getItem('running-tools.unit-calculator-distance-input-value')).to.equal('5'); - expect(localStorage.getItem('running-tools.unit-calculator-distance-input-unit')).to.equal('"kilometers"'); - expect(localStorage.getItem('running-tools.unit-calculator-distance-output-unit')).to.equal('"miles"'); - expect(localStorage.getItem('running-tools.unit-calculator-time-input-value')).to.equal('90'); - expect(localStorage.getItem('running-tools.unit-calculator-time-input-unit')).to.equal('"hh:mm:ss"'); - expect(localStorage.getItem('running-tools.unit-calculator-time-output-unit')).to.equal('"minutes"'); - expect(localStorage.getItem('running-tools.unit-calculator-speed-input-value')).to.equal('15'); - expect(localStorage.getItem('running-tools.unit-calculator-speed-input-unit')).to.equal('"miles_per_hour"'); - expect(localStorage.getItem('running-tools.unit-calculator-speed-output-unit')).to.equal('"seconds_per_mile"'); + expect(localStorage.getItem('running-tools.unit-calculator-inputs')).to.equal(JSON.stringify({ + distance: { + inputValue: 5, + inputUnit: 'kilometers', + outputUnit: 'miles', + }, + time: { + inputValue: 90, + inputUnit: 'hh:mm:ss', + outputUnit: 'minutes', + }, + speed_and_pace: { + inputValue: 15, + inputUnit: 'miles_per_hour', + outputUnit: 'seconds_per_mile', + }, + })); });