running-tools

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

commit bdca9057686d5eef92641513c8a356aea9dc4bb2
parent df2e196cbdbe1a47eadb626b89c391ded5e75642
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sat,  1 Jun 2024 18:46:08 -0700

Isolate target sets to specific calculators

Cross-calculator target sets are rarely necessary and make it more
difficult to implement new calculators.

Diffstat:
Msrc/components/TargetSetSelector.vue | 2+-
Msrc/views/PaceCalculator.vue | 4+++-
Msrc/views/RaceCalculator.vue | 4+++-
Msrc/views/SplitCalculator.vue | 4+++-
Mtests/e2e/cross-calculator.spec.js | 26+++++++++++++++++++-------
Mtests/e2e/pace-calculator.spec.js | 20+++++++++++---------
Mtests/e2e/race-calculator.spec.js | 19++++++++++---------
Mtests/unit/views/PaceCalculator.spec.js | 30++++++++++++++++++++++++------
Mtests/unit/views/RaceCalculator.spec.js | 44+++++++++++++++++++++++++++++++-------------
Mtests/unit/views/SplitCalculator.spec.js | 14+++++++-------
10 files changed, 112 insertions(+), 55 deletions(-)

diff --git a/src/components/TargetSetSelector.vue b/src/components/TargetSetSelector.vue @@ -42,7 +42,7 @@ const model = defineModel('selectedTargetSet', { */ const targetSets = defineModel('targetSets', { type: Object, - default: targetUtils.defaultTargetSets, + default: {}, }); defineProps({ diff --git a/src/views/PaceCalculator.vue b/src/views/PaceCalculator.vue @@ -63,7 +63,9 @@ const selectedTargetSet = useStorage('pace-calculator-target-set', '_pace_target /** * The target sets */ -const targetSets = useStorage('target-sets', targetUtils.defaultTargetSets); +const targetSets = useStorage('pace-calculator-target-sets', { + _pace_targets: targetUtils.defaultTargetSets._pace_targets +}); </script> <style scoped> diff --git a/src/views/RaceCalculator.vue b/src/views/RaceCalculator.vue @@ -93,7 +93,9 @@ const selectedTargetSet = useStorage('race-calculator-target-set', '_race_target /** * The target sets */ -let targetSets = useStorage('target-sets', targetUtils.defaultTargetSets); +let targetSets = useStorage('race-calculator-target-sets', { + _race_targets: targetUtils.defaultTargetSets._race_targets +}); /** * The statistics for the current input race diff --git a/src/views/SplitCalculator.vue b/src/views/SplitCalculator.vue @@ -90,7 +90,9 @@ const selectedTargetSet = useStorage('split-calculator-target-set', '_split_targ /** * The default output targets */ -const targetSets = useStorage('target-sets', targetUtils.defaultTargetSets); +const targetSets = useStorage('split-calculator-target-sets', { + _split_targets: targetUtils.defaultTargetSets._split_targets +}); /** * The target table results diff --git a/tests/e2e/cross-calculator.spec.js b/tests/e2e/cross-calculator.spec.js @@ -16,8 +16,22 @@ test('Save and update state when navigating between calculators', async ({ page await page.getByText('Advanced Options').click(); await page.getByLabel('Default units').selectOption('Kilometers'); - // Switch target set - await page.getByLabel('Selected target set').selectOption('5K Mile Splits'); + // Create custom target set + await page.getByLabel('Selected target set').selectOption('[ Create New Target Set ]'); + await expect(page.getByRole('row').nth(1)).toHaveText('There aren\'t any targets in this set yet.'); + await expect(page.getByRole('row')).toHaveCount(2); + + // Edit new target set + await page.getByRole('button', { name: 'Edit target set' }).click(); + await expect(page.getByLabel('Target set label')).toHaveValue('New target set'); + await page.getByLabel('Target set label').fill('800m Splits'); + await page.getByRole('button', { name: 'Add distance target' }).click(); + await page.getByLabel('Target distance value').nth(0).fill('0.4'); + await page.getByLabel('Target distance unit').nth(0).selectOption('Kilometers'); + await page.getByRole('button', { name: 'Add distance target' }).click(); + await page.getByLabel('Target distance value').nth(1).fill('800'); + await page.getByLabel('Target distance unit').nth(1).selectOption('Meters'); + await page.getByRole('button', { name: 'Close' }).click(); // Go to race calculator await page.getByRole('link', { name: 'Back' }).click(); @@ -71,11 +85,9 @@ test('Save and update state when navigating between calculators', async ({ page await page.getByRole('button', { name: 'Pace Calculator' }).click(); // Assert paces are correct (input pace not reset) - await expect(page.getByRole('row').nth(1)).toHaveText('1.6 km' + '7:42.30'); - await expect(page.getByRole('row').nth(2)).toHaveText('2.08 km' + '10:00'); - await expect(page.getByRole('row').nth(3)).toHaveText('3.2 km' + '15:24.60'); - await expect(page.getByRole('row').nth(4)).toHaveText('5 km' + '24:04.68'); - await expect(page.getByRole('row')).toHaveCount(5); + await expect(page.getByRole('row').nth(1)).toHaveText('0.4 km' + '1:55.57'); + await expect(page.getByRole('row').nth(2)).toHaveText('800 m' + '3:51.15'); + await expect(page.getByRole('row')).toHaveCount(3); // Return to race calculator await page.getByRole('link', { name: 'Back' }).click(); diff --git a/tests/e2e/pace-calculator.spec.js b/tests/e2e/pace-calculator.spec.js @@ -63,15 +63,6 @@ test('Customize target sets', async ({ page }) => { await expect(page.getByRole('row').nth(18)).toHaveText('2.45 mi' + '19:00'); await expect(page.getByRole('row')).toHaveCount(33); - // Switch target set - await page.getByLabel('Selected target set').selectOption('5K Mile Splits'); - - // Assert paces are correct - await expect(page.getByRole('row').nth(1)).toHaveText('1 mi' + '7:45.00'); - await expect(page.getByRole('row').nth(2)).toHaveText('2 mi' + '15:30.00'); - await expect(page.getByRole('row').nth(3)).toHaveText('5 km' + '24:04.68'); - await expect(page.getByRole('row')).toHaveCount(4); - // Create custom target set await page.getByLabel('Selected target set').selectOption('[ Create New Target Set ]'); await expect(page.getByRole('row').nth(1)).toHaveText('There aren\'t any targets in this set yet.'); @@ -94,7 +85,18 @@ test('Customize target sets', async ({ page }) => { await expect(page.getByRole('row').nth(2)).toHaveText('800 m' + '3:51.15'); await expect(page.getByRole('row')).toHaveCount(3); + // Switch target set + await page.getByLabel('Selected target set').selectOption('Less-common Pace Targets'); + + // Assert paces are correct + await expect(page.getByRole('row').nth(11)).toHaveText('1.01 mi' + '7:49.65'); + await expect(page.getByRole('row').nth(13)).toHaveText('1.29 mi' + '10:01'); + await expect(page.getByRole('row').nth(14)).toHaveText('1.5 mi' + '11:37.50'); + await expect(page.getByRole('row').nth(18)).toHaveText('2.45 mi' + '19:00'); + await expect(page.getByRole('row')).toHaveCount(33); + // Delete custom target set + await page.getByLabel('Selected target set').selectOption('800m Splits'); await page.getByRole('button', { name: 'Edit target set' }).click(); await expect(page.getByLabel('Target set label')).toHaveValue('800m Splits'); await page.getByRole('button', { name: 'Delete target set' }).click(); diff --git a/tests/e2e/race-calculator.spec.js b/tests/e2e/race-calculator.spec.js @@ -81,15 +81,6 @@ test('Customize target sets', async ({ page }) => { await expect(page.getByRole('row').nth(12)).toHaveText('3.49 mi' + '19:00' + '5:27 / mi'); await expect(page.getByRole('row')).toHaveCount(19); - // Switch target set - await page.getByLabel('Selected target set').selectOption('5K Mile Splits'); - - // Assert race predictions are correct - await expect(page.getByRole('row').nth(1)).toHaveText('1 mi' + '4:55.53' + '4:56 / mi'); - await expect(page.getByRole('row').nth(2)).toHaveText('2 mi' + '10:30.00' + '5:15 / mi'); - await expect(page.getByRole('row').nth(3)).toHaveText('5 km' + '16:47.58' + '5:24 / mi'); - await expect(page.getByRole('row')).toHaveCount(4); - // Create custom target set await page.getByLabel('Selected target set').selectOption('[ Create New Target Set ]'); await expect(page.getByRole('row').nth(1)).toHaveText('There aren\'t any targets in this set yet.'); @@ -112,7 +103,17 @@ test('Customize target sets', async ({ page }) => { await expect(page.getByRole('row').nth(2)).toHaveText('10 km' + '34:53.84' + '5:37 / mi'); await expect(page.getByRole('row')).toHaveCount(3); + // Switch target set + await page.getByLabel('Selected target set').selectOption('Less-common Race Targets'); + + // Assert race predictions are correct + await expect(page.getByRole('row').nth(5)).toHaveText('1.01 mi' + '4:58.81' + '4:56 / mi'); + await expect(page.getByRole('row').nth(6)).toHaveText('1.5 mi' + '7:41.60' + '5:08 / mi'); + await expect(page.getByRole('row').nth(12)).toHaveText('3.49 mi' + '19:00' + '5:27 / mi'); + await expect(page.getByRole('row')).toHaveCount(19); + // Delete custom target set + await page.getByLabel('Selected target set').selectOption('XC Race Targets'); await page.getByRole('button', { name: 'Edit target set' }).click(); await expect(page.getByLabel('Target set label')).toHaveValue('XC Race Targets'); await page.getByRole('button', { name: 'Delete target set' }).click(); diff --git a/tests/unit/views/PaceCalculator.spec.js b/tests/unit/views/PaceCalculator.spec.js @@ -137,17 +137,35 @@ test('should save input pace to localStorage', async () => { test('should load selected target set from localStorage', async () => { // Initialize localStorage - localStorage.setItem('running-tools.pace-calculator-target-set', '"_race_targets"'); + const targetSet2 = { + name: 'Pace targets #2', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + ], + }; + localStorage.setItem('running-tools.pace-calculator-target-sets', JSON.stringify({ + '_pace_targets': { + name: 'Pace targets #1', + targets: [ + { result: 'time', distanceValue: 400, distanceUnit: 'meters' }, + { result: 'time', distanceValue: 800, distanceUnit: 'meters' }, + { result: 'time', distanceValue: 1600, distanceUnit: 'meters' }, + ], + }, + 'B': targetSet2, + })); + localStorage.setItem('running-tools.pace-calculator-target-set', '"B"'); // Initialize component const wrapper = shallowMount(PaceCalculator); // Assert selection is loaded expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet) - .to.equal('_race_targets'); - const raceTargets = targetUtils.defaultTargetSets._race_targets.targets; + .to.equal('B'); expect(wrapper.findComponent({ name: 'simple-target-table' }).vm.targets) - .to.deep.equal(raceTargets); + .to.deep.equal(targetSet2.targets); }); test('should save selected target set to localStorage when modified', async () => { @@ -156,11 +174,11 @@ test('should save selected target set to localStorage when modified', async () = // Select a new target set await wrapper.findComponent({ name: 'target-set-selector' }) - .setValue('_race_targets', 'selectedTargetSet'); + .setValue('B', 'selectedTargetSet'); // New selected target set should be saved to localStorage expect(localStorage.getItem('running-tools.pace-calculator-target-set')) - .to.equal('"_race_targets"'); + .to.equal('"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 @@ -11,7 +11,7 @@ test('should correctly predict race times', async () => { // Initialize component const wrapper = shallowMount(RaceCalculator); - // Enter input pace data + // Enter input race data await wrapper.findComponent({ name: 'pace-input' }).setValue({ distanceValue: 5, distanceUnit: 'kilometers', @@ -37,7 +37,7 @@ test('should correctly calculate distance results according to default units set // Initialize component const wrapper = shallowMount(RaceCalculator); - // Enter input pace data + // Enter input race data await wrapper.findComponent({ name: 'pace-input' }).setValue({ distanceValue: 5, distanceUnit: 'kilometers', @@ -101,7 +101,7 @@ test('should correctly calculate race statistics', async () => { // Initialize component const wrapper = shallowMount(RaceCalculator); - // Enter input pace data + // Enter input race data await wrapper.findComponent({ name: 'pace-input' }).setValue({ distanceValue: 5, distanceUnit: 'kilometers', @@ -124,7 +124,7 @@ test('should correctly calculate results according to advanced model options', a // Initialize component const wrapper = shallowMount(RaceCalculator); - // Enter input pace data + // Enter input race data await wrapper.findComponent({ name: 'pace-input' }).setValue({ distanceValue: 5, distanceUnit: 'kilometers', @@ -165,7 +165,7 @@ test('should correctly calculate results according to advanced model options', a expect(result.time).to.equal(2400); }); -test('should load input pace from localStorage', async () => { +test('should load input race from localStorage', async () => { // Initialize localStorage localStorage.setItem('running-tools.race-calculator-input', JSON.stringify({ distanceValue: 1, @@ -184,11 +184,11 @@ test('should load input pace from localStorage', async () => { }); }); -test('should save input pace to localStorage', async () => { +test('should save input race to localStorage', async () => { // Initialize component const wrapper = shallowMount(RaceCalculator); - // Enter input pace data + // Enter input race data await wrapper.findComponent({ name: 'pace-input' }).setValue({ distanceValue: 1, distanceUnit: 'miles', @@ -205,17 +205,35 @@ test('should save input pace to localStorage', async () => { test('should load selected target set from localStorage', async () => { // Initialize localStorage - localStorage.setItem('running-tools.race-calculator-target-set', '"_pace_targets"'); + const targetSet2 = { + name: 'Race targets #2', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + ], + }; + localStorage.setItem('running-tools.race-calculator-target-sets', JSON.stringify({ + '_race_targets': { + name: 'Race targets #1', + targets: [ + { result: 'time', distanceValue: 400, distanceUnit: 'meters' }, + { result: 'time', distanceValue: 800, distanceUnit: 'meters' }, + { result: 'time', distanceValue: 1600, distanceUnit: 'meters' }, + ], + }, + '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('_pace_targets'); - const paceTargets = targetUtils.defaultTargetSets._pace_targets.targets; + .to.equal('B'); expect(wrapper.findComponent({ name: 'simple-target-table' }).vm.targets) - .to.deep.equal(paceTargets); + .to.deep.equal(targetSet2.targets); }); test('should save selected target set to localStorage when modified', async () => { @@ -224,11 +242,11 @@ test('should save selected target set to localStorage when modified', async () = // Select a new target set await wrapper.findComponent({ name: 'target-set-selector' }) - .setValue('_pace_targets', 'selectedTargetSet'); + .setValue('B', 'selectedTargetSet'); // New selected target set should be saved to localStorage expect(localStorage.getItem('running-tools.race-calculator-target-set')) - .to.equal('"_pace_targets"'); + .to.equal('"B"'); }); test('should save default units setting to localStorage when modified', async () => { diff --git a/tests/unit/views/SplitCalculator.spec.js b/tests/unit/views/SplitCalculator.spec.js @@ -32,7 +32,7 @@ test('should initialize undefined splits to 0:00.00', async () => { test('should correctly load split times from split targets', async () => { // Initialize localStorage - localStorage.setItem('running-tools.target-sets', JSON.stringify({ + localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({ '_split_targets': { name: 'Split targets', targets: [ @@ -130,7 +130,7 @@ test('should correctly calculate paces and cumulative times from entered split t test('should correctly sort split targets', async () => { // Initialize localStorage (targets are mis-ordered) - localStorage.setItem('running-tools.target-sets', JSON.stringify({ + localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({ '_split_targets': { name: 'Split targets', targets: [ @@ -166,7 +166,7 @@ test('should correctly sort split targets', async () => { test('should ignore time based targets', async () => { // Initialize localStorage - localStorage.setItem('running-tools.target-sets', JSON.stringify({ + localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({ '_split_targets': { name: 'Split targets', targets: [ @@ -202,7 +202,7 @@ test('should ignore time based targets', async () => { test('should correctly save split times with split targets in localStorage', async () => { // Initialize localStorage - localStorage.setItem('running-tools.target-sets', JSON.stringify({ + localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({ '_split_targets': { name: 'Split targets', targets: [ @@ -221,7 +221,7 @@ test('should correctly save split times with split targets in localStorage', asy await wrapper.findAllComponents({ name: 'time-input' })[2].setValue(200); // Assert targets correctly saved in localStorage - expect(localStorage.getItem('running-tools.target-sets')).to.equal(JSON.stringify({ + expect(localStorage.getItem('running-tools.split-calculator-target-sets')).to.equal(JSON.stringify({ '_split_targets': { name: 'Split targets', targets: [ @@ -235,7 +235,7 @@ test('should correctly save split times with split targets in localStorage', asy test('should update results when a new target set is selected', async () => { // Initialize localStorage - localStorage.setItem('running-tools.target-sets', JSON.stringify({ + localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({ '_split_targets': { name: 'Split targets', targets: [ @@ -291,7 +291,7 @@ test('should update results when a new target set is selected', async () => { test('should load selected target set from localStorage', async () => { // Initialize localStorage localStorage.setItem('running-tools.split-calculator-target-set', '"B"'); - localStorage.setItem('running-tools.target-sets', JSON.stringify({ + localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({ '_split_targets': { name: 'Split targets', targets: [