test: Replace static tests with comprehensive programmatic test suite

Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
2026-03-14 16:52:23 -06:00
committed by Tanner
parent 31531ff118
commit 20ce84ed89
+162 -108
View File
@@ -145,35 +145,165 @@ function checkDisplay(expected, message) {
// --- Test Cases --- // --- Test Cases ---
test('Addition', () => { const testNumbers = [
press('1+2='); 0, 1, -1, 5, -5, 0.5, -0.5, 123, -123, 1.23, -1.23,
checkDisplay('3'); 1234567, -1234567, 99999999, -99999999,
1e-6, -1e-6, Math.PI, Math.E
];
// Helper to press a number, handling negatives
function pressNumber(n) {
const s = String(n);
if (s.startsWith('-')) {
press(s.substring(1));
buttonPress('N');
} else {
press(s);
}
}
// --- Generated Tests: Binary Operations ---
const binaryOps = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => a / b,
};
for (const op in binaryOps) {
for (const a of testNumbers) {
for (const b of testNumbers) {
if (op === '/' && b === 0) {
test(`Binary Edge Case: ${a} / 0`, () => {
pressNumber(a);
buttonPress('/');
pressNumber(0);
buttonPress('=');
checkDisplay(a === 0 ? NaN : (a > 0 ? Infinity : -Infinity));
});
continue;
}
test(`Binary: ${a} ${op} ${b}`, () => {
pressNumber(a);
buttonPress(op);
pressNumber(b);
buttonPress('=');
const expected = binaryOps[op](a, b);
checkDisplay(expected);
});
}
}
}
// --- Generated Tests: Unary Operations ---
const unaryOps = {
'r': { name: 'sqrt', fn: Math.sqrt },
's': { name: 'x^2', fn: (a) => a * a },
'i': { name: '1/x', fn: (a) => 1 / a },
'%': { name: '%', fn: (a) => a / 100 },
};
for (const op in unaryOps) {
for (const a of testNumbers) {
if (op === 'r' && a < 0) {
test(`Unary Edge Case: sqrt(${a})`, () => {
pressNumber(a);
buttonPress('r');
checkDisplay(NaN);
});
continue;
}
test(`Unary: ${unaryOps[op].name}(${a})`, () => {
pressNumber(a);
buttonPress(op);
const expected = unaryOps[op].fn(a);
checkDisplay(expected);
});
}
}
// --- Generated Tests: Chaining Operations ---
// To keep runtime sane, we'll pick a smaller subset for chaining
const chainNumbers = [0, 1, -5, 0.5, 10, 123];
const chainOps = ['+', '-', '*', '/'];
for (const a of chainNumbers) {
for (const b of chainNumbers) {
for (const c of chainNumbers) {
for (const op1 of chainOps) {
for (const op2 of chainOps) {
test(`Chaining: ${a} ${op1} ${b} ${op2} ${c}`, () => {
pressNumber(a);
buttonPress(op1);
pressNumber(b);
buttonPress(op2); // This will execute the first operation
pressNumber(c);
buttonPress('='); // This will execute the second operation
// Calculator does sequential evaluation: (a op1 b) op2 c
const expected = binaryOps[op2](binaryOps[op1](a, b), c);
checkDisplay(expected);
});
}
}
}
}
}
// --- Generated Tests: Trigonometry ---
const trigOps = {
'sin': { fn: Math.sin, name: 'sin' },
'cos': { fn: Math.cos, name: 'cos' },
'tan': { fn: Math.tan, name: 'tan' },
};
const trigAngles = [0, 30, 45, 60, 90, 180, 270, 360, Math.PI/6, Math.PI/4, Math.PI/2, Math.PI, 3*Math.PI/2, 2*Math.PI];
// Test in DEG mode (default)
for (const op in trigOps) {
for (const angle of trigAngles) {
test(`Trig (deg): ${trigOps[op].name}(${angle})`, () => {
pressNumber(angle);
buttonPress(op);
const expected = trigOps[op].fn(angle * Math.PI / 180);
checkDisplay(expected);
});
}
}
// Test in RAD mode
test('Switch to RAD mode', () => {
buttonPress('angleMode');
assert.strictEqual(scientificOperators.angleMode.val, 'rad');
}); });
test('Subtraction', () => { for (const op in trigOps) {
press('9-5='); for (const angle of trigAngles) {
checkDisplay('4'); // tan(pi/2) is Infinity, which the calculator can handle
if (op === 'tan' && (Math.abs(angle - Math.PI/2) < 1e-9 || Math.abs(angle - 3*Math.PI/2) < 1e-9)) {
test(`Trig Edge Case (rad): tan(${angle})`, () => {
pressNumber(angle);
buttonPress('tan');
checkDisplay(Infinity);
});
continue;
}
test(`Trig (rad): ${trigOps[op].name}(${angle})`, () => {
pressNumber(angle);
buttonPress(op);
const expected = trigOps[op].fn(angle);
checkDisplay(expected);
});
}
}
test('Switch back to DEG mode for subsequent tests', () => {
buttonPress('angleMode');
assert.strictEqual(scientificOperators.angleMode.val, 'deg');
}); });
test('Multiplication', () => {
press('3*4=');
checkDisplay('12');
});
test('Division', () => { // --- Specific Functionality Tests ---
press('10/2=');
checkDisplay('5');
});
test('Chained operations (no precedence)', () => {
press('2+3*4='); // (2+3)*4 = 20
checkDisplay('20');
});
test('Decimal numbers', () => {
press('1.5+2.5=');
checkDisplay('4');
});
test('Clear and All Clear', () => { test('Clear and All Clear', () => {
press('123+'); press('123+');
@@ -191,54 +321,10 @@ test('Clear and All Clear', () => {
checkDisplay('1'); checkDisplay('1');
}); });
test('Negative result', () => {
press('5-10=');
checkDisplay('-5');
});
test('Negative input', () => {
press('5*');
press('2');
press('N'); // make it -2
press('=');
checkDisplay('-10');
});
test('Division by zero', () => {
press('5/0=');
checkDisplay('INF');
});
test('Percentage', () => {
press('200*5%=');
checkDisplay('10');
});
test('Square root', () => {
press('16');
press('r');
checkDisplay('4');
press('='); // pressing equals after unary op
checkDisplay('4');
});
test('Square', () => {
press('5');
press('s');
checkDisplay('25');
press('=');
checkDisplay('25');
});
test('Inverse', () => {
press('4');
press('i');
checkDisplay('0.25');
});
test('Backspace', () => { test('Backspace', () => {
press('123B'); press('123.45B');
checkDisplay('123.4');
press('BB');
checkDisplay('12'); checkDisplay('12');
press('B'); press('B');
checkDisplay('1'); checkDisplay('1');
@@ -255,6 +341,11 @@ test('Repeated equals', () => {
checkDisplay('8'); checkDisplay('8');
press('='); press('=');
checkDisplay('11'); checkDisplay('11');
press('R'); press('R');
press('10-2=');
checkDisplay('8');
press('=');
checkDisplay('6');
}); });
test('Operator change', () => { test('Operator change', () => {
@@ -267,42 +358,5 @@ test('Long number with separators', () => {
checkDisplay('1,234,567'); checkDisplay('1,234,567');
}); });
test('Sine (deg)', () => {
press('30');
buttonPress('sin');
checkDisplay('0.5');
press('R'); press('R');
press('90');
buttonPress('sin');
checkDisplay('1');
press('R'); press('R');
press('180');
buttonPress('sin');
checkDisplay('0');
});
test('Cosine (deg)', () => {
press('60');
buttonPress('cos');
checkDisplay('0.5');
press('R'); press('R');
press('0');
buttonPress('cos');
checkDisplay('1');
press('R'); press('R');
press('90');
buttonPress('cos');
checkDisplay('0');
});
test('Angle mode switch to radians and back', () => {
buttonPress('angleMode'); // now rad
// The buttonPress function doesn't return the state, but we can check the object it modifies
assert.strictEqual(scientificOperators.angleMode.val, 'rad');
buttonPress('angleMode'); // back to deg
assert.strictEqual(scientificOperators.angleMode.val, 'deg');
});
// Run all the defined tests // Run all the defined tests
runTests(); runTests();