Skip to content

Commit fb7f7a9

Browse files
fix: improve string validation to reject partial numeric strings
- Change from parseFloat() to Number() for string input validation - Now properly throws HyperMathError for strings like "3.14abc" instead of parsing as 3.14 - Add comprehensive tests for processInput() method (10+ new test cases) - Add tests for formatResult() method - Update README and CHANGELOG to reflect validation behavior - Test suite now includes 62 tests (all passing)
1 parent be49396 commit fb7f7a9

4 files changed

Lines changed: 121 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Fixed
11+
- 🐛 **String validation**: Changed from `parseFloat()` to `Number()` for string input validation to properly reject strings with trailing non-numeric characters (e.g., `"3.14abc"` now throws `HyperMathError` instead of being parsed as `3.14`)
12+
13+
### Changed
14+
- 📝 **Documentation**: Updated README to clarify that string validation uses `Number()` and rejects partial numeric strings
15+
16+
### Testing
17+
- ✅ Added comprehensive tests for `processInput()` method (10+ new test cases)
18+
- ✅ Added comprehensive tests for `formatResult()` method
19+
- ✅ Test suite now includes 62 tests (up from 52)
20+
821
## [0.1.0] - 2025-11-04
922

1023
### ⚠️ BREAKING CHANGES

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ See [CHANGELOG.md](CHANGELOG.md) for complete migration guide.
189189
- **Edge cases**:
190190
- **Division by zero**: Throws `DivisionByZeroError` instead of returning 0
191191
- **Invalid inputs**: Any null, undefined, or non-numeric string values will throw `HyperMathError`
192-
- **String parsing**: String inputs are parsed using `parseFloat()`, so partial numeric strings like "123abc" will parse as 123
192+
- **String parsing**: String inputs are validated using `Number()` to ensure the entire string is numeric. Strings with trailing non-numeric characters (e.g., `"123abc"`) will throw `HyperMathError`
193193
- **Type safety**: TypeScript users benefit from full type definitions and the custom `HyperMathError` class for better error handling
194194

195195
## Support

src/lib/hypermath.spec.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,4 +317,107 @@ describe('HyperMath', () => {
317317
expect(HyperMath.formatNumber(2.224)).toBe(2.22);
318318
});
319319
});
320+
describe('HyperMath.processInput', () => {
321+
// Valid numbers
322+
it('should return the same number for valid number input', () => {
323+
expect((HyperMath as any).processInput(42)).toBe(42);
324+
expect((HyperMath as any).processInput(-3.14)).toBe(-3.14);
325+
expect((HyperMath as any).processInput(0)).toBe(0);
326+
});
327+
328+
// Valid strings
329+
it('should parse valid numeric strings', () => {
330+
expect((HyperMath as any).processInput('42')).toBe(42);
331+
expect((HyperMath as any).processInput(' -3.14 ')).toBe(-3.14);
332+
expect((HyperMath as any).processInput('0')).toBe(0);
333+
expect((HyperMath as any).processInput('2.5')).toBe(2.5);
334+
expect((HyperMath as any).processInput('1.63575')).toBe(1.63575);
335+
});
336+
337+
// Empty string
338+
it('should throw HyperMathError for empty string', () => {
339+
expect(() => (HyperMath as any).processInput('')).toThrow(HyperMathError);
340+
expect(() => (HyperMath as any).processInput(' ')).toThrow(
341+
HyperMathError,
342+
);
343+
});
344+
345+
// Invalid strings
346+
it('should throw HyperMathError for non-numeric strings', () => {
347+
expect(() => (HyperMath as any).processInput('abc')).toThrow(
348+
HyperMathError,
349+
);
350+
expect(() => (HyperMath as any).processInput('3.14abc')).toThrow(
351+
HyperMathError,
352+
);
353+
expect(() => (HyperMath as any).processInput('NaN')).toThrow(
354+
HyperMathError,
355+
);
356+
expect(() => (HyperMath as any).processInput('Infinity')).toThrow(
357+
HyperMathError,
358+
);
359+
});
360+
361+
// Null and undefined
362+
it('should throw HyperMathError for null or undefined', () => {
363+
expect(() => (HyperMath as any).processInput(null)).toThrow(HyperMathError);
364+
expect(() => (HyperMath as any).processInput(undefined)).toThrow(
365+
HyperMathError,
366+
);
367+
});
368+
369+
// NaN and Infinity
370+
it('should throw HyperMathError for NaN and Infinity number inputs', () => {
371+
expect(() => (HyperMath as any).processInput(NaN)).toThrow(HyperMathError);
372+
expect(() => (HyperMath as any).processInput(Infinity)).toThrow(
373+
HyperMathError,
374+
);
375+
expect(() => (HyperMath as any).processInput(-Infinity)).toThrow(
376+
HyperMathError,
377+
);
378+
});
379+
380+
// Unsupported types
381+
it('should throw HyperMathError for unsupported types', () => {
382+
expect(() => (HyperMath as any).processInput({})).toThrow(HyperMathError);
383+
expect(() => (HyperMath as any).processInput([])).toThrow(HyperMathError);
384+
expect(() => (HyperMath as any).processInput(true)).toThrow(HyperMathError);
385+
expect(() => (HyperMath as any).processInput(Symbol('sym'))).toThrow(
386+
HyperMathError,
387+
);
388+
expect(() => (HyperMath as any).processInput(() => 1)).toThrow(
389+
HyperMathError,
390+
);
391+
});
392+
});
393+
describe('HyperMath.formatResult', () => {
394+
// Valid finite numbers
395+
it('should format a number to 2 decimal places', () => {
396+
expect((HyperMath as any).formatResult(3.14159)).toBe(3.14);
397+
expect((HyperMath as any).formatResult(5.1)).toBe(5.1);
398+
expect((HyperMath as any).formatResult(7)).toBe(7);
399+
expect((HyperMath as any).formatResult(-2.567)).toBe(-2.57);
400+
expect((HyperMath as any).formatResult(-0.1)).toBe(-0.1);
401+
expect((HyperMath as any).formatResult(0.001)).toBe(0);
402+
expect((HyperMath as any).formatResult(0.009)).toBe(0.01);
403+
expect((HyperMath as any).formatResult(123456789.999)).toBe(123456790);
404+
expect((HyperMath as any).formatResult(1.126)).toBe(1.13);
405+
expect((HyperMath as any).formatResult(2.224)).toBe(2.22);
406+
expect((HyperMath as any).formatResult(0)).toBe(0);
407+
});
408+
409+
// Non-finite numbers
410+
it('should throw HyperMathError for NaN', () => {
411+
expect(() => (HyperMath as any).formatResult(NaN)).toThrow(HyperMathError);
412+
});
413+
414+
it('should throw HyperMathError for Infinity', () => {
415+
expect(() => (HyperMath as any).formatResult(Infinity)).toThrow(
416+
HyperMathError,
417+
);
418+
expect(() => (HyperMath as any).formatResult(-Infinity)).toThrow(
419+
HyperMathError,
420+
);
421+
});
422+
});
320423
});

src/lib/hypermath.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ export class HyperMath {
6060
throw new HyperMathError('Invalid input: empty string');
6161
}
6262

63-
const parsedNumber = parseFloat(trimmedValue);
63+
// Use Number() instead of parseFloat() to ensure the entire string is valid
64+
// Number() returns NaN for strings with trailing non-numeric characters
65+
// while parseFloat() would parse '3.14abc' as 3.14
66+
const parsedNumber = Number(trimmedValue);
6467

6568
// Check if parsing was successful and result is finite
6669
if (!Number.isFinite(parsedNumber)) {

0 commit comments

Comments
 (0)