commit 608a6f6768a41f4b8c0e21742aeaac5b105955e9
parent 49de02063e07c29ea764fa088c034c8fd6df404e
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Thu, 13 Jun 2024 12:00:30 -0700
Extract Split Calculator logic into component
Diffstat:
4 files changed, 379 insertions(+), 315 deletions(-)
diff --git a/src/components/SplitOutputTable.vue b/src/components/SplitOutputTable.vue
@@ -0,0 +1,127 @@
+<template>
+ <table class="split-output-table">
+ <thead>
+ <tr>
+ <th>
+ <span>Distance</span>
+ <span class="mobile-abbreviation">Dist.</span>
+ </th>
+
+ <th>Time</th>
+
+ <th>Split</th>
+
+ <th>Pace</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <tr v-for="(item, index) in results" :key="index">
+ <td>
+ {{ formatUtils.formatNumber(item.distanceValue, 0, 2, false) }}
+ {{ unitUtils.DISTANCE_UNITS[item.distanceUnit].symbol }}
+ </td>
+
+ <td>
+ {{ formatUtils.formatDuration(item.time, 3, 2, true) }}
+ </td>
+
+ <td>
+ <time-input v-model="targets[index].splitTime" label="Split duration" :showHours="false"/>
+ </td>
+
+ <td>
+ {{ formatUtils.formatDuration(item.pace, 3, 0, true) }}
+ / {{ unitUtils.DISTANCE_UNITS[unitUtils.getDefaultDistanceUnit(defaultUnitSystem)]
+ .symbol }}
+ </td>
+ </tr>
+
+ <tr v-if="results.length === 0" class="empty-message">
+ <td colspan="5">
+ There aren't any targets in this set yet.
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</template>
+
+<script setup>
+import { computed } from 'vue';
+
+import formatUtils from '@/utils/format';
+import unitUtils from '@/utils/units';
+
+import TimeInput from '@/components/TimeInput.vue';
+
+/**
+ * The split targets
+ */
+const targets = defineModel({
+ type: Array,
+ default: () => [],
+})
+
+const props = defineProps({
+ /**
+ * The unit system to use when showing result paces
+ */
+ defaultUnitSystem: {
+ type: String,
+ default: 'metric',
+ },
+});
+
+/**
+ * The target table results
+ */
+const results = computed(() => {
+ // Initialize results array
+ const results = [];
+
+ for (let i = 0; i < targets.value.length; i += 1) {
+ // Calculate split and total times
+ const splitTime = targets.value[i].splitTime || 0;
+ const totalTime = i === 0 ? splitTime : results[i - 1].time + splitTime;
+
+ // Calculate split and total distances
+ const totalDistance = unitUtils.convertDistance(
+ targets.value[i].distanceValue,
+ targets.value[i].distanceUnit, 'meters',
+ );
+ const splitDistance = i === 0 ? totalDistance : totalDistance - results[i - 1].distance;
+
+ // Calculate pace
+ const pace = splitTime / unitUtils.convertDistance(splitDistance, 'meters',
+ unitUtils.getDefaultDistanceUnit(props.defaultUnitSystem));
+
+ // Add row to results array
+ results.push({
+ distance: totalDistance,
+ distanceValue: targets.value[i].distanceValue,
+ distanceUnit: targets.value[i].distanceUnit,
+ time: totalTime,
+ splitTime,
+ pace,
+ });
+ }
+
+ // Return results array
+ return results;
+});
+</script>
+
+<style scoped>
+/* Show/hide mobile abbreviations */
+.split-output-table th:first-child span.mobile-abbreviation {
+ display: none;
+}
+@media only screen and (max-width: 500px) {
+ .split-output-table th:first-child span:not(.mobile-abbreviation) {
+ display: none;
+ }
+ .split-output-table th:first-child span.mobile-abbreviation {
+ display: inherit;
+ }
+}
+</style>
diff --git a/src/views/SplitCalculator.vue b/src/views/SplitCalculator.vue
@@ -15,52 +15,7 @@
</div>
<div class="output">
- <table class="results">
- <thead>
- <tr>
- <th>
- <span>Distance</span>
- <span class="mobile-abbreviation">Dist.</span>
- </th>
-
- <th>Time</th>
-
- <th>Split</th>
-
- <th>Pace</th>
- </tr>
- </thead>
-
- <tbody>
- <tr v-for="(item, index) in results" :key="index">
- <td>
- {{ formatUtils.formatNumber(item.distanceValue, 0, 2, false) }}
- {{ unitUtils.DISTANCE_UNITS[item.distanceUnit].symbol }}
- </td>
-
- <td>
- {{ formatUtils.formatDuration(item.totalTime, 3, 2, true) }}
- </td>
-
- <td v-if="targetSets[selectedTargetSet]">
- <time-input v-model="targetSets[selectedTargetSet].targets[index].split"
- label="Split duration" :showHours="false"/>
- </td>
-
- <td>
- {{ formatUtils.formatDuration(item.pace, 3, 0, true) }}
- / {{ unitUtils.DISTANCE_UNITS[unitUtils.getDefaultDistanceUnit(defaultUnitSystem)]
- .symbol }}
- </td>
- </tr>
-
- <tr v-if="!targetSets[selectedTargetSet] || targetSets[selectedTargetSet].targets.length === 0" class="empty-message">
- <td colspan="5">
- There aren't any targets in this set yet.
- </td>
- </tr>
- </tbody>
- </table>
+ <split-output-table :default-unit-system="defaultUnitSystem" v-model="targetSet"/>
</div>
</div>
</template>
@@ -68,12 +23,11 @@
<script setup>
import { computed } from 'vue';
-import formatUtils from '@/utils/format';
import targetUtils from '@/utils/targets';
import unitUtils from '@/utils/units';
+import SplitOutputTable from '@/components/SplitOutputTable.vue';
import TargetSetSelector from '@/components/TargetSetSelector.vue';
-import TimeInput from '@/components/TimeInput.vue';
import useStorage from '@/composables/useStorage';
@@ -95,46 +49,21 @@ const targetSets = useStorage('split-calculator-target-sets', {
});
/**
- * The target table results
+ * The active target set
*/
-const results = computed(() => {
- // Initialize results array
- const results = [];
-
- // Check for missing target set
- if (!targetSets.value[selectedTargetSet.value]) return [];
-
- let targets = targetSets.value[selectedTargetSet.value].targets;
-
- for (let i = 0; i < targets.length; i += 1) {
- // Calculate split and total times
- const splitTime = targets[i].split || 0;
- const totalTime = i === 0 ? splitTime : results[i - 1].totalTime + splitTime;
-
- // Calculate split and total distances
- const totalDistance = unitUtils.convertDistance(
- targets[i].distanceValue,
- targets[i].distanceUnit, 'meters',
- );
- const splitDistance = i === 0 ? totalDistance : totalDistance - results[i - 1].distance;
-
- // Calculate pace
- const pace = splitTime / unitUtils.convertDistance(splitDistance, 'meters',
- unitUtils.getDefaultDistanceUnit(defaultUnitSystem.value));
-
- // Add row to results array
- results.push({
- distance: totalDistance,
- distanceValue: targets[i].distanceValue,
- distanceUnit: targets[i].distanceUnit,
- totalTime,
- splitTime,
- pace,
- });
- }
-
- // Return results array
- return results;
+const targetSet = computed({
+ get: () => {
+ if (targetSets.value[selectedTargetSet.value]) {
+ return targetSets.value[selectedTargetSet.value].targets
+ } else {
+ return []
+ }
+ },
+ set: (newValue) => {
+ if (targetSets.value[selectedTargetSet.value]) {
+ targetSets.value[selectedTargetSet.value].targets = newValue;
+ }
+ },
});
</script>
@@ -151,17 +80,4 @@ const results = computed(() => {
min-width: 400px;
}
}
-
-/* Show/hide mobile abbreviations */
-.results th:first-child span.mobile-abbreviation {
- display: none;
-}
-@media only screen and (max-width: 500px) {
- .results th:first-child span:not(.mobile-abbreviation) {
- display: none;
- }
- .results th:first-child span.mobile-abbreviation {
- display: inherit;
- }
-}
</style>
diff --git a/tests/unit/components/SplitOutputTable.spec.js b/tests/unit/components/SplitOutputTable.spec.js
@@ -0,0 +1,157 @@
+import { test, expect } from 'vitest';
+import { shallowMount } from '@vue/test-utils';
+import SplitOutputTable from '@/components/SplitOutputTable.vue';
+
+test('should initialize undefined splits to 0:00.00', async () => {
+ // Initialize component
+ const wrapper = shallowMount(SplitOutputTable, {
+ propsData: {
+ modelValue: [
+ { result: 'time', distanceValue: 1, distanceUnit: 'miles' },
+ { result: 'time', distanceValue: 2, distanceUnit: 'miles' },
+ { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' },
+ ],
+ },
+ });
+
+ // Assert results are correct
+ const rows = wrapper.findAll('tbody tr');
+ expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 mi');
+ expect(rows[0].findAll('td')[1].element.textContent).to.equal('0:00.00');
+ expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(0);
+ expect(rows[0].findAll('td').length).to.equal(4);
+ expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 mi');
+ expect(rows[1].findAll('td')[1].element.textContent).to.equal('0:00.00');
+ expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(0);
+ expect(rows[1].findAll('td').length).to.equal(4);
+ expect(rows[2].findAll('td')[0].element.textContent).to.equal('5 km');
+ expect(rows[2].findAll('td')[1].element.textContent).to.equal('0:00.00');
+ expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(0);
+ expect(rows[2].findAll('td').length).to.equal(4);
+ expect(rows.length).to.equal(3);
+});
+
+test('should correctly load split times from split targets', async () => {
+ // Initialize component
+ const wrapper = shallowMount(SplitOutputTable, {
+ propsData: {
+ modelValue: [
+ { result: 'time', distanceValue: 1, distanceUnit: 'kilometers', splitTime: 180 },
+ { result: 'time', distanceValue: 2, distanceUnit: 'kilometers', splitTime: 190 },
+ { result: 'time', distanceValue: 3000, distanceUnit: 'meters', splitTime: 200 },
+ ],
+ },
+ });
+
+ // Assert results are correct
+ const rows = wrapper.findAll('tbody tr');
+ expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 km');
+ expect(rows[0].findAll('td')[1].element.textContent).to.equal('3:00.00');
+ expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue)
+ .to.equal(180);
+ expect(rows[0].findAll('td').length).to.equal(4);
+ expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 km');
+ expect(rows[1].findAll('td')[1].element.textContent).to.equal('6:10.00');
+ expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue)
+ .to.equal(190);
+ expect(rows[1].findAll('td').length).to.equal(4);
+ expect(rows[2].findAll('td')[0].element.textContent).to.equal('3000 m');
+ expect(rows[2].findAll('td')[1].element.textContent).to.equal('9:30.00');
+ expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue)
+ .to.equal(200);
+ expect(rows[2].findAll('td').length).to.equal(4);
+ expect(rows.length).to.equal(3);
+});
+
+test('should correctly calculate paces and cumulative times from entered split times', async () => {
+ // Initialize component
+ const wrapper = shallowMount(SplitOutputTable, {
+ propsData: {
+ modelValue: [
+ { result: 'time', distanceValue: 1, distanceUnit: 'miles' },
+ { result: 'time', distanceValue: 2, distanceUnit: 'miles' },
+ { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' },
+ ],
+ },
+ });
+
+ // Update split times
+ await wrapper.findAllComponents({ name: 'time-input' })[0].setValue(420);
+ await wrapper.findAllComponents({ name: 'time-input' })[1].setValue(390);
+ await wrapper.findAllComponents({ name: 'time-input' })[2].setValue(390);
+
+ // Assert results are correct
+ const rows = wrapper.findAll('tbody tr');
+ expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 mi');
+ expect(rows[0].findAll('td')[1].element.textContent).to.equal('7:00.00');
+ expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' })
+ .vm.modelValue).to.equal(420);
+ expect(rows[0].findAll('td')[3].element.textContent).to.equal('4:21 / km');
+ expect(rows[0].findAll('td').length).to.equal(4);
+ expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 mi');
+ expect(rows[1].findAll('td')[1].element.textContent).to.equal('13:30.00');
+ expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' })
+ .vm.modelValue).to.equal(390);
+ expect(rows[1].findAll('td')[3].element.textContent).to.equal('4:02 / km');
+ expect(rows[1].findAll('td').length).to.equal(4);
+ expect(rows[2].findAll('td')[0].element.textContent).to.equal('5 km');
+ expect(rows[2].findAll('td')[1].element.textContent).to.equal('20:00.00');
+ expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' })
+ .vm.modelValue).to.equal(390);
+ expect(rows[2].findAll('td')[3].element.textContent).to.equal('3:39 / km');
+ expect(rows[2].findAll('td').length).to.equal(4);
+ expect(rows.length).to.equal(3);
+});
+
+test('should correctly update modelValue with split times', async () => {
+ // Initialize component
+ const wrapper = shallowMount(SplitOutputTable, {
+ propsData: {
+ modelValue: [
+ { result: 'time', distanceValue: 1, distanceUnit: 'kilometers', splitTime: 180 },
+ { result: 'time', distanceValue: 2, distanceUnit: 'kilometers', splitTime: 180 },
+ { result: 'time', distanceValue: 3000, distanceUnit: 'meters', splitTime: 180 },
+ ],
+ },
+ });
+
+ // Update split times
+ await wrapper.findAllComponents({ name: 'time-input' })[1].setValue(190);
+ await wrapper.findAllComponents({ name: 'time-input' })[2].setValue(200);
+
+ // Assert modelValue correctly updated
+ expect(wrapper.vm.modelValue).to.deep.equal([
+ { result: 'time', distanceValue: 1, distanceUnit: 'kilometers', splitTime: 180 },
+ { result: 'time', distanceValue: 2, distanceUnit: 'kilometers', splitTime: 190 },
+ { result: 'time', distanceValue: 3000, distanceUnit: 'meters', splitTime: 200 },
+ ]);
+});
+
+test('should update paces according to default units setting', async () => {
+ // Initialize component
+ const wrapper = shallowMount(SplitOutputTable, {
+ propsData: {
+ modelValue: [
+ { result: 'time', distanceValue: 1, distanceUnit: 'miles', splitTime: 300 },
+ { result: 'time', distanceValue: 2, distanceUnit: 'miles', splitTime: 300 },
+ { result: 'time', distanceValue: 5, distanceUnit: 'kilometers', splitTime: 330 },
+ ],
+ defaultUnitSystem: 'metric',
+ }
+ });
+
+ // Assert paces are correct
+ let rows = wrapper.findAll('tbody tr');
+ expect(rows[0].findAll('td')[3].element.textContent).to.equal('3:06 / km');
+ expect(rows[1].findAll('td')[3].element.textContent).to.equal('3:06 / km');
+ expect(rows[2].findAll('td')[3].element.textContent).to.equal('3:05 / km');
+
+ // Change default units
+ await wrapper.setProps({ defaultUnitSystem: 'imperial' });
+
+ // Assert paces are correct
+ rows = wrapper.findAll('tbody tr');
+ expect(rows[0].findAll('td')[3].element.textContent).to.equal('5:00 / mi');
+ expect(rows[1].findAll('td')[3].element.textContent).to.equal('5:00 / mi');
+ expect(rows[2].findAll('td')[3].element.textContent).to.equal('4:58 / mi');
+});
diff --git a/tests/unit/views/SplitCalculator.spec.js b/tests/unit/views/SplitCalculator.spec.js
@@ -6,36 +6,31 @@ beforeEach(() => {
localStorage.clear();
})
-test('should initialize undefined splits to 0:00.00', async () => {
+test('should load selected target set from localStorage', async () => {
+ // Initialize localStorage
+ localStorage.setItem('running-tools.split-calculator-target-set', '"B"');
+
// Initialize component
const wrapper = shallowMount(SplitCalculator);
- // Assert results are correct
- const rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 mi');
- expect(rows[0].findAll('td')[1].element.textContent).to.equal('0:00.00');
- expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(0);
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('0:00 / mi');
- expect(rows[0].findAll('td').length).to.equal(4);
- expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 mi');
- expect(rows[1].findAll('td')[1].element.textContent).to.equal('0:00.00');
- expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(0);
- expect(rows[1].findAll('td')[3].element.textContent).to.equal('0:00 / mi');
- expect(rows[1].findAll('td').length).to.equal(4);
- expect(rows[2].findAll('td')[0].element.textContent).to.equal('5 km');
- expect(rows[2].findAll('td')[1].element.textContent).to.equal('0:00.00');
- expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(0);
- expect(rows[2].findAll('td')[3].element.textContent).to.equal('0:00 / mi');
- expect(rows[2].findAll('td').length).to.equal(4);
- expect(rows.length).to.equal(3);
+ // Assert selection is loaded
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('B');
});
-test('should correctly load split times from split targets', async () => {
+test('should load targets from localStorage and pass to splitOutputTable', async () => {
// Initialize localStorage
localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({
'_split_targets': {
name: 'Split targets',
targets: [
+ { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
+ ],
+ },
+ 'B': {
+ name: 'Split targets #2',
+ targets: [
{ type: 'distance', distanceValue: 1, distanceUnit: 'kilometers', split: 180 },
{ type: 'distance', distanceValue: 2, distanceUnit: 'kilometers', split: 190 },
{ type: 'distance', distanceValue: 3000, distanceUnit: 'meters', split: 200 },
@@ -46,27 +41,26 @@ test('should correctly load split times from split targets', async () => {
// Initialize component
const wrapper = shallowMount(SplitCalculator);
- // Assert results are correct
- const rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 km');
- expect(rows[0].findAll('td')[1].element.textContent).to.equal('3:00.00');
- expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue)
- .to.equal(180);
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('4:50 / mi');
- expect(rows[0].findAll('td').length).to.equal(4);
- expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 km');
- expect(rows[1].findAll('td')[1].element.textContent).to.equal('6:10.00');
- expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue)
- .to.equal(190);
- expect(rows[1].findAll('td')[3].element.textContent).to.equal('5:06 / mi');
- expect(rows[1].findAll('td').length).to.equal(4);
- expect(rows[2].findAll('td')[0].element.textContent).to.equal('3000 m');
- expect(rows[2].findAll('td')[1].element.textContent).to.equal('9:30.00');
- expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue)
- .to.equal(200);
- expect(rows[2].findAll('td')[3].element.textContent).to.equal('5:22 / mi');
- expect(rows[2].findAll('td').length).to.equal(4);
- expect(rows.length).to.equal(3);
+ // Assert default split targets are initially loaded
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
+ .to.equal('_split_targets');
+ expect(wrapper.findComponent({ name: 'split-output-table' }).vm.modelValue).to.deep.equal([
+ { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
+ ]);
+
+ // Select a new target set
+ await wrapper.findComponent({ name: 'target-set-selector' }).setValue('B', 'selectedTargetSet');
+
+ // Assert new target set is loaded
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
+ .to.equal('B');
+ expect(wrapper.findComponent({ name: 'split-output-table' }).vm.modelValue).to.deep.equal([
+ { type: 'distance', distanceValue: 1, distanceUnit: 'kilometers', split: 180 },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'kilometers', split: 190 },
+ { type: 'distance', distanceValue: 3000, distanceUnit: 'meters', split: 200 },
+ ]);
});
test('should correctly handle null target set', async () => {
@@ -76,59 +70,25 @@ test('should correctly handle null target set', async () => {
// Initialize component
const wrapper = shallowMount(SplitCalculator);
- // Assert results are empty
- let rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[0].element.textContent.trim())
- .to.equal('There aren\'t any targets in this set yet.');
- expect(rows[0].findAll('td').length).to.equal(1);
- expect(rows.length).to.equal(1);
+ // Assert selection is loaded
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('does_not_exist');
+
+ // Assert empty array passed to SplitOutputTable
+ expect(wrapper.findComponent({ name: 'split-output-table' }).vm.modelValue).to.deep.equal([]);
// Switch to valid target set
await wrapper.findComponent({ name: 'target-set-selector' })
.setValue('_split_targets', 'selectedTargetSet');
- // Assert results are correct
- rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 mi');
- expect(rows[0].findAll('td')[1].element.textContent).to.equal('0:00.00');
- expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(0);
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('0:00 / mi');
- expect(rows.length).to.equal(3);
-});
-
-test('should correctly calculate paces and cumulative times from entered split times', async () => {
- // Initialize component
- const wrapper = shallowMount(SplitCalculator);
-
- // Update split times
- await wrapper.findAllComponents({ name: 'time-input' })[0].setValue(420);
- await wrapper.findAllComponents({ name: 'time-input' })[1].setValue(390);
- await wrapper.findAllComponents({ name: 'time-input' })[2].setValue(390);
-
- // Assert results are correct
- const rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 mi');
- expect(rows[0].findAll('td')[1].element.textContent).to.equal('7:00.00');
- expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(420);
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('7:00 / mi');
- expect(rows[0].findAll('td').length).to.equal(4);
- expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 mi');
- expect(rows[1].findAll('td')[1].element.textContent).to.equal('13:30.00');
- expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(390);
- expect(rows[1].findAll('td')[3].element.textContent).to.equal('6:30 / mi');
- expect(rows[1].findAll('td').length).to.equal(4);
- expect(rows[2].findAll('td')[0].element.textContent).to.equal('5 km');
- expect(rows[2].findAll('td')[1].element.textContent).to.equal('20:00.00');
- expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(390);
- expect(rows[2].findAll('td')[3].element.textContent).to.equal('5:52 / mi');
- expect(rows[2].findAll('td').length).to.equal(4);
- expect(rows.length).to.equal(3);
+ // Assert non-empty target set passed to SplitOutputTable
+ expect(wrapper.findComponent({ name: 'split-output-table' }).vm.modelValue).to.deep.equal([
+ { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
+ { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
+ ]);
});
-test('should correctly save split times with split targets in localStorage', async () => {
+test('should update targets in localStorage when modified by splitOutputTable', async () => {
// Initialize localStorage
localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({
'_split_targets': {
@@ -145,8 +105,11 @@ test('should correctly save split times with split targets in localStorage', asy
const wrapper = shallowMount(SplitCalculator);
// Update split times
- await wrapper.findAllComponents({ name: 'time-input' })[1].setValue(190);
- await wrapper.findAllComponents({ name: 'time-input' })[2].setValue(200);
+ await wrapper.findComponent({ name: 'split-output-table' }).setValue([
+ { type: 'distance', distanceValue: 1, distanceUnit: 'kilometers', split: 180 },
+ { type: 'distance', distanceValue: 2, distanceUnit: 'kilometers', split: 190 },
+ { type: 'distance', distanceValue: 3000, distanceUnit: 'meters', split: 200 },
+ ]);
// Assert targets correctly saved in localStorage
expect(localStorage.getItem('running-tools.split-calculator-target-sets')).to.equal(JSON.stringify({
@@ -161,113 +124,6 @@ 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.split-calculator-target-sets', JSON.stringify({
- '_split_targets': {
- name: 'Split targets',
- targets: [
- { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
- ],
- },
- 'B': {
- name: 'Split targets #2',
- targets: [
- { type: 'distance', distanceValue: 1, distanceUnit: 'kilometers', split: 180 },
- { type: 'distance', distanceValue: 2, distanceUnit: 'kilometers', split: 190 },
- { type: 'distance', distanceValue: 3000, distanceUnit: 'meters', split: 200 },
- ],
- },
- }));
-
- // Initialize component
- const wrapper = shallowMount(SplitCalculator);
-
- // Assert default split targets are initially loaded
- expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet)
- .to.equal('_split_targets');
- expect(wrapper.findAll('tbody td')[0].element.textContent).to.equal('1 mi');
-
- // Select a new target set
- await wrapper.findComponent({ name: 'target-set-selector' }).setValue('B', 'selectedTargetSet');
-
- // Assert results are correct
- const rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 km');
- expect(rows[0].findAll('td')[1].element.textContent).to.equal('3:00.00');
- expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(180);
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('4:50 / mi');
- expect(rows[0].findAll('td').length).to.equal(4);
- expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 km');
- expect(rows[1].findAll('td')[1].element.textContent).to.equal('6:10.00');
- expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(190);
- expect(rows[1].findAll('td')[3].element.textContent).to.equal('5:06 / mi');
- expect(rows[1].findAll('td').length).to.equal(4);
- expect(rows[2].findAll('td')[0].element.textContent).to.equal('3000 m');
- expect(rows[2].findAll('td')[1].element.textContent).to.equal('9:30.00');
- expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(200);
- expect(rows[2].findAll('td')[3].element.textContent).to.equal('5:22 / mi');
- expect(rows[2].findAll('td').length).to.equal(4);
- expect(rows.length).to.equal(3);
-});
-
-test('should load selected target set from localStorage', async () => {
- // Initialize localStorage
- localStorage.setItem('running-tools.split-calculator-target-set', '"B"');
- localStorage.setItem('running-tools.split-calculator-target-sets', JSON.stringify({
- '_split_targets': {
- name: 'Split targets',
- targets: [
- { type: 'distance', distanceValue: 1, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 2, distanceUnit: 'miles' },
- { type: 'distance', distanceValue: 5, distanceUnit: 'kilometers' },
- ],
- },
- 'B': {
- name: 'Split targets #2',
- targets: [
- { type: 'distance', distanceValue: 1, distanceUnit: 'kilometers', split: 180 },
- { type: 'distance', distanceValue: 2, distanceUnit: 'kilometers', split: 190 },
- { type: 'distance', distanceValue: 3000, distanceUnit: 'meters', split: 200 },
- ],
- },
- }));
-
-
- // Initialize component
- const wrapper = shallowMount(SplitCalculator);
-
- // Assert selection is loaded
- expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.selectedTargetSet).to.equal('B');
-
- // Assert results are correct
- const rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[0].element.textContent).to.equal('1 km');
- expect(rows[0].findAll('td')[1].element.textContent).to.equal('3:00.00');
- expect(rows[0].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(180);
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('4:50 / mi');
- expect(rows[0].findAll('td').length).to.equal(4);
- expect(rows[1].findAll('td')[0].element.textContent).to.equal('2 km');
- expect(rows[1].findAll('td')[1].element.textContent).to.equal('6:10.00');
- expect(rows[1].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(190);
- expect(rows[1].findAll('td')[3].element.textContent).to.equal('5:06 / mi');
- expect(rows[1].findAll('td').length).to.equal(4);
- expect(rows[2].findAll('td')[0].element.textContent).to.equal('3000 m');
- expect(rows[2].findAll('td')[1].element.textContent).to.equal('9:30.00');
- expect(rows[2].findAll('td')[2].findComponent({ name: 'time-input' })
- .vm.modelValue).to.equal(200);
- expect(rows[2].findAll('td')[3].element.textContent).to.equal('5:22 / mi');
- expect(rows[2].findAll('td').length).to.equal(4);
- expect(rows.length).to.equal(3);
-});
-
test('should save selected target set to localStorage when modified', async () => {
// Initialize component
const wrapper = shallowMount(SplitCalculator);
@@ -281,41 +137,49 @@ test('should save selected target set to localStorage when modified', async () =
.to.equal('"_race_targets"');
});
-test('should update paces according to default units setting', async () => {
+test('should load default units from localStorage and pass to splitOutputTable', async () => {
+ // Initialize localStorage
+ localStorage.setItem('running-tools.default-unit-system', '"metric"');
+
// Initialize component
const wrapper = shallowMount(SplitCalculator);
- // Enter split times
- await wrapper.findAllComponents({ name: 'time-input' })[0].setValue(300);
- await wrapper.findAllComponents({ name: 'time-input' })[1].setValue(300);
- await wrapper.findAllComponents({ name: 'time-input' })[2].setValue(330);
+ // Assert default units setting is initialy loaded
+ expect(wrapper.find('select', { name: 'Default units' }).element.value).to.equal('metric');
- // Set default units setting
- await wrapper.find('select', { name: 'Default units' }).setValue('metric');
-
- // Assert paces are correct
- let rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('3:06 / km');
- expect(rows[1].findAll('td')[3].element.textContent).to.equal('3:06 / km');
- expect(rows[2].findAll('td')[3].element.textContent).to.equal('3:05 / km');
+ // Assert prop is correct
+ expect(wrapper.findComponent({ name: 'split-output-table' }).vm.defaultUnitSystem)
+ .to.equal('metric');
// Change default units
await wrapper.find('select').setValue('imperial');
- // Assert paces are correct
- rows = wrapper.findAll('tbody tr');
- expect(rows[0].findAll('td')[3].element.textContent).to.equal('5:00 / mi');
- expect(rows[1].findAll('td')[3].element.textContent).to.equal('5:00 / mi');
- expect(rows[2].findAll('td')[3].element.textContent).to.equal('4:58 / mi');
+ // Assert prop is correct
+ expect(wrapper.findComponent({ name: 'split-output-table' }).vm.defaultUnitSystem)
+ .to.equal('imperial');
});
test('should save default units setting to localStorage when modified', async () => {
// Initialize component
const wrapper = shallowMount(SplitCalculator);
- // Change default units
+ // Set default units setting
+ await wrapper.find('select').setValue('metric');
+
+ // New default units should be saved to localStorage
+ expect(localStorage.getItem('running-tools.default-unit-system')).to.equal('"metric"');
+
+ // Set default units setting
await wrapper.find('select').setValue('imperial');
// New default units should be saved to localStorage
expect(localStorage.getItem('running-tools.default-unit-system')).to.equal('"imperial"');
});
+
+test('should correctly set targetSetSelector setType prop', async () => {
+ // Initialize component
+ const wrapper = shallowMount(SplitCalculator);
+
+ // Assert setType prop is correctly set
+ expect(wrapper.findComponent({ name: 'target-set-selector' }).vm.setType).to.equal('split');
+});