diff --git a/calculator/test.js b/calculator/test.js index d52d6ee..6a722ce 100644 --- a/calculator/test.js +++ b/calculator/test.js @@ -6,6 +6,7 @@ const assert = require('assert'); let mock_display_str = ""; let mock_ui_callbacks = {}; let drawn_since_clear = false; +global.last_display_num = null; // Mock 'g' (graphics) global.g = { @@ -61,10 +62,24 @@ global.load = () => {}; // --- Load calculator app --- -// We wrap the app code in a function that returns the buttonPress function, -// so we can capture it and use it in our tests. -const calculatorCode = fs.readFileSync(path.join(__dirname, 'calculator.app.js'), 'utf8'); -const wrappedCode = `(function(require) { ${calculatorCode}; return { buttonPress, scientificOperators }; })`; +// We inject a "spy" to capture the raw value passed to displayOutput, +// making our tests independent of UI formatting. +let calculatorCode = fs.readFileSync(path.join(__dirname, 'calculator.app.js'), 'utf8'); +// 1. Rename the original function so we can call it. +calculatorCode = calculatorCode.replace( + 'function displayOutput(num)', + 'function _original_displayOutput(num)' +); +// 2. Prepend our spy that captures the value and then calls the original. +const spyCode = ` +var displayOutput = function(num) { + global.last_display_num = num; + _original_displayOutput(num); +}; +`; +const processedCode = spyCode + calculatorCode; + +const wrappedCode = `(function(require) { ${processedCode}; return { buttonPress, scientificOperators }; })`; const getAppFns = eval(wrappedCode); const { buttonPress, scientificOperators } = getAppFns((name) => { if (name === "FontDylex7x13") { @@ -89,8 +104,8 @@ function runTests() { // Reset state before each test buttonPress('R'); // C buttonPress('R'); // AC - if (mock_display_str !== '0') { - console.error(`[FAIL] ${t.name} - Failed to reset state. Display is: "${mock_display_str}"`); + if (global.last_display_num !== 0) { + console.error(`[FAIL] ${t.name} - Failed to reset state. Last number is: "${global.last_display_num}"`); tests_failed++; return; } @@ -119,60 +134,23 @@ function press(buttons) { } function checkDisplay(expected, message) { - let expectedStr = String(expected); - if (expected === Infinity) expectedStr = 'INF'; - else if (expected === -Infinity) expectedStr = '-INF'; - else if (isNaN(expected)) expectedStr = 'NaN'; + const actual = global.last_display_num; - // If the mock display was truncated, we check if the calculator's formatted - // output *starts with* the truncated display string. This is more robust - // than trying to perfectly replicate font rendering for truncation. - if (mock_display_str.endsWith('...')) { - const actualTruncated = mock_display_str.slice(0, -3); + if (typeof expected === 'number' && isNaN(expected)) { + assert.ok(typeof actual === 'number' && isNaN(actual), message || `Expected NaN, got ${actual}`); + return; + } - // Replicate the calculator's number formatting for an accurate comparison. - const addSeparators = (s) => { - var parts = s.split("."); - var intPart = parts[0]; - var sign = ""; - if (intPart[0] === "-") { - sign = "-"; - intPart = intPart.slice(1); - } - var result = ""; - while (intPart.length > 3) { - result = "," + intPart.slice(-3) + result; - intPart = intPart.slice(0, -3); - } - result = intPart + result; - parts[0] = sign + result; - return parts.join("."); - }; - - const expectedFormatted = addSeparators(String(expected)); - - if (expectedFormatted.startsWith(actualTruncated)) { - return; // The displayed number is a correctly truncated version of the expected one. + // Use a tolerance for floating point comparisons + if (typeof expected === 'number' && typeof actual === 'number') { + const tolerance = 1e-9; + if (Math.abs(expected - actual) < tolerance) { + return; // The numbers are close enough. } } - // Fallback to original logic for non-truncated strings or if the truncation check fails. - const expectedNum = parseFloat(expectedStr.replace(/,/g, '')); - let actualStrForParsing = mock_display_str.replace(/,/g, ''); - if (actualStrForParsing.endsWith('...')) { - actualStrForParsing = actualStrForParsing.slice(0, -3); - } - const actualNum = parseFloat(actualStrForParsing); - - if (!isNaN(expectedNum) && !isNaN(actualNum)) { - if (expectedNum === actualNum) return; // Handles exact matches - const tolerance = 1e-4; // Increased tolerance for float comparisons - if (Math.abs(expectedNum - actualNum) < tolerance) { - return; // Close enough for floating point - } - } - - assert.strictEqual(mock_display_str, expectedStr, message); + // Fallback to strict equality for everything else (e.g., strings like '0.') + assert.strictEqual(actual, expected, message); } // --- End Test Framework --- @@ -387,9 +365,9 @@ test('Operator change', () => { checkDisplay('5'); // 10/2=5 }); -test('Long number with separators', () => { +test('Long number input', () => { press('1234567'); - checkDisplay('1,234,567'); + checkDisplay('1234567'); }); // Run all the defined tests