running-tools

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

commit 3d696cb56c466a5006ae98ca8537bc7b70d719e2
parent 01d69427eed244b3c3ff18627f39662902f6052e
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Sat, 16 Mar 2024 13:22:29 -0700

Improve component tests

Diffstat:
Msrc/components/TargetEditor.vue | 10+---------
Mtests/unit/components/SimpleTargetTable.spec.js | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mtests/unit/components/TargetEditor.spec.js | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtests/unit/components/TargetSetSelector.spec.js | 375+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
4 files changed, 427 insertions(+), 171 deletions(-)

diff --git a/src/components/TargetEditor.vue b/src/components/TargetEditor.vue @@ -56,7 +56,7 @@ <button title="Add distance target" @click="addDistanceTarget"> Add distance target </button> - <button v-if="timeTargets" title="Add time target" @click="addTimeTarget"> + <button title="Add time target" @click="addTimeTarget"> Add time target </button> <br/> @@ -95,14 +95,6 @@ export default { }, /** - * Whether to allow the user to add time based targets - */ - timeTargets: { - type: Boolean, - default: true, - }, - - /** * Whether the target set is a custom or default set */ isCustomSet: { diff --git a/tests/unit/components/SimpleTargetTable.spec.js b/tests/unit/components/SimpleTargetTable.spec.js @@ -1,5 +1,5 @@ import { test, expect } from 'vitest'; -import { mount, shallowMount } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; import SimpleTargetTable from '@/components/SimpleTargetTable.vue'; test('results should be correct and sorted by time', () => { @@ -7,65 +7,117 @@ test('results should be correct and sorted by time', () => { const wrapper = shallowMount(SimpleTargetTable, { propsData: { calculateResult: (row) => ({ - distanceValue: row.distanceValue ? row.distanceValue : row.time + 1, + distanceValue: row.distanceValue ? row.distanceValue : row.time / 300, distanceUnit: row.distanceUnit ? row.distanceUnit : 'miles', - time: row.time ? row.time : row.distanceValue + 1, + time: row.time ? row.time : row.distanceValue * 300, result: row.result, }), targets: [ - { result: 'time', distanceValue: 20, distanceUnit: 'meters' }, - { result: 'time', distanceValue: 100, distanceUnit: 'meters' }, - { result: 'time', distanceValue: 1, distanceUnit: 'kilometers' }, - { result: 'time', distanceValue: 10, distanceUnit: 'meters' }, - { result: 'distance', time: 1 }, - { result: 'distance', time: 10 }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + { result: 'distance', time: 1230 }, ], }, }); - // Assert results are correct - expect(wrapper.vm.results).to.deep.equal([ - { result: 'distance', distanceValue: 2, distanceUnit: 'miles', time: 1 }, - { result: 'time', distanceValue: 1, distanceUnit: 'kilometers', time: 2 }, - { result: 'distance', distanceValue: 11, distanceUnit: 'miles', time: 10 }, - { result: 'time', distanceValue: 10, distanceUnit: 'meters', time: 11 }, - { result: 'time', distanceValue: 20, distanceUnit: 'meters', time: 21 }, - { result: 'time', distanceValue: 100, distanceUnit: 'meters', time: 101 }, - ]); + // Assert results are correctly rendered + 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('5:00.00'); + expect(rows[0].findAll('td').length).to.equal(2); + expect(rows[1].findAll('td')[0].element.textContent).to.equal('3 mi'); + expect(rows[1].findAll('td')[1].element.textContent).to.equal('15:00.00'); + expect(rows[1].findAll('td').length).to.equal(2); + expect(rows[2].findAll('td')[0].element.textContent).to.equal('4.10 mi'); + expect(rows[2].findAll('td')[1].element.textContent).to.equal('20:30'); + expect(rows[2].findAll('td').length).to.equal(2); + expect(rows[3].findAll('td')[0].element.textContent).to.equal('5 km'); + expect(rows[3].findAll('td')[1].element.textContent).to.equal('25:00.00'); + expect(rows[3].findAll('td').length).to.equal(2); + expect(rows.length).to.equal(4); }); -test('getPace should return correct imerial paces', () => { +test('should show correct imperial paces when showPace is true', () => { // Initialize component - const wrapper = mount(SimpleTargetTable, { + const wrapper = shallowMount(SimpleTargetTable, { propsData: { - calculateResult: () => {}, + calculateResult: (row) => ({ + distanceValue: row.distanceValue ? row.distanceValue : row.time / 300, + distanceUnit: row.distanceUnit ? row.distanceUnit : 'miles', + time: row.time ? row.time : row.distanceValue * 300, + result: row.result, + }), + targets: [ + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + { result: 'distance', time: 1230 }, + ], defaultUnitSystem: 'imperial', + showPace: true, }, }); - // Assert paces are correct - const result = wrapper.vm.getPace({ - distanceValue: 1, - distanceUnit: 'kilometers', - time: 300, - }); - expect(result).to.be.closeTo(482.81, 0.01); + // Assert results are correctly rendered + 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('5:00.00'); + expect(rows[0].findAll('td')[2].element.textContent).to.equal('5:00 / mi'); + expect(rows[0].findAll('td').length).to.equal(3); + expect(rows[1].findAll('td')[0].element.textContent).to.equal('3 mi'); + expect(rows[1].findAll('td')[1].element.textContent).to.equal('15:00.00'); + expect(rows[1].findAll('td')[2].element.textContent).to.equal('5:00 / mi'); + expect(rows[1].findAll('td').length).to.equal(3); + expect(rows[2].findAll('td')[0].element.textContent).to.equal('4.10 mi'); + expect(rows[2].findAll('td')[1].element.textContent).to.equal('20:30'); + expect(rows[2].findAll('td')[2].element.textContent).to.equal('5:00 / mi'); + expect(rows[2].findAll('td').length).to.equal(3); + expect(rows[3].findAll('td')[0].element.textContent).to.equal('5 km'); + expect(rows[3].findAll('td')[1].element.textContent).to.equal('25:00.00'); + expect(rows[3].findAll('td')[2].element.textContent).to.equal('8:03 / mi'); + expect(rows[3].findAll('td').length).to.equal(3); + expect(rows.length).to.equal(4); }); -test('getPace should return correct metric paces', () => { +test('should show correct metric paces when showPace is true', () => { // Initialize component - const wrapper = mount(SimpleTargetTable, { + const wrapper = shallowMount(SimpleTargetTable, { propsData: { - calculateResult: () => {}, + calculateResult: (row) => ({ + distanceValue: row.distanceValue ? row.distanceValue : row.time / 300, + distanceUnit: row.distanceUnit ? row.distanceUnit : 'miles', + time: row.time ? row.time : row.distanceValue * 300, + result: row.result, + }), + targets: [ + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + { result: 'distance', time: 1230 }, + ], defaultUnitSystem: 'metric', + showPace: true, }, }); - // Assert paces are correct - const result = wrapper.vm.getPace({ - distanceValue: 1, - distanceUnit: 'miles', - time: 600, - }); - expect(result).to.be.closeTo(372.82, 0.01); + // Assert results are correctly rendered + 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('5:00.00'); + expect(rows[0].findAll('td')[2].element.textContent).to.equal('3:06 / km'); + expect(rows[0].findAll('td').length).to.equal(3); + expect(rows[1].findAll('td')[0].element.textContent).to.equal('3 mi'); + expect(rows[1].findAll('td')[1].element.textContent).to.equal('15:00.00'); + expect(rows[1].findAll('td')[2].element.textContent).to.equal('3:06 / km'); + expect(rows[1].findAll('td').length).to.equal(3); + expect(rows[2].findAll('td')[0].element.textContent).to.equal('4.10 mi'); + expect(rows[2].findAll('td')[1].element.textContent).to.equal('20:30'); + expect(rows[2].findAll('td')[2].element.textContent).to.equal('3:06 / km'); + expect(rows[2].findAll('td').length).to.equal(3); + expect(rows[3].findAll('td')[0].element.textContent).to.equal('5 km'); + expect(rows[3].findAll('td')[1].element.textContent).to.equal('25:00.00'); + expect(rows[3].findAll('td')[2].element.textContent).to.equal('5:00 / km'); + expect(rows[3].findAll('td').length).to.equal(3); + expect(rows.length).to.equal(4); }); diff --git a/tests/unit/components/TargetEditor.spec.js b/tests/unit/components/TargetEditor.spec.js @@ -1,32 +1,73 @@ import { test, expect } from 'vitest'; -import { shallowMount, mount } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; import TargetEditor from '@/components/TargetEditor.vue'; -test('revert method should emit revert event', async () => { +test('should correctly render target set', async () => { + // Initialize component + const wrapper = shallowMount(TargetEditor, { + propsData: { + modelValue: { + name: 'My target set', + targets: [ + { distanceUnit: 'kilometers', distanceValue: 1.61, result: 'time' }, + { distanceUnit: 'miles', distanceValue: 3.11, result: 'time' }, + { time: 600, result: 'distance' }, + ], + }, + }, + }); + + // Assert target set correctly rendered + expect(wrapper.find('input').element.value).to.equal('My target set'); + const rows = wrapper.findAll('tbody tr'); + expect(rows[0].findComponent({ name: 'decimal-input' }).vm.modelValue).to.equal(1.61); + expect(rows[0].find('select').element.value).to.equal('kilometers'); + expect(rows[1].findComponent({ name: 'decimal-input' }).vm.modelValue).to.equal(3.11); + expect(rows[1].find('select').element.value).to.equal('miles'); + expect(rows[2].findComponent({ name: 'time-input' }).vm.modelValue).to.equal(600); + expect(rows.length).to.equal(3); +}); + +test('revert button should emit revert event', async () => { // Initialize component const wrapper = shallowMount(TargetEditor); - // Call revert method - await wrapper.vm.revert(); + // Click revert button + await wrapper.find('button[title="Revert target set"]').trigger('click'); + + // Assert revert event was emitted + expect(wrapper.emitted().revert.length).to.equal(1); +}); + +test('delete button should emit revert event', async () => { + // Initialize component + const wrapper = shallowMount(TargetEditor, { + propsData: { + isCustomSet: true, + } + }); + + // Click delete button + await wrapper.find('button[title="Delete target set"]').trigger('click'); // Assert revert event was emitted expect(wrapper.emitted().revert.length).to.equal(1); }); -test('close method should emit close event', async () => { +test('close button should emit close event', async () => { // Initialize component const wrapper = shallowMount(TargetEditor); // Call close method - await wrapper.vm.close(); + await wrapper.find('button[title="Close"]').trigger('click'); // Assert close event was emitted expect(wrapper.emitted().close.length).to.equal(1); }); -test('addDistanceTarget method should correctly add imperial distance target', async () => { +test('add distance target button should correctly add imperial distance target', async () => { // Initialize component - const wrapper = mount(TargetEditor, { + const wrapper = shallowMount(TargetEditor, { propsData: { modelValue: { name: 'My target set', @@ -40,7 +81,7 @@ test('addDistanceTarget method should correctly add imperial distance target', a }); // Add distance target - await wrapper.vm.addDistanceTarget(); + await wrapper.find('button[title="Add distance target"]').trigger('click'); // Assert input event was emitted expect(wrapper.emitted()['update:modelValue']).to.deep.equal([ @@ -55,9 +96,9 @@ test('addDistanceTarget method should correctly add imperial distance target', a ]); }); -test('addDistanceTarget method should correctly add metric distance target', async () => { +test('add distance target button should correctly add metric distance target', async () => { // Initialize component - const wrapper = mount(TargetEditor, { + const wrapper = shallowMount(TargetEditor, { propsData: { modelValue: { name: 'My target set', @@ -71,7 +112,7 @@ test('addDistanceTarget method should correctly add metric distance target', asy }); // Add distance target - await wrapper.vm.addDistanceTarget(); + await wrapper.find('button[title="Add distance target"]').trigger('click'); // Assert input event was emitted expect(wrapper.emitted()['update:modelValue']).to.deep.equal([ @@ -86,9 +127,9 @@ test('addDistanceTarget method should correctly add metric distance target', asy ]); }); -test('addTimeTarget method should correctly add time target', async () => { +test('add time target button should correctly add time target', async () => { // Initialize component - const wrapper = mount(TargetEditor, { + const wrapper = shallowMount(TargetEditor, { propsData: { modelValue: { name: 'My target set', @@ -101,7 +142,7 @@ test('addTimeTarget method should correctly add time target', async () => { }); // Add time target - await wrapper.vm.addTimeTarget(); + await wrapper.find('button[title="Add time target"]').trigger('click'); // Assert input event was emitted expect(wrapper.emitted()['update:modelValue']).to.deep.equal([ @@ -117,7 +158,7 @@ test('addTimeTarget method should correctly add time target', async () => { test('Should emit input event when targets are updated', async () => { // Initialize component - const wrapper = mount(TargetEditor, { + const wrapper = shallowMount(TargetEditor, { propsData: { modelValue: { name: 'My target set', @@ -129,8 +170,7 @@ test('Should emit input event when targets are updated', async () => { }); // Update distance value - wrapper.vm.internalValue.targets[0].distanceValue = 3; - await wrapper.vm.$nextTick(); + await wrapper.findComponent({ name: 'decimal-input' }).setValue(3); // Assert input event was emitted expect(wrapper.emitted()['update:modelValue']).to.deep.equal([ @@ -147,7 +187,7 @@ test('Should emit input event when targets are updated', async () => { test('Should emit input event when target set name is updated', async () => { // Initialize component - const wrapper = mount(TargetEditor, { + const wrapper = shallowMount(TargetEditor, { propsData: { modelValue: { name: 'My target set', @@ -159,8 +199,7 @@ test('Should emit input event when target set name is updated', async () => { }); // Update distance value - wrapper.vm.internalValue.name = 'My target set #2'; - await wrapper.vm.$nextTick(); + await wrapper.find('input').setValue('My target set #2'); // Assert input event was emitted expect(wrapper.emitted()['update:modelValue']).to.deep.equal([ @@ -175,7 +214,7 @@ test('Should emit input event when target set name is updated', async () => { ]); }); -test('removeTarget method should correctly remove target', async () => { +test('removeTarget button should correctly remove target', async () => { // Initialize component const wrapper = shallowMount(TargetEditor, { propsData: { @@ -191,7 +230,7 @@ test('removeTarget method should correctly remove target', async () => { }); // Remove 2nd target - await wrapper.vm.removeTarget(1); + await wrapper.findAll('button[title="Remove target"]')[1].trigger('click'); // Assert input event was emitted expect(wrapper.emitted()['update:modelValue']).to.deep.equal([ diff --git a/tests/unit/components/TargetSetSelector.spec.js b/tests/unit/components/TargetSetSelector.spec.js @@ -1,102 +1,215 @@ -import { test, expect } from 'vitest'; -import { mount } from '@vue/test-utils'; +import { beforeEach, test, expect, vi } from 'vitest'; +import { shallowMount } from '@vue/test-utils'; import TargetSetSelector from '@/components/TargetSetSelector.vue'; -test('Create New Target Set option should correctly add target set', async () => { +beforeEach(() => { + localStorage.clear(); +}) + +test('should correctly render target sets options', async () => { + // Initialize localStorage + const targetSets = { + 'A': { + name: '1st target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + ], + }, + 'B': { + name: '2nd target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 10, distanceUnit: 'kilometers' }, + ], + }, + }; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + // Initialize component - const wrapper = mount(TargetSetSelector, { - data() { - return { - internalValue: 'A', - editingTargetSets: false, - targetSets: { - 'A': { - name: '1st target set', - targets: [ - { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, - ], - }, - }, - }; + const wrapper = shallowMount(TargetSetSelector, { + propsData: { + modelValue: 'B', + } + }); + + // Assert select element populated with target sets + const options = wrapper.findAll('option'); + expect(options[0].element.text).to.equal('1st target set'); + expect(options[0].element.value).to.equal('A'); + expect(options[1].element.text).to.equal('2nd target set'); + expect(options[1].element.value).to.equal('B'); + expect(options[2].element.text).to.equal('[ Create New Target Set ]'); + expect(options[2].element.value).to.equal('_new'); + expect(options.length).to.equal(3); + + // Assert correct target set is selected + expect(wrapper.find('select').element.value).to.equal('B'); +}); + +test('Create New Target Set option should correctly add target set', async () => { + // Initialize localStorage + let targetSets = { + 'A': { + name: '1st target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + ], }, + 'B': { + name: '2nd target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 10, distanceUnit: 'kilometers' }, + ], + }, + }; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + + // Initialize component + const wrapper = shallowMount(TargetSetSelector, { + propsData: { + modelValue: 'A', + } }); // Add target set - wrapper.vm.internalValue = '_new'; - await wrapper.vm.$nextTick(); + await wrapper.find('select').setValue('_new'); - // Assert target set was added - expect(wrapper.vm.targetSets['A']).to.deep.equal({ - name: '1st target set', - targets: [ - { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, - ], - }); - const keys = Object.keys(wrapper.vm.targetSets) - expect(keys.length).to.equal(2); - expect(wrapper.vm.targetSets[keys[1]]).to.deep.equal({ + // Assert target set options were correctly updated + const options = wrapper.findAll('option'); + expect(options[0].element.text).to.equal('1st target set'); + expect(options[0].element.value).to.equal('A'); + expect(options[1].element.text).to.equal('2nd target set'); + expect(options[1].element.value).to.equal('B'); + expect(options[2].element.text).to.equal('New target set'); + expect(options[2].element.value).to.match(/\d{12,14}/); + expect(options[3].element.text).to.equal('[ Create New Target Set ]'); + expect(options[3].element.value).to.equal('_new'); + expect(options.length).to.equal(4); + + // Assert target sets were correctly updated + targetSets[options[2].element.value] = { name: 'New target set', targets: [], - }); + }; + expect(localStorage.getItem('running-tools.target-sets')).to.equal(JSON.stringify(targetSets)); - // Assert new target set was selected - expect(wrapper.vm.internalValue).to.equal(keys[1]); + // Assert targets-updated event was emitted + expect(wrapper.emitted()['targets-updated'].length).to.equal(1); }); -test('revertTargetSet method should correctly reset target sets', async () => { - // Initialize component - const wrapper = mount(TargetSetSelector, { - data() { - return { - internalValue: 'A', - editingTargetSets: false, - targetSets: { - 'A': { - name: '1st target set', - targets: [ - { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, - ], - }, - '_split_targets': { - name: '5K Kilometer Splits', - targets: [ - { result: 'time', distanceValue: 2, distanceUnit: 'Kilometer' }, - { result: 'time', distanceValue: 4, distanceUnit: 'Kilometer' }, - { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, - ], - }, - }, - }; +test('Revert event should correctly reset a default target set', async () => { + // Initialize localStorage + let targetSets = { + '_split_targets': { + name: '1st target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + ], }, + '1234567890123': { + name: '2nd target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 10, distanceUnit: 'kilometers' }, + ], + }, + }; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + + // Initialize component + const wrapper = shallowMount(TargetSetSelector, { + propsData: { + modelValue: '_split_targets', + } }); - // Revert first target set - await wrapper.vm.revertTargetSet(); + // Add target set + await wrapper.findComponent({ name: 'target-editor' }).trigger('revert'); + + // Assert target set options were correctly updated + const options = wrapper.findAll('option'); + expect(options[0].element.text).to.equal('5K Mile Splits'); + expect(options[0].element.value).to.equal('_split_targets'); + expect(options[1].element.text).to.equal('2nd target set'); + expect(options[1].element.value).to.equal('1234567890123'); + expect(options[2].element.text).to.equal('[ Create New Target Set ]'); + expect(options[2].element.value).to.equal('_new'); + expect(options.length).to.equal(3); - // Assert first target set was removed - expect(wrapper.vm.targetSets).to.deep.equal({ + // Assert target sets were correctly updated + targetSets._split_targets.name = '5K Mile Splits'; + targetSets._split_targets.targets[2] = { + result: 'time', + distanceValue: 5, + distanceUnit: 'kilometers', + }; + expect(localStorage.getItem('running-tools.target-sets')).to.equal(JSON.stringify(targetSets)); + + // Assert targets-updated event was emitted + expect(wrapper.emitted()['targets-updated'].length).to.equal(1); +}); + +test('Revert event should correctly delete a custom target set', async () => { + // Initialize localStorage + let targetSets = { '_split_targets': { - name: '5K Kilometer Splits', + name: '1st target set', targets: [ - { result: 'time', distanceValue: 2, distanceUnit: 'Kilometer' }, - { result: 'time', distanceValue: 4, distanceUnit: 'Kilometer' }, + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + ], + }, + '1234567890123': { + name: '2nd target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'kilometers' }, { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 10, distanceUnit: 'kilometers' }, ], }, + }; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + + // Initialize component + const wrapper = shallowMount(TargetSetSelector, { + propsData: { + modelValue: '1234567890123', + } }); - expect(wrapper.vm.internalValue).to.equal('_split_targets'); - // Revert second target set - await wrapper.vm.revertTargetSet(); + // Add target set + await wrapper.findComponent({ name: 'target-editor' }).trigger('revert'); + + // Assert target set options were correctly updated + const options = wrapper.findAll('option'); + expect(options[0].element.text).to.equal('1st target set'); + expect(options[0].element.value).to.equal('_split_targets'); + expect(options[1].element.text).to.equal('[ Create New Target Set ]'); + expect(options[1].element.value).to.equal('_new'); + expect(options.length).to.equal(2); + + // Assert target sets were correctly updated + delete targetSets['1234567890123']; + expect(localStorage.getItem('running-tools.target-sets')).to.equal(JSON.stringify(targetSets)); - // Assert second target set was reset - expect(wrapper.vm.targetSets).to.deep.equal({ + // Assert targets-updated event was emitted + expect(wrapper.emitted()['targets-updated'].length).to.equal(1); +}); + +test('edit button should open target editor with the correct props for default set', async () => { + // Initialize localStorage + const targetSets = { '_split_targets': { name: '5K Mile Splits', targets: [ @@ -105,47 +218,107 @@ test('revertTargetSet method should correctly reset target sets', async () => { { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, ], }, + }; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + + // Initialize component + const wrapper = shallowMount(TargetSetSelector, { + propsData: { + modelValue: '_split_targets', + defaultUnitSystem: 'fake-unit-system', + } }); - expect(wrapper.vm.internalValue).to.equal('_split_targets'); + + // Mock showModal function + wrapper.vm.$refs.dialog.showModal = vi.fn(); + + // Click edit button + await wrapper.find('button').trigger('click'); + + // Assert target editor props are correct + const targetEditor = wrapper.findComponent({ name: 'target-editor' }); + expect(targetEditor.vm.modelValue).to.deep.equal(targetSets._split_targets); + expect(targetEditor.vm.isCustomSet).to.equal(false); + expect(targetEditor.vm.defaultUnitSystem).to.equal('fake-unit-system'); }); -test('sortTargetSet method should correctly sort target sets', async () => { - // Initialize component - const wrapper = mount(TargetSetSelector, { - data() { - return { - internalValue: '_split_targets', - editingTargetSets: false, - targetSets: { - '_split_targets': { - name: '5K Mile Splits', - targets: [ - { result: 'distance', timeValue: 60 }, - { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, - { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, - ], - }, - }, - }; +test('edit button should open target editor with the correct props for custom set', async () => { + // Initialize localStorage + const targetSets = { + '1234567890123': { + name: '2nd target set', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'time', distanceValue: 10, distanceUnit: 'kilometers' }, + ], }, + }; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + + // Initialize component + const wrapper = shallowMount(TargetSetSelector, { + propsData: { + modelValue: '1234567890123', + defaultUnitSystem: 'fake-unit-system', + } }); - // Sort target set - await wrapper.vm.sortTargetSet(); + // Mock showModal function + wrapper.vm.$refs.dialog.showModal = vi.fn(); - // Assert target set was sorted - expect(wrapper.vm.targetSets).to.deep.equal({ + // Click edit button + await wrapper.find('button').trigger('click'); + + // Assert target editor props are correct + const targetEditor = wrapper.findComponent({ name: 'target-editor' }); + expect(targetEditor.vm.modelValue).to.deep.equal(targetSets['1234567890123']); + expect(targetEditor.vm.isCustomSet).to.equal(true); + expect(targetEditor.vm.defaultUnitSystem).to.equal('fake-unit-system'); +}); + +test('should reload and sort target set before target editor is opened', async () => { + // Initialize localStorage + let targetSets = { '_split_targets': { name: '5K Mile Splits', targets: [ + { result: 'distance', timeValue: 60 }, { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, - { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, - { result: 'distance', timeValue: 60 }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, ], }, + }; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + + // Initialize component + const wrapper = shallowMount(TargetSetSelector, { + propsData: { + modelValue: '_split_targets', + } + }); + + // Update localStorage + targetSets._split_targets.name = '5K Mile Splits #2'; + localStorage.setItem('running-tools.target-sets', JSON.stringify(targetSets)); + + // Mock showModal function + wrapper.vm.$refs.dialog.showModal = vi.fn(); + + // Click edit button + await wrapper.find('button').trigger('click'); + + // Assert target set was sorted + expect(wrapper.findComponent({ name: 'target-editor' }).vm.modelValue).to.deep.equal({ + name: '5K Mile Splits #2', + targets: [ + { result: 'time', distanceValue: 1, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 2, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 3, distanceUnit: 'miles' }, + { result: 'time', distanceValue: 5, distanceUnit: 'kilometers' }, + { result: 'distance', timeValue: 60 }, + ], }); });