commit 8bf59b1ddfc25984b5d5074009945848aa018092
parent bf518dc569a9970f68d021e03d94233fbb3e1eaa
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Sat, 12 Jul 2025 11:40:32 -0700
Implement formatDistance and formatPace functions
Also moved existing formatNumber and formatDuration functions to unit
utils.
Diffstat:
11 files changed, 543 insertions(+), 350 deletions(-)
diff --git a/src/components/DecimalInput.vue b/src/components/DecimalInput.vue
@@ -4,7 +4,7 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
-import { formatNumber } from '@/utils/format';
+import { formatNumber } from '@/utils/units';
/**
* The component value
diff --git a/src/components/DoubleOutputTable.vue b/src/components/DoubleOutputTable.vue
@@ -31,9 +31,8 @@ import { computed } from 'vue';
import { ResultType } from '@/utils/calculators';
import type { TargetResult } from '@/utils/calculators';
-import { formatDuration, formatNumber } from '@/utils/format';
import type { Target } from '@/utils/targets';
-import { DistanceUnitData } from '@/utils/units';
+import { formatDistance, formatDuration } from '@/utils/units';
import type { Distance, DistanceTime } from '@/utils/units';
interface Props {
@@ -66,8 +65,7 @@ const props = defineProps<Props>();
const results = computed(() => {
// Calculate results
const results: Array<Array<string>> = [[
- formatNumber(props.inputDistance.distanceValue, 0, 2, false) + ' '
- + DistanceUnitData[props.inputDistance.distanceUnit].symbol
+ formatDistance(props.inputDistance, false),
]];
props.inputTimes.forEach((input, y) => {
diff --git a/src/components/SplitOutputTable.vue b/src/components/SplitOutputTable.vue
@@ -18,12 +18,11 @@
<tbody>
<tr v-for="(item, index) in results" :key="index">
<td>
- {{ formatNumber(item.distanceValue, 0, 2, false) }}
- {{ DistanceUnitData[item.distanceUnit].symbol }}
+ {{ formatDistance(model[index] as Distance, false) }}
</td>
<td>
- {{ formatDuration(item.time, 3, 2, true) }}
+ {{ formatDuration(item.total.time, 3, 2, true) }}
</td>
<td>
@@ -31,9 +30,7 @@
</td>
<td>
- {{ formatDuration(item.pace, 3, 0, true) }}
- / {{ DistanceUnitData[getDefaultDistanceUnit(defaultUnitSystem)]
- .symbol }}
+ {{ formatPace(item.split, getDefaultPaceUnit(defaultUnitSystem)) }}
</td>
</tr>
@@ -49,21 +46,17 @@
<script setup lang="ts">
import { computed } from 'vue';
-import { formatDuration, formatNumber } from '@/utils/format';
import type { SplitTarget } from '@/utils/targets';
-import { DistanceUnits, DistanceUnitData, UnitSystems, convertDistance,
- getDefaultDistanceUnit } from '@/utils/units';
+import { DistanceUnits, UnitSystems, convertDistance, formatDistance, formatDuration,
+ formatPace, getDefaultPaceUnit } from '@/utils/units';
+import type { Distance, DistanceTime } from '@/utils/units';
import TimeInput from '@/components/TimeInput.vue';
import useObjectModel from '@/composables/useObjectModel';
interface SplitTargetResult {
- distance: number,
- distanceValue: number,
- distanceUnit: DistanceUnits,
- time: number,
- splitTime: number,
- pace: number,
+ split: DistanceTime,
+ total: DistanceTime,
};
interface Props {
@@ -95,27 +88,29 @@ const results = computed(() => {
for (let i = 0; i < model.value.length; i += 1) {
// Calculate split and total times
const splitTime = model.value[i].splitTime || 0;
- const totalTime = i === 0 ? splitTime : results[i - 1].time + splitTime;
+ const totalTime = i === 0 ? splitTime : results[i - 1].total.time + splitTime;
// Calculate split and total distances
const totalDistance = convertDistance(
model.value[i].distanceValue,
- model.value[i].distanceUnit, DistanceUnits.Meters,
+ model.value[i].distanceUnit,
+ DistanceUnits.Meters,
);
- const splitDistance = i === 0 ? totalDistance : totalDistance - results[i - 1].distance;
-
- // Calculate pace
- const pace = splitTime / convertDistance(splitDistance, DistanceUnits.Meters,
- getDefaultDistanceUnit(props.defaultUnitSystem));
+ const splitDistance = i === 0 ? totalDistance :
+ totalDistance - results[i - 1].total.distanceValue;
// Add row to results array
results.push({
- distance: totalDistance,
- distanceValue: model.value[i].distanceValue,
- distanceUnit: model.value[i].distanceUnit,
- time: totalTime,
- splitTime,
- pace,
+ split: {
+ distanceValue: splitDistance,
+ distanceUnit: DistanceUnits.Meters,
+ time: splitTime,
+ },
+ total: {
+ distanceValue: totalDistance,
+ distanceUnit: DistanceUnits.Meters,
+ time: totalTime,
+ },
});
}
diff --git a/src/utils/calculators.ts b/src/utils/calculators.ts
@@ -1,10 +1,9 @@
-import { formatDuration, formatNumber } from '@/utils/format';
import * as paceUtils from '@/utils/paces';
import * as raceUtils from '@/utils/races';
import { TargetTypes, workoutTargetToString } from '@/utils/targets';
import type { StandardTarget, WorkoutTarget } from '@/utils/targets';
-import { DistanceUnits, DistanceUnitData, UnitSystems, convertDistance,
- getDefaultDistanceUnit } from '@/utils/units';
+import { DistanceUnits, UnitSystems, convertDistance, formatDistance, formatDuration, formatPace,
+ getDefaultDistanceUnit, getDefaultPaceUnit } from '@/utils/units';
import type { DistanceTime } from '@/utils/units';
/*
@@ -99,21 +98,15 @@ function calculateStandardResult(input: DistanceTime, target: StandardTarget,
distanceUnit = units;
}
- // Calculate numerical pace
- const pace = time / convertDistance(distanceValue, distanceUnit,
- getDefaultDistanceUnit(defaultUnitSystem));
-
return {
// Convert distance to key string
- key: formatNumber(distanceValue, 0, 2, target.type === TargetTypes.Time) + ' ' +
- DistanceUnitData[distanceUnit].symbol,
+ key: formatDistance({ distanceValue, distanceUnit }, target.type === TargetTypes.Time),
// Convert time to time string
value: formatDuration(time, 3, preciseDurations ? 2 : 0, target.type === TargetTypes.Distance),
// Convert pace to pace string
- pace: formatDuration(pace, 3, 0, true) + ' / '
- + DistanceUnitData[getDefaultDistanceUnit(defaultUnitSystem)].symbol,
+ pace: formatPace({ time, distanceValue, distanceUnit }, getDefaultPaceUnit(defaultUnitSystem)),
// Convert dist/time result to key/value
result: target.type === TargetTypes.Distance ? ResultType.Value : ResultType.Key,
diff --git a/src/utils/format.ts b/src/utils/format.ts
@@ -1,102 +0,0 @@
-/**
- * Format a number as a string
- * @param {number} value The number
- * @param {number} minPadding The minimum number of digits to show before the decimal point
- * @param {number} maxDigits The maximum number of digits to show after the decimal point
- * @param {boolean} extraDigits Whether to show extra zeros after the decimal point
- * @returns {string} The formatted value
- */
-export function formatNumber(value: number, minPadding: number = 0, maxDigits: number = 2,
- extraDigits: boolean = true): string {
-
- // Initialize result
- let result = '';
-
- // Remove sign
- const negative = value < 0;
- const fixedValue = Math.abs(value);
-
- // Address edge cases
- if (Number.isNaN(fixedValue)) {
- return 'NaN';
- }
- if (fixedValue === Infinity) {
- return negative ? '-Infinity' : 'Infinity';
- }
-
- // Convert number to string
- if (extraDigits) {
- result = fixedValue.toFixed(maxDigits);
- } else {
- const power = 10 ** maxDigits;
- result = (Math.round((fixedValue + Number.EPSILON) * power) / power).toString();
- }
-
- // Add padding
- const currentPadding = result.split('.')[0].length;
- result = result.padStart(result.length - currentPadding + minPadding, '0');
-
- // Add negative sign
- if (negative) {
- result = `-${result}`;
- }
-
- // Return result
- return result;
-}
-
-/**
- * Format a duration as a string
- * @param {number} value The duration (in seconds)
- * @param {number} minPadding The minimum number of digits to show before the decimal point
- * @param {number} maxDigits The maximum number of digits to show after the decimal point
- * @param {boolean} extraDigits Whether to show extra zeros after the decimal point
- * @returns {string} The formatted value
- */
-export function formatDuration(value: number, minPadding: number = 6, maxDigits: number = 2,
- extraDigits: boolean = true): string {
- // Check if value is NaN
- if (Number.isNaN(value)) {
- return 'NaN';
- }
-
- // Initialize result
- let result = '';
-
- // Check value sign
- if (value < 0) {
- result += '-';
- }
-
- // Check if value is valid
- if (Math.abs(value) === Infinity) {
- return `${result}Infinity`;
- }
-
- // Validate padding
- let fixedPadding = Math.min(minPadding, 6);
-
- // Prevent rounding errors
- const fixedValue = parseFloat(Math.abs(value).toFixed(maxDigits));
-
- // Calculate parts
- const hours = Math.floor(fixedValue / 3600);
- const minutes = Math.floor((fixedValue % 3600) / 60);
- const seconds = fixedValue % 60;
-
- // Format parts
- if (hours !== 0 || fixedPadding >= 5) {
- result += hours.toString().padStart(fixedPadding - 4, '0');
- result += ':';
- fixedPadding = 4;
- }
- if (minutes !== 0 || fixedPadding >= 3) {
- result += minutes.toString().padStart(fixedPadding - 2, '0');
- result += ':';
- fixedPadding = 2;
- }
- result += formatNumber(seconds, fixedPadding, maxDigits, extraDigits);
-
- // Return result
- return result;
-}
diff --git a/src/utils/targets.ts b/src/utils/targets.ts
@@ -1,5 +1,5 @@
-import { formatDuration, formatNumber } from '@/utils/format';
-import { DistanceUnits, DistanceUnitData, convertDistance } from '@/utils/units';
+import { DistanceUnits, convertDistance, formatDistance, formatDuration } from '@/utils/units';
+import type { Distance } from '@/utils/units';
/*
* The two basic types of targets: those defined by distance and those defined by time
@@ -89,13 +89,13 @@ export function sort(targets: Array<Target>): Array<Target> {
* @return {string} The string description
*/
export function workoutTargetToString(target: WorkoutTarget): string {
- let result = formatNumber(target.splitValue, 0, 2, false) + ' ' +
- DistanceUnitData[target.splitUnit].symbol;
+ let result = formatDistance({ distanceValue: target.splitValue, distanceUnit: target.splitUnit },
+ false);
+
if (target.type === TargetTypes.Time) {
result += ' @ ' + formatDuration(target.time, 3, 2, false);
} else if (target.distanceValue != target.splitValue || target.distanceUnit != target.splitUnit) {
- result += ' @ ' + formatNumber(target.distanceValue, 0, 2, false) + ' ' +
- DistanceUnitData[target.distanceUnit].symbol;
+ result += ' @ ' + formatDistance(target as Distance, false);
}
return result;
}
diff --git a/src/utils/units.ts b/src/utils/units.ts
@@ -237,6 +237,133 @@ export function detectDefaultUnitSystem(): UnitSystems {
}
/**
+ * Format a number as a string
+ * @param {number} value The number
+ * @param {number} minPadding The minimum number of digits to show before the decimal point
+ * @param {number} maxDigits The maximum number of digits to show after the decimal point
+ * @param {boolean} extraDigits Whether to show extra zeros after the decimal point
+ * @returns {string} The formatted number
+ */
+export function formatNumber(value: number, minPadding: number = 0, maxDigits: number = 2,
+ extraDigits: boolean = true): string {
+
+ // Initialize result
+ let result = '';
+
+ // Remove sign
+ const negative = value < 0;
+ const fixedValue = Math.abs(value);
+
+ // Address edge cases
+ if (Number.isNaN(fixedValue)) {
+ return 'NaN';
+ }
+ if (fixedValue === Infinity) {
+ return negative ? '-Infinity' : 'Infinity';
+ }
+
+ // Convert number to string
+ if (extraDigits) {
+ result = fixedValue.toFixed(maxDigits);
+ } else {
+ const power = 10 ** maxDigits;
+ result = (Math.round((fixedValue + Number.EPSILON) * power) / power).toString();
+ }
+
+ // Add padding
+ const currentPadding = result.split('.')[0].length;
+ result = result.padStart(result.length - currentPadding + minPadding, '0');
+
+ // Add negative sign
+ if (negative) {
+ result = `-${result}`;
+ }
+
+ // Return result
+ return result;
+}
+
+/**
+ * Format a distance as a string
+ * @param {Distance} input The distance
+ * @param {boolean} extraDigits Whether to show extra zeros after the decimal point
+ * @returns {string} The formatted distance
+ */
+export function formatDistance(input: Distance, extraDigits: boolean) {
+ return formatNumber(input.distanceValue, 0, 2, extraDigits) + ' '
+ + DistanceUnitData[input.distanceUnit].symbol;
+}
+
+/**
+ * Format a duration as a string
+ * @param {number} value The duration (in seconds)
+ * @param {number} minPadding The minimum number of digits to show before the decimal point
+ * @param {number} maxDigits The maximum number of digits to show after the decimal point
+ * @param {boolean} extraDigits Whether to show extra zeros after the decimal point
+ * @returns {string} The formatted duration
+ */
+export function formatDuration(value: number, minPadding: number = 6, maxDigits: number = 2,
+ extraDigits: boolean = true): string {
+ // Check if value is NaN
+ if (Number.isNaN(value)) {
+ return 'NaN';
+ }
+
+ // Initialize result
+ let result = '';
+
+ // Check value sign
+ if (value < 0) {
+ result += '-';
+ }
+
+ // Check if value is valid
+ if (Math.abs(value) === Infinity) {
+ return `${result}Infinity`;
+ }
+
+ // Validate padding
+ let fixedPadding = Math.min(minPadding, 6);
+
+ // Prevent rounding errors
+ const fixedValue = parseFloat(Math.abs(value).toFixed(maxDigits));
+
+ // Calculate parts
+ const hours = Math.floor(fixedValue / 3600);
+ const minutes = Math.floor((fixedValue % 3600) / 60);
+ const seconds = fixedValue % 60;
+
+ // Format parts
+ if (hours !== 0 || fixedPadding >= 5) {
+ result += hours.toString().padStart(fixedPadding - 4, '0');
+ result += ':';
+ fixedPadding = 4;
+ }
+ if (minutes !== 0 || fixedPadding >= 3) {
+ result += minutes.toString().padStart(fixedPadding - 2, '0');
+ result += ':';
+ fixedPadding = 2;
+ }
+ result += formatNumber(seconds, fixedPadding, maxDigits, extraDigits);
+
+ // Return result
+ return result;
+}
+
+/**
+ * Calculate the pace of a distance/time pair and format it as a string
+ * @param {DistanceTime} input The input distance/time pair
+ * @param {PaceUnits} unit The desired pace unit
+ * @returns {string} The formatted pace
+ */
+export function formatPace(input: DistanceTime, unit: PaceUnits) {
+ const dist = convertDistance(input.distanceValue, input.distanceUnit, DistanceUnits.Meters);
+ const pace = convertPace(input.time / dist, PaceUnits.SecondsPerMeter, unit)
+ const result = formatDuration(pace, 3, 0, true) + ' ' + PaceUnitData[unit].symbol;
+ return result;
+}
+
+/**
* Get the default distance unit in a unit system
* @param {UnitSystems} unitSystem The unit system
* @returns {DistanceUnits} The default distance unit
diff --git a/src/views/RaceCalculator.vue b/src/views/RaceCalculator.vue
@@ -43,11 +43,10 @@ import { computed } from 'vue';
import { Calculators, calculateRaceResults, calculateRaceStats } from '@/utils/calculators';
import type { RaceOptions, RaceStats } from '@/utils/calculators';
-import { formatNumber } from '@/utils/format';
import { RacePredictionModel } from '@/utils/races';
import { defaultTargetSets } from '@/utils/targets';
import type { StandardTargetSets } from '@/utils/targets';
-import { DistanceUnits, UnitSystems, detectDefaultUnitSystem } from '@/utils/units';
+import { DistanceUnits, UnitSystems, detectDefaultUnitSystem, formatNumber } from '@/utils/units';
import type { DistanceTime } from '@/utils/units';
import AdvancedOptionsInput from '@/components/AdvancedOptionsInput.vue';
diff --git a/src/views/UnitCalculator.vue b/src/views/UnitCalculator.vue
@@ -20,10 +20,10 @@
<span class="equals"> = </span>
<span v-if="isTimeUnit(input.outputUnit)" class="output-value" aria-label="Output value">
- {{ formatDuration(outputValue, 6, 3, true) }}
+ {{ unitUtils.formatDuration(outputValue, 6, 3, true) }}
</span>
<span v-else class="output-value" aria-label="Output value">
- {{ formatNumber(outputValue, 0, 3, true) }}
+ {{ unitUtils.formatNumber(outputValue, 0, 3, true) }}
</span>
<select v-model="input.outputUnit" class="output-units" aria-label="Output units">
@@ -37,7 +37,6 @@
<script setup lang="ts">
import { computed } from 'vue';
-import { formatDuration, formatNumber } from '@/utils/format';
import * as unitUtils from '@/utils/units';
import DecimalInput from '@/components/DecimalInput.vue';
diff --git a/tests/unit/utils/format.spec.js b/tests/unit/utils/format.spec.js
@@ -1,193 +0,0 @@
-import { describe, test, expect } from 'vitest';
-import * as formatUtils from '@/utils/format';
-
-describe('formatNumber method', () => {
- test('should correctly format number when padding is not 0', () => {
- let result = formatUtils.formatNumber(12.3, 3, 0);
- expect(result).to.equal('012');
-
- result = formatUtils.formatNumber(12.3, 3, 2);
- expect(result).to.equal('012.30');
-
- result = formatUtils.formatNumber(123, 2, 0);
- expect(result).to.equal('123');
-
- result = formatUtils.formatNumber(-12, 3, 0);
- expect(result).to.equal('-012');
- });
-
- test('should correctly format number when extraDigits is true', () => {
- let result = formatUtils.formatNumber(1234, 0, 2);
- expect(result).to.equal('1234.00');
-
- result = formatUtils.formatNumber(1234.5, 0, 2);
- expect(result).to.equal('1234.50');
-
- result = formatUtils.formatNumber(1234.56, 0, 2);
- expect(result).to.equal('1234.56');
-
- result = formatUtils.formatNumber(1234.567, 0, 2);
- expect(result).to.equal('1234.57');
-
- result = formatUtils.formatNumber(1234.56, 0, 0);
- expect(result).to.equal('1235');
- });
-
- test('should correctly format number when extraDigits is false', () => {
- let result = formatUtils.formatNumber(1234, 0, 2, false);
- expect(result).to.equal('1234');
-
- result = formatUtils.formatNumber(1234.5, 0, 2, false);
- expect(result).to.equal('1234.5');
-
- result = formatUtils.formatNumber(1234.56, 0, 2, false);
- expect(result).to.equal('1234.56');
-
- result = formatUtils.formatNumber(1234.567, 0, 2, false);
- expect(result).to.equal('1234.57');
-
- result = formatUtils.formatNumber(1234.56, 0, 0, false);
- expect(result).to.equal('1235');
- });
-
- test('should correctly format undefined', () => {
- let result = formatUtils.formatNumber(undefined, 0, 2);
- expect(result).to.equal('NaN');
-
- result = formatUtils.formatNumber(undefined, 0, 2, false);
- expect(result).to.equal('NaN');
-
- result = formatUtils.formatNumber(undefined, 5, 2);
- expect(result).to.equal('NaN');
- });
-
- test('should correctly format NaN', () => {
- let result = formatUtils.formatNumber(NaN, 0, 0);
- expect(result).to.equal('NaN');
-
- result = formatUtils.formatNumber(NaN, 0, 2, false);
- expect(result).to.equal('NaN');
-
- result = formatUtils.formatNumber(NaN, 5, 2);
- expect(result).to.equal('NaN');
- });
-
- test('should correctly format +/- Infinity', () => {
- let result = formatUtils.formatNumber(Infinity);
- expect(result).to.equal('Infinity');
-
- result = formatUtils.formatNumber(Infinity, 10, 2);
- expect(result).to.equal('Infinity');
-
- result = formatUtils.formatNumber(-Infinity);
- expect(result).to.equal('-Infinity');
- });
-
- test('should correctly format numbers less than 1', () => {
- let result = formatUtils.formatNumber(0.123, 0, 0);
- expect(result).to.equal('0');
-
- result = formatUtils.formatNumber(0.123, 0, 2);
- expect(result).to.equal('0.12');
- });
-
- test('should correctly format negative numbers', () => {
- let result = formatUtils.formatNumber(-12, 0, 2, false);
- expect(result).to.equal('-12');
-
- result = formatUtils.formatNumber(-12, 0, 2);
- expect(result).to.equal('-12.00');
-
- result = formatUtils.formatNumber(-12.34, 0, 2);
- expect(result).to.equal('-12.34');
-
- result = formatUtils.formatNumber(-12.34, 3, 2);
- expect(result).to.equal('-012.34');
-
- result = formatUtils.formatNumber(-0.12, 0, 2);
- expect(result).to.equal('-0.12');
- });
-});
-
-describe('formatDuration method', () => {
- test('should correctly divide durations into parts', () => {
- const result = formatUtils.formatDuration(3600 + 120 + 3 + 0.4);
- expect(result).to.equal('01:02:03.40');
- });
-
- test('should correctly format duration when padding is 7', () => {
- const result = formatUtils.formatDuration(3600 + 120 + 3 + 0.4, 7);
- expect(result).to.equal('01:02:03.40');
- });
-
- test('should correctly format duration when padding is 3', () => {
- let result = formatUtils.formatDuration(3600 + 120 + 3 + 0.4, 3);
- expect(result).to.equal('1:02:03.40');
-
- result = formatUtils.formatDuration(120 + 3 + 0.4, 3);
- expect(result).to.equal('2:03.40');
-
- result = formatUtils.formatDuration(3 + 0.4, 3);
- expect(result).to.equal('0:03.40');
- });
-
- test('should correctly format duration when padding is 0', () => {
- const result = formatUtils.formatDuration(0.4, 0);
- expect(result).to.equal('0.40');
- });
-
- test('should correctly format duration when digits is 3', () => {
- const result = formatUtils.formatDuration(3600 + 120 + 3 + 0.4567, 0, 3);
- expect(result).to.equal('1:02:03.457');
- });
-
- test('should correctly format duration when digits is 0', () => {
- const result = formatUtils.formatDuration(3600 + 120 + 3 + 0.456, 0, 0);
- expect(result).to.equal('1:02:03');
- });
-
- test('should correctly format NaN', () => {
- const result = formatUtils.formatDuration(NaN);
- expect(result).to.equal('NaN');
- });
-
- test('should correctly format +/- Infinity', () => {
- let result = formatUtils.formatDuration(Infinity);
- expect(result).to.equal('Infinity');
-
- result = formatUtils.formatDuration(-Infinity);
- expect(result).to.equal('-Infinity');
- });
-
- test('should correctly format 0 when padding is 0', () => {
- const result = formatUtils.formatDuration(0, 0);
- expect(result).to.equal('0.00');
- });
-
- test('should correctly format negative durations', () => {
- const result = formatUtils.formatDuration(-3600 - 120 - 3 - 0.4);
- expect(result).to.equal('-01:02:03.40');
- });
-
- test('should correctly format 59.9999', () => {
- const result = formatUtils.formatDuration(59.9999);
- expect(result).to.equal('00:01:00.00');
- });
-
- test('should correctly format duration when extraDigits is false', () => {
- let result = formatUtils.formatDuration(83, 0, 2, false);
- expect(result).to.equal('1:23');
-
- result = formatUtils.formatDuration(83.4, 0, 2, false);
- expect(result).to.equal('1:23.4');
-
- result = formatUtils.formatDuration(83.45, 0, 2, false);
- expect(result).to.equal('1:23.45');
-
- result = formatUtils.formatDuration(83.456, 0, 2, false);
- expect(result).to.equal('1:23.46');
-
- result = formatUtils.formatDuration(83.45, 0, 0, false);
- expect(result).to.equal('1:23');
- });
-});
diff --git a/tests/unit/utils/units.spec.js b/tests/unit/utils/units.spec.js
@@ -60,3 +60,380 @@ describe('convertSpeedPace method', () => {
expect(result).to.equal(1);
});
});
+
+describe('formatNumber method', () => {
+ test('should correctly format number when padding is not 0', () => {
+ let result = units.formatNumber(12.3, 3, 0);
+ expect(result).to.equal('012');
+
+ result = units.formatNumber(12.3, 3, 2);
+ expect(result).to.equal('012.30');
+
+ result = units.formatNumber(123, 2, 0);
+ expect(result).to.equal('123');
+
+ result = units.formatNumber(-12, 3, 0);
+ expect(result).to.equal('-012');
+ });
+
+ test('should correctly format number when extraDigits is true', () => {
+ let result = units.formatNumber(1234, 0, 2);
+ expect(result).to.equal('1234.00');
+
+ result = units.formatNumber(1234.5, 0, 2);
+ expect(result).to.equal('1234.50');
+
+ result = units.formatNumber(1234.56, 0, 2);
+ expect(result).to.equal('1234.56');
+
+ result = units.formatNumber(1234.567, 0, 2);
+ expect(result).to.equal('1234.57');
+
+ result = units.formatNumber(1234.56, 0, 0);
+ expect(result).to.equal('1235');
+ });
+
+ test('should correctly format number when extraDigits is false', () => {
+ let result = units.formatNumber(1234, 0, 2, false);
+ expect(result).to.equal('1234');
+
+ result = units.formatNumber(1234.5, 0, 2, false);
+ expect(result).to.equal('1234.5');
+
+ result = units.formatNumber(1234.56, 0, 2, false);
+ expect(result).to.equal('1234.56');
+
+ result = units.formatNumber(1234.567, 0, 2, false);
+ expect(result).to.equal('1234.57');
+
+ result = units.formatNumber(1234.56, 0, 0, false);
+ expect(result).to.equal('1235');
+ });
+
+ test('should correctly format undefined', () => {
+ let result = units.formatNumber(undefined, 0, 2);
+ expect(result).to.equal('NaN');
+
+ result = units.formatNumber(undefined, 0, 2, false);
+ expect(result).to.equal('NaN');
+
+ result = units.formatNumber(undefined, 5, 2);
+ expect(result).to.equal('NaN');
+ });
+
+ test('should correctly format NaN', () => {
+ let result = units.formatNumber(NaN, 0, 0);
+ expect(result).to.equal('NaN');
+
+ result = units.formatNumber(NaN, 0, 2, false);
+ expect(result).to.equal('NaN');
+
+ result = units.formatNumber(NaN, 5, 2);
+ expect(result).to.equal('NaN');
+ });
+
+ test('should correctly format +/- Infinity', () => {
+ let result = units.formatNumber(Infinity);
+ expect(result).to.equal('Infinity');
+
+ result = units.formatNumber(Infinity, 10, 2);
+ expect(result).to.equal('Infinity');
+
+ result = units.formatNumber(-Infinity);
+ expect(result).to.equal('-Infinity');
+ });
+
+ test('should correctly format numbers smaller than 1', () => {
+ let result = units.formatNumber(0.123, 0, 0);
+ expect(result).to.equal('0');
+
+ result = units.formatNumber(0.123, 0, 2);
+ expect(result).to.equal('0.12');
+ });
+
+ test('should correctly format negative numbers', () => {
+ let result = units.formatNumber(-12, 0, 2, false);
+ expect(result).to.equal('-12');
+
+ result = units.formatNumber(-12, 0, 2);
+ expect(result).to.equal('-12.00');
+
+ result = units.formatNumber(-12.34, 0, 2);
+ expect(result).to.equal('-12.34');
+
+ result = units.formatNumber(-12.34, 3, 2);
+ expect(result).to.equal('-012.34');
+
+ result = units.formatNumber(-0.12, 0, 2);
+ expect(result).to.equal('-0.12');
+ });
+});
+
+describe('formatDistance method', () => {
+ test('should correctly format distances with a variety of units', () => {
+ let result = units.formatDistance({
+ distanceValue: 1,
+ distanceUnit: units.DistanceUnits.Yards,
+ }, false);
+ expect(result).to.equal('1 yd');
+
+ result = units.formatDistance({
+ distanceValue: 2,
+ distanceUnit: units.DistanceUnits.Meters,
+ }, false);
+ expect(result).to.equal('2 m');
+
+ result = units.formatDistance({
+ distanceValue: 3,
+ distanceUnit: units.DistanceUnits.Kilometers,
+ }, false);
+ expect(result).to.equal('3 km');
+
+ result = units.formatDistance({
+ distanceValue: 4,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('4 mi');
+
+ result = units.formatDistance({
+ distanceValue: 5,
+ distanceUnit: units.DistanceUnits.Marathons,
+ }, false);
+ expect(result).to.equal('5 Mar');
+ });
+
+ test('should correctly format distance when extraDigits is true', () => {
+ let result = units.formatDistance({
+ distanceValue: 1234,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, true);
+ expect(result).to.equal('1234.00 mi');
+
+ result = units.formatDistance({
+ distanceValue: 1234.5,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, true);
+ expect(result).to.equal('1234.50 mi');
+
+ result = units.formatDistance({
+ distanceValue: 1234.56,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, true);
+ expect(result).to.equal('1234.56 mi');
+
+ result = units.formatDistance({
+ distanceValue: 1234.567,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, true);
+ expect(result).to.equal('1234.57 mi');
+ });
+
+ test('should correctly format distance when extraDigits is false', () => {
+ let result = units.formatDistance({
+ distanceValue: 1234,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('1234 mi');
+
+ result = units.formatDistance({
+ distanceValue: 1234.5,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('1234.5 mi');
+
+ result = units.formatDistance({
+ distanceValue: 1234.56,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('1234.56 mi');
+
+ result = units.formatDistance({
+ distanceValue: 1234.567,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('1234.57 mi');
+ });
+
+ test('should correctly format distances smaller than 1', () => {
+ let result = units.formatDistance({
+ distanceValue: 0,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('0 mi');
+
+ result = units.formatDistance({
+ distanceValue: 0,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, true);
+ expect(result).to.equal('0.00 mi');
+
+ result = units.formatDistance({
+ distanceValue: 0.1,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('0.1 mi');
+
+ result = units.formatDistance({
+ distanceValue: 0.1,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, true);
+ expect(result).to.equal('0.10 mi');
+
+ result = units.formatDistance({
+ distanceValue: 0.12,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('0.12 mi');
+ });
+
+ test('should correctly format negative distances', () => {
+ let result = units.formatDistance({
+ distanceValue: -1234,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('-1234 mi');
+
+ result = units.formatDistance({
+ distanceValue: -1234,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, true);
+ expect(result).to.equal('-1234.00 mi');
+
+ result = units.formatDistance({
+ distanceValue: -1234.56,
+ distanceUnit: units.DistanceUnits.Miles,
+ }, false);
+ expect(result).to.equal('-1234.56 mi');
+ });
+});
+
+describe('formatDuration method', () => {
+ test('should correctly divide durations into parts', () => {
+ const result = units.formatDuration(3600 + 120 + 3 + 0.4);
+ expect(result).to.equal('01:02:03.40');
+ });
+
+ test('should correctly format duration when padding is 7', () => {
+ const result = units.formatDuration(3600 + 120 + 3 + 0.4, 7);
+ expect(result).to.equal('01:02:03.40');
+ });
+
+ test('should correctly format duration when padding is 3', () => {
+ let result = units.formatDuration(3600 + 120 + 3 + 0.4, 3);
+ expect(result).to.equal('1:02:03.40');
+
+ result = units.formatDuration(120 + 3 + 0.4, 3);
+ expect(result).to.equal('2:03.40');
+
+ result = units.formatDuration(3 + 0.4, 3);
+ expect(result).to.equal('0:03.40');
+ });
+
+ test('should correctly format duration when padding is 0', () => {
+ const result = units.formatDuration(0.4, 0);
+ expect(result).to.equal('0.40');
+ });
+
+ test('should correctly format duration when digits is 3', () => {
+ const result = units.formatDuration(3600 + 120 + 3 + 0.4567, 0, 3);
+ expect(result).to.equal('1:02:03.457');
+ });
+
+ test('should correctly format duration when digits is 0', () => {
+ const result = units.formatDuration(3600 + 120 + 3 + 0.456, 0, 0);
+ expect(result).to.equal('1:02:03');
+ });
+
+ test('should correctly format NaN', () => {
+ const result = units.formatDuration(NaN);
+ expect(result).to.equal('NaN');
+ });
+
+ test('should correctly format +/- Infinity', () => {
+ let result = units.formatDuration(Infinity);
+ expect(result).to.equal('Infinity');
+
+ result = units.formatDuration(-Infinity);
+ expect(result).to.equal('-Infinity');
+ });
+
+ test('should correctly format 0 when padding is 0', () => {
+ const result = units.formatDuration(0, 0);
+ expect(result).to.equal('0.00');
+ });
+
+ test('should correctly format negative durations', () => {
+ const result = units.formatDuration(-3600 - 120 - 3 - 0.4);
+ expect(result).to.equal('-01:02:03.40');
+ });
+
+ test('should correctly format 59.9999', () => {
+ const result = units.formatDuration(59.9999);
+ expect(result).to.equal('00:01:00.00');
+ });
+
+ test('should correctly format duration when extraDigits is false', () => {
+ let result = units.formatDuration(83, 0, 2, false);
+ expect(result).to.equal('1:23');
+
+ result = units.formatDuration(83.4, 0, 2, false);
+ expect(result).to.equal('1:23.4');
+
+ result = units.formatDuration(83.45, 0, 2, false);
+ expect(result).to.equal('1:23.45');
+
+ result = units.formatDuration(83.456, 0, 2, false);
+ expect(result).to.equal('1:23.46');
+
+ result = units.formatDuration(83.45, 0, 0, false);
+ expect(result).to.equal('1:23');
+ });
+});
+
+describe('formatPace method', () => {
+ test('should correctly format paces in a variety of units', () => {
+ let result = units.formatPace({
+ distanceValue: 1,
+ distanceUnit: units.DistanceUnits.Meters,
+ time: 600,
+ }, units.PaceUnits.SecondsPerMeter);
+ expect(result).to.equal('10:00 s/m');
+
+ result = units.formatPace({
+ distanceValue: 2,
+ distanceUnit: units.DistanceUnits.Kilometers,
+ time: 600,
+ }, units.PaceUnits.TimePerKilometer);
+ expect(result).to.equal('5:00 / km');
+
+ result = units.formatPace({
+ distanceValue: 3,
+ distanceUnit: units.DistanceUnits.Miles,
+ time: 600,
+ }, units.PaceUnits.TimePerMile);
+ expect(result).to.equal('3:20 / mi');
+ });
+
+ test('should correctly format paces that require distance conversion', () => {
+ let result = units.formatPace({
+ distanceValue: 100,
+ distanceUnit: units.DistanceUnits.Meters,
+ time: 600,
+ }, units.PaceUnits.TimePerKilometer);
+ expect(result).to.equal('1:40:00 / km');
+
+ result = units.formatPace({
+ distanceValue: 2,
+ distanceUnit: units.DistanceUnits.Kilometers,
+ time: 600,
+ }, units.PaceUnits.TimePerMile);
+ expect(result).to.equal('8:03 / mi');
+
+ result = units.formatPace({
+ distanceValue: 0.03,
+ distanceUnit: units.DistanceUnits.Miles,
+ time: 600,
+ }, units.PaceUnits.SecondsPerMeter);
+ expect(result).to.equal('0:12 s/m');
+ });
+});