commit b3641bc4d8fb6ffd34390b7050f431e8495bf976
parent f8903eefac949b7eebf6e2c3a33bb6d55e2af521
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Sat, 19 Jul 2025 16:35:06 -0700
Implement custom batch column label option
Only available when the workout calculator is selected and target name
customization is enabled.
Diffstat:
11 files changed, 337 insertions(+), 47 deletions(-)
diff --git a/src/components/AdvancedOptionsInput.vue b/src/components/AdvancedOptionsInput.vue
@@ -24,6 +24,13 @@
</select>
</div>
+ <div v-if="batchOptions && props.batchInput && props.type === Calculators.Workout"
+ v-show="(options as WorkoutOptions).customTargetNames">
+ Batch Column Label:
+ <input v-model="batchOptions.label" :placeholder="formatDistance(props.batchInput, false)"
+ aria-label="Batch column label"/>
+ </div>
+
<div v-if="props.type === Calculators.Race || props.type === Calculators.Workout">
Prediction Model:
<select v-model="(options as RaceOptions).model" aria-label="Prediction model">
@@ -47,10 +54,12 @@
<script setup lang="ts">
import { Calculators } from '@/core/calculators';
-import type { StandardOptions, RaceOptions, WorkoutOptions } from '@/core/calculators';
+import type { BatchOptions, StandardOptions, RaceOptions,
+ WorkoutOptions } from '@/core/calculators';
import { RacePredictionModels } from '@/core/racePrediction';
import type { TargetSets } from '@/core/targets';
-import { UnitSystems } from '@/core/units';
+import { UnitSystems, formatDistance } from '@/core/units';
+import type { DistanceTime } from '@/core/units';
import DecimalInput from '@/components/DecimalInput.vue';
import TargetSetSelector from '@/components/TargetSetSelector.vue';
@@ -66,6 +75,16 @@ const defaultUnitSystem = defineModel<UnitSystems>('defaultUnitSystem');
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,
+
+ /*
* The calculator options
*/
options: CalculatorOptions,
@@ -82,7 +101,9 @@ const props = defineProps<{
}>();
// Generate internal refs tied to options and targetSets props
-const emit = defineEmits(['update:options', 'update:targetSets']);
+const emit = defineEmits(['update:batchOptions', 'update:options', 'update:targetSets']);
+const batchOptions = useObjectModel<BatchOptions | undefined>(() => props.batchOptions, (x) =>
+ emit('update:batchOptions', x));
const options = useObjectModel<CalculatorOptions>(() => props.options, (x) =>
emit('update:options', x));
const targetSets = useObjectModel<TargetSets>(() => props.targetSets, (x) =>
diff --git a/src/components/DoubleOutputTable.vue b/src/components/DoubleOutputTable.vue
@@ -32,40 +32,45 @@ import { computed } from 'vue';
import { ResultType } from '@/core/calculators';
import type { TargetResult } from '@/core/calculators';
import type { Target } from '@/core/targets';
-import { formatDistance, formatDuration } from '@/core/units';
+import { formatDuration } from '@/core/units';
import type { Distance, DistanceTime } from '@/core/units';
interface Props {
- /**
+ /*
* The method that generates the target table rows
*/
calculateResult: (x: DistanceTime, y: Target) => TargetResult,
- /**
- * The target set
+ /*
+ * The input distance
*/
- targets: Array<Target>,
+ inputDistance: Distance,
- /**
+ /*
* The set of input times
*/
inputTimes: Array<number>,
- /**
- * The input distance
+ /*
+ * The label to use for the first column
*/
- inputDistance: Distance,
+ label: string,
+
+ /*
+ * The target set
+ */
+ targets: Array<Target>,
}
const props = defineProps<Props>();
-/**
+/*
* The target table results
*/
const results = computed(() => {
// Calculate results
const results: Array<Array<string>> = [[
- formatDistance(props.inputDistance, false),
+ props.label
]];
props.inputTimes.forEach((input, y) => {
diff --git a/src/core/calculators.ts b/src/core/calculators.ts
@@ -47,6 +47,7 @@ export interface WorkoutOptions extends RaceOptions {
export interface BatchOptions {
calculator: Calculators.Pace | Calculators.Race | Calculators.Workout,
increment: number,
+ label: string,
rows: number,
};
@@ -80,6 +81,7 @@ export const defaultInput: DistanceTime = {
export const defaultBatchOptions: BatchOptions = {
calculator: Calculators.Workout,
increment: 15,
+ label: '',
rows: 20,
};
export const defaultPaceOptions: StandardOptions = {
diff --git a/src/core/utils.ts b/src/core/utils.ts
@@ -62,6 +62,13 @@ export function unsetLocalStorage(key: string) {
export function migrateLocalStorage() {
/* eslint-disable @typescript-eslint/no-explicit-any */
+ // Add label property to batch-calculator-options (>1.4.1)
+ const batchOptions = getLocalStorage<any>('batch-calculator-options');
+ if (batchOptions !== null && batchOptions.label === undefined) {
+ batchOptions.label = '';
+ setLocalStorage('batch-calculator-options', batchOptions);
+ }
+
// Move pace-calculator-target-set into new pace-calculator-options (>1.4.1)
const paceSelectedTargetSet = getLocalStorage<string>('pace-calculator-target-set');
if (paceSelectedTargetSet !== null) {
diff --git a/src/views/BatchCalculator.vue b/src/views/BatchCalculator.vue
@@ -16,9 +16,9 @@
<div>
Calculator:
<select aria-label="Calculator" v-model="options.calculator">
- <option value="pace">Pace Calculator</option>
- <option value="race">Race Calculator</option>
- <option value="workout">Workout Calculator</option>
+ <option :value="calculators.Calculators.Pace">Pace Calculator</option>
+ <option :value="calculators.Calculators.Race">Race Calculator</option>
+ <option :value="calculators.Calculators.Workout">Workout Calculator</option>
</select>
</div>
</div>
@@ -27,13 +27,14 @@
<summary>
<h2>Advanced Options</h2>
</summary>
- <advanced-options-input v-model:defaultUnitSystem="defaultUnitSystem"
- v-model:options="calcOptions" v-model:targetSets="targetSets" :type="options.calculator"/>
+ <advanced-options-input :batch-input="input" v-model:batch-options="options"
+ v-model:defaultUnitSystem="defaultUnitSystem" v-model:options="calcOptions"
+ v-model:targetSets="targetSets" :type="options.calculator"/>
</details>
<h2>Batch Results</h2>
- <double-output-table class="output" :input-times="inputTimes" :input-distance="inputDistance"
- :calculate-result="calculateResult"
+ <double-output-table class="output" :calculate-result="calculateResult"
+ :input-distance="inputDistance" :input-times="inputTimes" :label="batchColumnLabel"
:targets="targetSets[calcOptions.selectedTargetSet] ?
targetSets[calcOptions.selectedTargetSet].targets : []"/>
</div>
@@ -46,7 +47,7 @@ import * as calculators from '@/core/calculators';
import type { BatchOptions, RaceOptions, StandardOptions, TargetResult,
WorkoutOptions } from '@/core/calculators';
import * as targetUtils from '@/core/targets';
-import { UnitSystems, detectDefaultUnitSystem } from '@/core/units';
+import { UnitSystems, detectDefaultUnitSystem, formatDistance } from '@/core/units';
import type { Distance, DistanceTime } from '@/core/units';
import AdvancedOptionsInput from '@/components/AdvancedOptionsInput.vue';
@@ -205,6 +206,18 @@ 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;
+ } else {
+ return formatDistance(input.value, false);
+ }
+});
</script>
<style scoped>
diff --git a/tests/e2e/batch-calculator.spec.js b/tests/e2e/batch-calculator.spec.js
@@ -33,13 +33,14 @@ test('Batch calculator', async ({ page }) => {
await expect(page.getByRole('row').nth(15).getByRole('cell')).toHaveCount(5);
await expect(page.getByRole('row')).toHaveCount(16);
- // Change prediction model and enable customized target names
+ // Change prediction model, enable customized target names, and set custom batch column label
await page.getByText('Advanced Options').click();
await page.getByLabel('Prediction model').selectOption('Riegel\'s Model');
await page.getByLabel('Target name customization').selectOption('Enabled');
+ await page.getByLabel('Batch column label').fill('foo');
// Assert workout results are correct
- await expect(page.getByRole('row').nth(0).getByRole('cell').nth(0)).toHaveText('2 mi');
+ await expect(page.getByRole('row').nth(0).getByRole('cell').nth(0)).toHaveText('foo');
await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m @ 5 km');
await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(5);
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30');
@@ -167,7 +168,7 @@ test('Batch calculator', async ({ page }) => {
{
await page.getByLabel('Calculator').selectOption('Workout Calculator');
await expect(page.getByLabel('Target name customization')).toHaveValue("true");
- await expect(page.getByRole('row').nth(0).getByRole('cell').nth(0)).toHaveText('2 mi');
+ await expect(page.getByRole('row').nth(0).getByRole('cell').nth(0)).toHaveText('foo');
await expect(page.getByRole('row').nth(0).getByRole('cell').nth(2)).toHaveText('800 m @ 5 km');
await expect(page.getByRole('row').nth(0).getByRole('cell')).toHaveCount(5);
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toHaveText('10:30');
diff --git a/tests/e2e/cross-calculator.spec.js b/tests/e2e/cross-calculator.spec.js
@@ -234,6 +234,7 @@ test('Cross-calculator', async ({ page }) => {
localStorage.getItem('running-tools.batch-calculator-options'))).toEqual(JSON.stringify({
calculator: 'race',
increment: 10,
+ label: '',
rows: 15,
}));
@@ -739,6 +740,7 @@ test('v1.4.1 Migration', async ({ page }) => {
calculator: 'race',
increment: 10,
rows: 15,
+ label: '',
}));
// Assert localStorage entries for the pace calculator are correct
diff --git a/tests/unit/components/AdvancedOptionsInput.spec.js b/tests/unit/components/AdvancedOptionsInput.spec.js
@@ -56,6 +56,8 @@ test('should be correctly render pace options according to props', () => {
});
expect(wrapper.findAll('select[aria-label="Target name customization"]')).to.have
.length(0);
+ expect(wrapper.findAll('input[aria-label="Batch column label"]')).to.have
+ .length(0);
expect(wrapper.findAll('select[aria-label="Prediction model"]')).to.have.length(0);
expect(wrapper.findAllComponents({ name: 'decimal-input' })).to.have.length(0);
});
@@ -82,6 +84,8 @@ test('should be correctly render race options according to props', () => {
.equal('_new');
expect(wrapper.findAll('select[aria-label="Target name customization"]')).to.have
.length(0);
+ expect(wrapper.findAll('input[aria-label="Batch column label"]')).to.have
+ .length(0);
expect(wrapper.find('select[aria-label="Prediction model"]').element.value).to
.equal('PurdyPointsModel');
expect(wrapper.findComponent({ name: 'decimal-input' }).vm.modelValue).to.equal(1.2);
@@ -144,6 +148,8 @@ test('should be correctly render split options according to props', () => {
.equal('_new');
expect(wrapper.findAll('select[aria-label="Target name customization"]')).to.have
.length(0);
+ expect(wrapper.findAll('input[aria-label="Batch column label"]')).to.have
+ .length(0);
expect(wrapper.findAll('select[aria-label="Prediction model"]')).to.have.length(0);
expect(wrapper.findAllComponents({ name: 'decimal-input' })).to.have.length(0);
});
@@ -170,11 +176,118 @@ test('should be correctly render workout options according to props', () => {
.equal('_new');
expect(wrapper.find('select[aria-label="Target name customization"]').element.value).to
.equal('true');
+ expect(wrapper.findAll('input[aria-label="Batch column label"]')).to.have
+ .length(0);
expect(wrapper.find('select[aria-label="Prediction model"]').element.value).to
.equal('PurdyPointsModel');
expect(wrapper.findComponent({ name: 'decimal-input' }).vm.modelValue).to.equal(1.2);
});
+test('should only show batch column label field when applicable', async () => {
+ // Initialize component with workout target name customization enabled
+ const wrapper = shallowMount(AdvancedOptionsInput, {
+ propsData: {
+ defaultUnitSystem: 'metric',
+ options: {
+ customTargetNames: true,
+ model: 'PurdyPointsModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: '_new',
+ },
+ targetSets: {},
+ type: 'workout',
+ },
+ attachTo: document.body,
+ });
+
+ // Assert batch column label field is hidden
+ expect(wrapper.findAll('input[aria-label="Batch column label"]')).to.have
+ .length(0);
+
+ // Add batchInput and batchOptions but disable workout target name customization
+ await wrapper.setProps({
+ batchInput: { // added
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 600,
+ },
+ batchOptions: { // added
+ calculator: 'workout',
+ increment: 32,
+ label: 'foo',
+ rows: 15,
+ },
+ defaultUnitSystem: 'metric',
+ options: {
+ customTargetNames: false, // disabled
+ model: 'PurdyPointsModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: '_new',
+ },
+ targetSets: {},
+ type: 'workout',
+ });
+
+ // Assert batch column label field is still hidden
+ expect(wrapper.find('input[aria-label="Batch column label"]').isVisible()).to.equal(false);
+
+ // Enable workout target name customization
+ await wrapper.setProps({
+ batchInput: {
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 600,
+ },
+ batchOptions: {
+ calculator: 'workout',
+ increment: 32,
+ label: 'foo',
+ rows: 15,
+ },
+ defaultUnitSystem: 'metric',
+ options: {
+ customTargetNames: true, // enabled
+ model: 'PurdyPointsModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: '_new',
+ },
+ targetSets: {},
+ type: 'workout',
+ });
+
+ // Assert batch column label field is now visible
+ expect(wrapper.find('input[aria-label="Batch column label"]').isVisible()).to.equal(true);
+ expect(wrapper.find('input[aria-label="Batch column label"]').element.placeholder).to.equal('2 mi')
+ expect(wrapper.find('input[aria-label="Batch column label"]').element.value).to.equal('foo')
+
+ // Switch to race calculator
+ await wrapper.setProps({
+ batchInput: {
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 600,
+ },
+ batchOptions: {
+ calculator: 'workout',
+ increment: 32,
+ label: 'foo',
+ rows: 15,
+ },
+ defaultUnitSystem: 'metric',
+ options: {
+ model: 'PurdyPointsModel',
+ riegelExponent: 1.2,
+ selectedTargetSet: '_new',
+ },
+ targetSets: {},
+ type: 'race', // changed
+ });
+
+ // Assert batch column label field is hidden again
+ expect(wrapper.findAll('input[aria-label="Batch column label"]')).to.have
+ .length(0);
+});
+
test('should pass correct props to TargetSetSelector', async () => {
// Initialize component
const wrapper = shallowMount(AdvancedOptionsInput, {
diff --git a/tests/unit/components/DoubleOutputTable.spec.js b/tests/unit/components/DoubleOutputTable.spec.js
@@ -34,12 +34,13 @@ test('should correctly render table body rows and headers', () => {
distanceUnit: 'miles',
distanceValue: 2,
},
+ label: 'foo',
},
});
// Assert headers are correctly generated from first row of results
const headers = wrapper.findAll('th');
- expect(headers[0].element.textContent).to.equal('2 mi');
+ expect(headers[0].element.textContent).to.equal('foo');
expect(headers[1].element.textContent).to.equal('key1');
expect(headers[2].element.textContent).to.equal('key2');
expect(headers[3].element.textContent).to.equal('key3');
@@ -80,12 +81,13 @@ test('Should display message when inputs are empty', () => {
distanceUnit: 'miles',
distanceValue: 2,
},
+ label: 'foo',
},
});
// Assert headers are correctly generated
const headers = wrapper.findAll('th');
- expect(headers[0].element.textContent).to.equal('2 mi');
+ expect(headers[0].element.textContent).to.equal('foo');
expect(headers.length).to.equal(1);
// Assert results are correctly rendered
diff --git a/tests/unit/core/utils.spec.js b/tests/unit/core/utils.spec.js
@@ -180,6 +180,8 @@ describe('set method', () => {
describe('migrate method', () => {
test('should correctly migrate <=1.4.1 calculator options', async () => {
// Initialize localStorage
+ localStorage.setItem('running-tools.batch-calculator-options',
+ '{"calculator":"race","increment":32,"rows":15}');
localStorage.setItem('running-tools.pace-calculator-target-set', '"A"');
localStorage.setItem('running-tools.race-calculator-options',
'{"model":"RiegelModel","riegelExponent":1.07}');
@@ -193,6 +195,8 @@ describe('migrate method', () => {
utils.migrateLocalStorage();
// Assert localStorage entries correctly migrated
+ expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(
+ '{"calculator":"race","increment":32,"rows":15,"label":""}');
expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(
'{"selectedTargetSet":"A"}');
expect(localStorage.getItem('running-tools.pace-calculator-target-set')).to.equal(null);
@@ -209,9 +213,7 @@ describe('migrate method', () => {
});
test('should correctly migrate partial <=1.4.1 calculator options', async () => {
- // Initialize localStorage (*-target-set options missing)
- localStorage.setItem('running-tools.race-calculator-options',
- '{"model":"RiegelModel","riegelExponent":1.07}');
+ // Initialize localStorage (workout-target-set option missing)
localStorage.setItem('running-tools.workout-calculator-options',
'{"model":"RiegelModel","riegelExponent":1.08}');
@@ -219,16 +221,15 @@ describe('migrate method', () => {
utils.migrateLocalStorage();
// Assert localStorage entries correctly migrated
- expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(
- '{"model":"RiegelModel","riegelExponent":1.07,"selectedTargetSet":"_race_targets"}');
expect(localStorage.getItem('running-tools.workout-calculator-options')).to.equal(
'{"model":"RiegelModel","riegelExponent":1.08,"selectedTargetSet":"_workout_targets",' +
'"customTargetNames":false}');
});
-
test('should not modify >1.4.1 calculator options', async () => {
// Initialize localStorage
+ localStorage.setItem('running-tools.batch-calculator-options',
+ '{"calculator":"race","increment":32,"label":"foo","rows":15}');
localStorage.setItem('running-tools.pace-calculator-options',
'{"selectedTargetSet":"A"}');
localStorage.setItem('running-tools.race-calculator-options',
@@ -243,6 +244,8 @@ describe('migrate method', () => {
utils.migrateLocalStorage();
// Assert localStorage entries not modified
+ expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(
+ '{"calculator":"race","increment":32,"label":"foo","rows":15}');
expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(
'{"selectedTargetSet":"A"}');
expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(
@@ -259,6 +262,7 @@ describe('migrate method', () => {
utils.migrateLocalStorage();
// Assert localStorage entries not modified
+ expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(null);
expect(localStorage.getItem('running-tools.pace-calculator-options')).to.equal(null);
expect(localStorage.getItem('running-tools.race-calculator-options')).to.equal(null);
expect(localStorage.getItem('running-tools.split-calculator-options')).to.equal(null);
diff --git a/tests/unit/views/BatchCalculator.spec.js b/tests/unit/views/BatchCalculator.spec.js
@@ -23,6 +23,11 @@ test('should load input from localStorage', async () => {
distanceUnit: 'miles',
time: 600,
});
+ expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.batchInput).to.deep.equal({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 600,
+ });
});
test('should save input to localStorage when modified', async () => {
@@ -49,6 +54,7 @@ test('should load batch options from localStorage', async () => {
localStorage.setItem('running-tools.batch-calculator-options', JSON.stringify({
calculator: 'race',
increment: 32,
+ label: 'foo',
rows: 15,
}));
@@ -59,39 +65,64 @@ test('should load batch options from localStorage', async () => {
expect(wrapper.find('select[aria-label="Calculator"]').element.value).to.equal('race');
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',
+ increment: 32,
+ label: 'foo',
+ rows: 15,
+ });
});
test('should save batch options to localStorage when modified', async () => {
// Initialize component
const wrapper = shallowMount(BatchCalculator);
- // Update active calculator
- await wrapper.find('select[aria-label="Calculator"]').setValue('race');
+ // Update increment value
+ await wrapper.findComponent({ name: 'time-input' }).setValue(32);
// Assert options saved
expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({
- calculator: 'race',
- increment: 15,
+ calculator: 'workout',
+ increment: 32,
+ label: '',
rows: 20,
}));
- // Update increment value
- await wrapper.findComponent({ name: 'time-input' }).setValue(32);
+ // Update number of rows
+ await wrapper.findComponent({ name: 'integer-input' }).setValue(15);
// Assert options saved
expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({
- calculator: 'race',
+ calculator: 'workout',
increment: 32,
- rows: 20,
+ label: '',
+ rows: 15,
}));
- // Update number of rows
- await wrapper.findComponent({ name: 'integer-input' }).setValue(15);
+ // Update batch column label
+ await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({
+ calculator: 'workout',
+ increment: 32,
+ label: 'foo',
+ rows: 15,
+ }, 'batch-options');
+
+ // Assert options saved
+ expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({
+ calculator: 'workout',
+ increment: 32,
+ label: 'foo',
+ rows: 15,
+ }));
+
+ // Update active calculator
+ await wrapper.find('select[aria-label="Calculator"]').setValue('race');
// Assert options saved
expect(localStorage.getItem('running-tools.batch-calculator-options')).to.equal(JSON.stringify({
calculator: 'race',
increment: 32,
+ label: 'foo',
rows: 15,
}));
});
@@ -352,6 +383,7 @@ test('should pass correct input props to DoubleOutputTable', async () => {
1200, 1215, 1230, 1245, 1260, 1275, 1290, 1305, 1320, 1335,
1350, 1365, 1380, 1395, 1410, 1425, 1440, 1455, 1470, 1485,
]);
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.label).to.equal('5 km');
// Change input pace
await wrapper.findComponent({ name: 'pace-input' }).setValue({
@@ -369,6 +401,7 @@ test('should pass correct input props to DoubleOutputTable', async () => {
600, 615, 630, 645, 660, 675, 690, 705, 720, 735,
750, 765, 780, 795, 810, 825, 840, 855, 870, 885,
]);
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.label).to.equal('2 mi');
// Change increment value
await wrapper.findComponent({ name: 'time-input' }).setValue(10);
@@ -382,6 +415,7 @@ test('should pass correct input props to DoubleOutputTable', async () => {
600, 610, 620, 630, 640, 650, 660, 670, 680, 690,
700, 710, 720, 730, 740, 750, 760, 770, 780, 790,
]);
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.label).to.equal('2 mi');
// Change number of rows
await wrapper.findComponent({ name: 'integer-input' }).setValue(15);
@@ -394,23 +428,109 @@ test('should pass correct input props to DoubleOutputTable', async () => {
expect(wrapper.findComponent({ name: 'double-output-table' }).vm.inputTimes).to.deep.equal([
600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740,
]);
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.label).to.equal('2 mi');
+
+ // Enter custom batch column label
+ await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({
+ calculator: 'workout',
+ increment: 10,
+ label: 'foo',
+ rows: 15,
+ }, 'batchOptions');
+
+ // Assert that the props are updated
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.inputDistance).to.deep.equal({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ });
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.inputTimes).to.deep.equal([
+ 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740,
+ ]);
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.label).to.equal('2 mi');
+
+ // Enable target name customization
+ await wrapper.findComponent({ name: 'advanced-options-input' }).setValue({
+ customTargetNames: true,
+ model: 'AverageModel',
+ riegelExponent: 1.06,
+ selectedTargetSet: '_workout_targets',
+ }, 'options');
+
+ // Assert that the props are updated
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.inputDistance).to.deep.equal({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ });
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.inputTimes).to.deep.equal([
+ 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740,
+ ]);
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.label).to.equal('foo');
+
+ // Switch calculators
+ await wrapper.find('select[aria-label="Calculator"]').setValue('race');
+
+ // Assert that the props are updated
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.inputDistance).to.deep.equal({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ });
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.inputTimes).to.deep.equal([
+ 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740,
+ ]);
+ expect(wrapper.findComponent({ name: 'double-output-table' }).vm.label).to.equal('2 mi');
});
-test('should correctly set AdvancedOptionsInput type prop', async () => {
+test('should correctly set AdvancedOptionsInput props', async () => {
// Initialize component
const wrapper = shallowMount(BatchCalculator);
- // Assert prop is correct for pace calculator
+ // Set input pace and batch options
+ await wrapper.findComponent({ name: 'pace-input' }).setValue({
+ distanceValue: 2,
+ distanceUnit: 'miles',
+ time: 600,
+ });
+ await wrapper.findComponent({ name: 'time-input' }).setValue(32);
+ 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,
+ label: '',
+ rows: 15,
+ });
+
+ // Assert props are correct for pace calculator
await wrapper.find('select[aria-label="Calculator"]').setValue('pace');
expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.type).to.equal('pace');
+ expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({
+ selectedTargetSet: '_pace_targets',
+ });
- // Update race calculator options
+ // Assert props are correct for race calculator
await wrapper.find('select[aria-label="Calculator"]').setValue('race');
expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.type).to.equal('race');
+ expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({
+ model: 'AverageModel',
+ riegelExponent: 1.06,
+ selectedTargetSet: '_race_targets',
+ });
- // Update workout calculator options
+ // Assert props are correct for workout calculator
await wrapper.find('select[aria-label="Calculator"]').setValue('workout');
expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.type).to.equal('workout');
+ expect(wrapper.findComponent({ name: 'advanced-options-input' }).vm.options).to.deep.equal({
+ customTargetNames: false,
+ model: 'AverageModel',
+ riegelExponent: 1.06,
+ selectedTargetSet: '_workout_targets',
+ });
});
test('should correctly calculate outputs', async () => {