test: Improve output validation by spying on raw display numbers
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
+34
-56
@@ -6,6 +6,7 @@ const assert = require('assert');
|
|||||||
let mock_display_str = "";
|
let mock_display_str = "";
|
||||||
let mock_ui_callbacks = {};
|
let mock_ui_callbacks = {};
|
||||||
let drawn_since_clear = false;
|
let drawn_since_clear = false;
|
||||||
|
global.last_display_num = null;
|
||||||
|
|
||||||
// Mock 'g' (graphics)
|
// Mock 'g' (graphics)
|
||||||
global.g = {
|
global.g = {
|
||||||
@@ -61,10 +62,24 @@ global.load = () => {};
|
|||||||
|
|
||||||
|
|
||||||
// --- Load calculator app ---
|
// --- Load calculator app ---
|
||||||
// We wrap the app code in a function that returns the buttonPress function,
|
// We inject a "spy" to capture the raw value passed to displayOutput,
|
||||||
// so we can capture it and use it in our tests.
|
// making our tests independent of UI formatting.
|
||||||
const calculatorCode = fs.readFileSync(path.join(__dirname, 'calculator.app.js'), 'utf8');
|
let calculatorCode = fs.readFileSync(path.join(__dirname, 'calculator.app.js'), 'utf8');
|
||||||
const wrappedCode = `(function(require) { ${calculatorCode}; return { buttonPress, scientificOperators }; })`;
|
// 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 getAppFns = eval(wrappedCode);
|
||||||
const { buttonPress, scientificOperators } = getAppFns((name) => {
|
const { buttonPress, scientificOperators } = getAppFns((name) => {
|
||||||
if (name === "FontDylex7x13") {
|
if (name === "FontDylex7x13") {
|
||||||
@@ -89,8 +104,8 @@ function runTests() {
|
|||||||
// Reset state before each test
|
// Reset state before each test
|
||||||
buttonPress('R'); // C
|
buttonPress('R'); // C
|
||||||
buttonPress('R'); // AC
|
buttonPress('R'); // AC
|
||||||
if (mock_display_str !== '0') {
|
if (global.last_display_num !== 0) {
|
||||||
console.error(`[FAIL] ${t.name} - Failed to reset state. Display is: "${mock_display_str}"`);
|
console.error(`[FAIL] ${t.name} - Failed to reset state. Last number is: "${global.last_display_num}"`);
|
||||||
tests_failed++;
|
tests_failed++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -119,60 +134,23 @@ function press(buttons) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkDisplay(expected, message) {
|
function checkDisplay(expected, message) {
|
||||||
let expectedStr = String(expected);
|
const actual = global.last_display_num;
|
||||||
if (expected === Infinity) expectedStr = 'INF';
|
|
||||||
else if (expected === -Infinity) expectedStr = '-INF';
|
|
||||||
else if (isNaN(expected)) expectedStr = 'NaN';
|
|
||||||
|
|
||||||
// If the mock display was truncated, we check if the calculator's formatted
|
if (typeof expected === 'number' && isNaN(expected)) {
|
||||||
// output *starts with* the truncated display string. This is more robust
|
assert.ok(typeof actual === 'number' && isNaN(actual), message || `Expected NaN, got ${actual}`);
|
||||||
// than trying to perfectly replicate font rendering for truncation.
|
return;
|
||||||
if (mock_display_str.endsWith('...')) {
|
|
||||||
const actualTruncated = mock_display_str.slice(0, -3);
|
|
||||||
|
|
||||||
// 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));
|
// Use a tolerance for floating point comparisons
|
||||||
|
if (typeof expected === 'number' && typeof actual === 'number') {
|
||||||
if (expectedFormatted.startsWith(actualTruncated)) {
|
const tolerance = 1e-9;
|
||||||
return; // The displayed number is a correctly truncated version of the expected one.
|
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.
|
// Fallback to strict equality for everything else (e.g., strings like '0.')
|
||||||
const expectedNum = parseFloat(expectedStr.replace(/,/g, ''));
|
assert.strictEqual(actual, expected, message);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
// --- End Test Framework ---
|
// --- End Test Framework ---
|
||||||
|
|
||||||
@@ -387,9 +365,9 @@ test('Operator change', () => {
|
|||||||
checkDisplay('5'); // 10/2=5
|
checkDisplay('5'); // 10/2=5
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Long number with separators', () => {
|
test('Long number input', () => {
|
||||||
press('1234567');
|
press('1234567');
|
||||||
checkDisplay('1,234,567');
|
checkDisplay('1234567');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run all the defined tests
|
// Run all the defined tests
|
||||||
|
|||||||
Reference in New Issue
Block a user