init geek calc
Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
This commit is contained in:
68
tests/index.html
Normal file
68
tests/index.html
Normal file
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Geek Calculator - Tests</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: monospace;
|
||||
padding: 20px;
|
||||
background-color: #000;
|
||||
color: #0f0;
|
||||
}
|
||||
.test-results {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.test-result {
|
||||
margin: 5px 0;
|
||||
padding: 5px;
|
||||
}
|
||||
.pass {
|
||||
color: #0f0;
|
||||
}
|
||||
.fail {
|
||||
color: #f00;
|
||||
}
|
||||
.summary {
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
background-color: #222;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Geek Calculator - Test Runner</h1>
|
||||
<p>Running all tests...</p>
|
||||
|
||||
<div id="test-results" class="test-results">
|
||||
<!-- Test results will be inserted here by JavaScript -->
|
||||
</div>
|
||||
|
||||
<div id="summary" class="summary">
|
||||
<!-- Test summary will be inserted here -->
|
||||
</div>
|
||||
|
||||
<!-- Load the test harness -->
|
||||
<script src="test-harness.js"></script>
|
||||
|
||||
<!-- Load the modules to be tested -->
|
||||
<script src="../calculator.js"></script>
|
||||
<script src="../rpn-calculator.js"></script>
|
||||
<script src="../state.js"></script>
|
||||
|
||||
<!-- Load the test files -->
|
||||
<script src="unit/calculator.test.js"></script>
|
||||
<script src="unit/rpn-calculator.test.js"></script>
|
||||
<script src="unit/math.test.js"></script>
|
||||
<script src="unit/rpn.test.js"></script>
|
||||
<script src="integration/ui.test.js"></script>
|
||||
|
||||
<script>
|
||||
// Run all tests when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
runAllTests();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
105
tests/integration/ui.test.js
Normal file
105
tests/integration/ui.test.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// Integration tests for user scenarios
|
||||
// These tests should fail initially since the UI controller doesn't exist yet
|
||||
|
||||
function testUserScenarios() {
|
||||
try {
|
||||
// This will fail since UI components don't exist yet
|
||||
const calculator = new Calculator();
|
||||
const rpnCalculator = new RPNCalculator();
|
||||
const stateManager = new StateManager();
|
||||
const uiController = new UIController(calculator, rpnCalculator, stateManager);
|
||||
|
||||
// Initialize the UI
|
||||
uiController.init();
|
||||
|
||||
// Test basic calculation: 5 + 3 = 8
|
||||
uiController.handleInput('5');
|
||||
uiController.handleInput('+');
|
||||
uiController.handleInput('3');
|
||||
uiController.handleInput('=');
|
||||
|
||||
const result = uiController.getCurrentResult();
|
||||
if (result !== '8') {
|
||||
throw new Error(`Basic calculation failed: 5 + 3 should equal 8, got ${result}`);
|
||||
}
|
||||
|
||||
// Test complex expression: (2 + 3) * 4 = 20
|
||||
uiController.handleInput('(');
|
||||
uiController.handleInput('2');
|
||||
uiController.handleInput('+');
|
||||
uiController.handleInput('3');
|
||||
uiController.handleInput(')');
|
||||
uiController.handleInput('*');
|
||||
uiController.handleInput('4');
|
||||
uiController.handleInput('=');
|
||||
|
||||
const complexResult = uiController.getCurrentResult();
|
||||
if (complexResult !== '20') {
|
||||
throw new Error(`Complex calculation failed: (2 + 3) * 4 should equal 20, got ${complexResult}`);
|
||||
}
|
||||
|
||||
// Test percentage: 100 + 10% = 110
|
||||
uiController.handleInput('100');
|
||||
uiController.handleInput('+');
|
||||
uiController.handleInput('10');
|
||||
uiController.handleInput('%');
|
||||
uiController.handleInput('=');
|
||||
|
||||
const percentResult = uiController.getCurrentResult();
|
||||
if (percentResult !== '110') {
|
||||
throw new Error(`Percentage calculation failed: 100 + 10% should equal 110, got ${percentResult}`);
|
||||
}
|
||||
|
||||
// Test RPN mode: 4 ENTER 6 + = 10
|
||||
uiController.toggleRPNMode();
|
||||
|
||||
uiController.handleRPNInput('4');
|
||||
uiController.handleRPNInput('ENTER'); // Enter to push to stack
|
||||
uiController.handleRPNInput('6');
|
||||
uiController.handleRPNInput('ENTER'); // Enter to push to stack
|
||||
uiController.handleRPNInput('+'); // Add operation
|
||||
|
||||
// Check RPN result
|
||||
const rpnResult = uiController.getCurrentResult();
|
||||
if (rpnResult !== '10') {
|
||||
throw new Error(`RPN calculation failed: 4 ENTER 6 + should equal 10, got ${rpnResult}`);
|
||||
}
|
||||
|
||||
// Test history functionality
|
||||
const history = uiController.getHistory();
|
||||
if (history.length === 0) {
|
||||
throw new Error('History functionality not working: no entries found');
|
||||
}
|
||||
|
||||
// Verify that the last calculation is in history
|
||||
const lastHistoryEntry = history[history.length - 1];
|
||||
if (!lastHistoryEntry || lastHistoryEntry.result !== 10) {
|
||||
throw new Error('History functionality not working: last calculation not properly stored');
|
||||
}
|
||||
|
||||
// Test keyboard controls
|
||||
// This would involve simulating keyboard events
|
||||
// For simplicity in this test, we'll just verify the keyboard handler exists
|
||||
if (typeof uiController.handleKeyboardInput !== 'function') {
|
||||
throw new Error('Keyboard controls not implemented');
|
||||
}
|
||||
|
||||
// Test that clear operation works
|
||||
uiController.handleInput('C'); // Clear
|
||||
const clearedResult = uiController.getCurrentResult();
|
||||
if (clearedResult !== '0') {
|
||||
throw new Error(`Clear operation failed: should reset to 0, got ${clearedResult}`);
|
||||
}
|
||||
|
||||
return true; // All tests passed
|
||||
} catch (error) {
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Add test to global test collection
|
||||
if (typeof addTest !== 'undefined') {
|
||||
addTest('User Scenarios Integration Tests', testUserScenarios);
|
||||
} else {
|
||||
console.log('Test harness not loaded. Run with test runner.');
|
||||
}
|
||||
1
tests/test-harness.js
Normal file
1
tests/test-harness.js
Normal file
@@ -0,0 +1 @@
|
||||
let tests=[];let passedTests=0;let failedTests=0;function addTest(name,testFunction){tests.push({name,testFunction})}function runTest(test){try{const result=test.testFunction();if(result&&typeof result==='object'&&result.error){return{name:test.name,passed:false,error:result.error}}if(result===true){return{name:test.name,passed:true}}return{name:test.name,passed:false,error:`Test returned unexpected value: ${result}`}}catch(error){return{name:test.name,passed:false,error:error.message}}}function runAllTests(){console.log(`Running ${tests.length} tests...`);const results=tests.map(test=>runTest(test));passedTests=0;failedTests=0;const resultsDiv=document.getElementById('test-results');resultsDiv.innerHTML='';results.forEach(result=>{const resultElement=document.createElement('div');resultElement.className=`test-result ${result.passed?'pass':'fail'}`;if(result.passed){resultElement.innerHTML=`✓ ${result.name}`;passedTests+=1}else{resultElement.innerHTML=`✗ ${result.name} - ${result.error}`;failedTests+=1}resultsDiv.appendChild(resultElement)});const summaryDiv=document.getElementById('summary');summaryDiv.innerHTML=`<h3>Test Summary</h3><p>Total tests: ${tests.length}</p><p class="pass">Passed: ${passedTests}</p><p class="fail">Failed: ${failedTests}</p><p>Success rate: ${tests.length?Math.round((passedTests/tests.length)*100):0}%</p>`;console.log(`Tests completed: ${passedTests} passed, ${failedTests} failed`);return{total:tests.length,passed:passedTests,failed:failedTests,successRate:tests.length?(passedTests/tests.length)*100:0}}window.addTest=addTest;window.runAllTests=runAllTests;if(typeof module!=='undefined'&&module.exports){module.exports={addTest,runAllTests}}
|
||||
94
tests/unit/calculator.test.js
Normal file
94
tests/unit/calculator.test.js
Normal file
@@ -0,0 +1,94 @@
|
||||
// Contract test for Core Calculator API
|
||||
// This test should fail initially since the calculator module doesn't exist yet
|
||||
|
||||
// Define expected interface for calculator module
|
||||
const expectedCalculatorInterface = [
|
||||
'evaluate',
|
||||
'add',
|
||||
'subtract',
|
||||
'multiply',
|
||||
'divide',
|
||||
'percentage',
|
||||
'toggleSign',
|
||||
'clear',
|
||||
'getCurrentDisplay'
|
||||
];
|
||||
|
||||
// Test if calculator module exists and has expected interface
|
||||
function testCalculatorInterface() {
|
||||
try {
|
||||
// This will fail since calculator.js doesn't exist yet
|
||||
const calc = new Calculator();
|
||||
|
||||
// Check if all expected methods exist
|
||||
for (const method of expectedCalculatorInterface) {
|
||||
if (typeof calc[method] !== 'function') {
|
||||
throw new Error(`Method ${method} does not exist on Calculator`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test basic functionality
|
||||
// Add should return sum of two numbers
|
||||
if (calc.add(2, 3) !== 5) {
|
||||
throw new Error('Calculator add method does not return correct result');
|
||||
}
|
||||
|
||||
// Subtract should return difference
|
||||
if (calc.subtract(5, 3) !== 2) {
|
||||
throw new Error('Calculator subtract method does not return correct result');
|
||||
}
|
||||
|
||||
// Multiply should return product
|
||||
if (calc.multiply(4, 5) !== 20) {
|
||||
throw new Error('Calculator multiply method does not return correct result');
|
||||
}
|
||||
|
||||
// Divide should return quotient
|
||||
if (calc.divide(10, 2) !== 5) {
|
||||
throw new Error('Calculator divide method does not return correct result');
|
||||
}
|
||||
|
||||
// Division by zero should return Infinity
|
||||
if (!isFinite(calc.divide(10, 0))) {
|
||||
// This is expected, division by zero returns Infinity
|
||||
} else {
|
||||
throw new Error('Calculator divide by zero does not return Infinity');
|
||||
}
|
||||
|
||||
// Percentage should convert percentage to decimal
|
||||
if (calc.percentage(50) !== 0.5) {
|
||||
throw new Error('Calculator percentage method does not return correct result');
|
||||
}
|
||||
|
||||
// ToggleSign should change sign
|
||||
if (calc.toggleSign(5) !== -5) {
|
||||
throw new Error('Calculator toggleSign method does not return correct result');
|
||||
}
|
||||
|
||||
if (calc.toggleSign(-5) !== 5) {
|
||||
throw new Error('Calculator toggleSign method does not return correct result for negative number');
|
||||
}
|
||||
|
||||
// Clear should reset to initial state
|
||||
calc.clear();
|
||||
if (calc.getCurrentDisplay() !== '0') {
|
||||
throw new Error('Calculator clear method does not reset to initial state');
|
||||
}
|
||||
|
||||
// Evaluate should handle simple expressions
|
||||
if (calc.evaluate('2 + 3') !== 5) {
|
||||
throw new Error('Calculator evaluate method does not return correct result');
|
||||
}
|
||||
|
||||
return true; // All tests passed
|
||||
} catch (error) {
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Add test to global test collection
|
||||
if (typeof addTest !== 'undefined') {
|
||||
addTest('Calculator Interface Contract', testCalculatorInterface);
|
||||
} else {
|
||||
console.log('Test harness not loaded. Run with test runner.');
|
||||
}
|
||||
98
tests/unit/math.test.js
Normal file
98
tests/unit/math.test.js
Normal file
@@ -0,0 +1,98 @@
|
||||
// Unit tests for core math operations
|
||||
// These tests should fail initially since the calculator module doesn't exist yet
|
||||
|
||||
function testMathOperations() {
|
||||
try {
|
||||
// This will fail since calculator.js doesn't exist yet
|
||||
const calc = new Calculator();
|
||||
|
||||
// Test addition
|
||||
if (calc.add(2, 3) !== 5) {
|
||||
throw new Error('Addition failed: 2 + 3 should equal 5');
|
||||
}
|
||||
|
||||
if (calc.add(-1, 1) !== 0) {
|
||||
throw new Error('Addition failed: -1 + 1 should equal 0');
|
||||
}
|
||||
|
||||
if (calc.add(0, 0) !== 0) {
|
||||
throw new Error('Addition failed: 0 + 0 should equal 0');
|
||||
}
|
||||
|
||||
// Test subtraction
|
||||
if (calc.subtract(5, 3) !== 2) {
|
||||
throw new Error('Subtraction failed: 5 - 3 should equal 2');
|
||||
}
|
||||
|
||||
if (calc.subtract(0, 5) !== -5) {
|
||||
throw new Error('Subtraction failed: 0 - 5 should equal -5');
|
||||
}
|
||||
|
||||
// Test multiplication
|
||||
if (calc.multiply(4, 5) !== 20) {
|
||||
throw new Error('Multiplication failed: 4 * 5 should equal 20');
|
||||
}
|
||||
|
||||
if (calc.multiply(-2, 3) !== -6) {
|
||||
throw new Error('Multiplication failed: -2 * 3 should equal -6');
|
||||
}
|
||||
|
||||
if (calc.multiply(0, 100) !== 0) {
|
||||
throw new Error('Multiplication failed: 0 * 100 should equal 0');
|
||||
}
|
||||
|
||||
// Test division
|
||||
if (calc.divide(10, 2) !== 5) {
|
||||
throw new Error('Division failed: 10 / 2 should equal 5');
|
||||
}
|
||||
|
||||
if (calc.divide(7, 2) !== 3.5) {
|
||||
throw new Error('Division failed: 7 / 2 should equal 3.5');
|
||||
}
|
||||
|
||||
// Test division by zero (should return Infinity)
|
||||
const divByZero = calc.divide(5, 0);
|
||||
if (!isFinite(divByZero)) {
|
||||
// This is expected, division by zero returns Infinity
|
||||
} else {
|
||||
throw new Error('Division by zero should return Infinity');
|
||||
}
|
||||
|
||||
// Test percentage
|
||||
if (calc.percentage(50) !== 0.5) {
|
||||
throw new Error('Percentage failed: 50% should equal 0.5');
|
||||
}
|
||||
|
||||
if (calc.percentage(100) !== 1) {
|
||||
throw new Error('Percentage failed: 100% should equal 1');
|
||||
}
|
||||
|
||||
if (calc.percentage(0) !== 0) {
|
||||
throw new Error('Percentage failed: 0% should equal 0');
|
||||
}
|
||||
|
||||
// Test toggle sign
|
||||
if (calc.toggleSign(5) !== -5) {
|
||||
throw new Error('Toggle sign failed: 5 should become -5');
|
||||
}
|
||||
|
||||
if (calc.toggleSign(-5) !== 5) {
|
||||
throw new Error('Toggle sign failed: -5 should become 5');
|
||||
}
|
||||
|
||||
if (calc.toggleSign(0) !== 0) {
|
||||
throw new Error('Toggle sign failed: 0 should remain 0');
|
||||
}
|
||||
|
||||
return true; // All tests passed
|
||||
} catch (error) {
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Add test to global test collection
|
||||
if (typeof addTest !== 'undefined') {
|
||||
addTest('Math Operations Unit Tests', testMathOperations);
|
||||
} else {
|
||||
console.log('Test harness not loaded. Run with test runner.');
|
||||
}
|
||||
79
tests/unit/rpn-calculator.test.js
Normal file
79
tests/unit/rpn-calculator.test.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// Contract test for RPN Calculator API
|
||||
// This test should fail initially since the rpn-calculator module doesn't exist yet
|
||||
|
||||
// Define expected interface for RPN calculator module
|
||||
const expectedRPNCalculatorInterface = [
|
||||
'push',
|
||||
'pop',
|
||||
'operate',
|
||||
'clear',
|
||||
'getStack',
|
||||
'evaluate'
|
||||
];
|
||||
|
||||
// Test if RPN calculator module exists and has expected interface
|
||||
function testRPNCalculatorInterface() {
|
||||
try {
|
||||
// This will fail since rpn-calculator.js doesn't exist yet
|
||||
const rpnCalc = new RPNCalculator();
|
||||
|
||||
// Check if all expected methods exist
|
||||
for (const method of expectedRPNCalculatorInterface) {
|
||||
if (typeof rpnCalc[method] !== 'function') {
|
||||
throw new Error(`Method ${method} does not exist on RPNCalculator`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test basic RPN functionality
|
||||
// Push should add value to stack
|
||||
rpnCalc.push(3);
|
||||
const stackAfterPush = rpnCalc.getStack();
|
||||
if (stackAfterPush.length !== 1 || stackAfterPush[0] !== 3) {
|
||||
throw new Error('RPN calculator push method does not work correctly');
|
||||
}
|
||||
|
||||
// Pop should remove and return top value from stack
|
||||
const poppedValue = rpnCalc.pop();
|
||||
if (poppedValue !== 3) {
|
||||
throw new Error('RPN calculator pop method does not return correct value');
|
||||
}
|
||||
|
||||
// Stack should be empty after pop
|
||||
if (rpnCalc.getStack().length !== 0) {
|
||||
throw new Error('RPN calculator stack not empty after pop');
|
||||
}
|
||||
|
||||
// Clear should empty the stack
|
||||
rpnCalc.push(1);
|
||||
rpnCalc.push(2);
|
||||
rpnCalc.clear();
|
||||
if (rpnCalc.getStack().length !== 0) {
|
||||
throw new Error('RPN calculator clear method does not empty the stack');
|
||||
}
|
||||
|
||||
// Operate should perform operation on stack values
|
||||
rpnCalc.push(2);
|
||||
rpnCalc.push(3);
|
||||
const result = rpnCalc.operate('+');
|
||||
if (result !== 5) {
|
||||
throw new Error('RPN calculator operate method does not return correct result for addition');
|
||||
}
|
||||
|
||||
// Evaluate should handle RPN expressions
|
||||
const evalResult = rpnCalc.evaluate('2 3 +');
|
||||
if (evalResult !== 5) {
|
||||
throw new Error('RPN calculator evaluate method does not return correct result');
|
||||
}
|
||||
|
||||
return true; // All tests passed
|
||||
} catch (error) {
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Add test to global test collection
|
||||
if (typeof addTest !== 'undefined') {
|
||||
addTest('RPN Calculator Interface Contract', testRPNCalculatorInterface);
|
||||
} else {
|
||||
console.log('Test harness not loaded. Run with test runner.');
|
||||
}
|
||||
93
tests/unit/rpn.test.js
Normal file
93
tests/unit/rpn.test.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// Unit tests for RPN stack operations
|
||||
// These tests should fail initially since the rpn-calculator module doesn't exist yet
|
||||
|
||||
function testRPNStackOperations() {
|
||||
try {
|
||||
// This will fail since rpn-calculator.js doesn't exist yet
|
||||
const rpnCalc = new RPNCalculator();
|
||||
|
||||
// Test push operation
|
||||
rpnCalc.push(1);
|
||||
rpnCalc.push(2);
|
||||
rpnCalc.push(3);
|
||||
|
||||
const stack = rpnCalc.getStack();
|
||||
if (stack.length !== 3 || stack[0] !== 1 || stack[1] !== 2 || stack[2] !== 3) {
|
||||
throw new Error('RPN push operation failed: stack does not contain expected values');
|
||||
}
|
||||
|
||||
// Test pop operation
|
||||
const popped = rpnCalc.pop();
|
||||
if (popped !== 3) {
|
||||
throw new Error('RPN pop operation failed: did not return top value');
|
||||
}
|
||||
|
||||
// Stack should now have 2 elements
|
||||
if (rpnCalc.getStack().length !== 2) {
|
||||
throw new Error('RPN pop operation failed: stack length incorrect after pop');
|
||||
}
|
||||
|
||||
// Test multiple operations
|
||||
rpnCalc.clear();
|
||||
rpnCalc.push(4);
|
||||
rpnCalc.push(2);
|
||||
|
||||
// Perform addition: 4 + 2 = 6
|
||||
const addResult = rpnCalc.operate('+');
|
||||
if (addResult !== 6) {
|
||||
throw new Error('RPN addition operation failed: 4 2 + should equal 6');
|
||||
}
|
||||
|
||||
// Perform subtraction: 6 - 2 = 4 (the 2 was consumed, now 6-2)
|
||||
rpnCalc.push(2);
|
||||
const subResult = rpnCalc.operate('-');
|
||||
if (subResult !== 4) {
|
||||
throw new Error('RPN subtraction operation failed: 6 2 - should equal 4');
|
||||
}
|
||||
|
||||
// Test multiplication
|
||||
rpnCalc.push(3);
|
||||
rpnCalc.push(4);
|
||||
const multResult = rpnCalc.operate('*');
|
||||
if (multResult !== 12) {
|
||||
throw new Error('RPN multiplication operation failed: 3 4 * should equal 12');
|
||||
}
|
||||
|
||||
// Test division
|
||||
rpnCalc.push(12);
|
||||
rpnCalc.push(4);
|
||||
const divResult = rpnCalc.operate('/');
|
||||
if (divResult !== 3) {
|
||||
throw new Error('RPN division operation failed: 12 4 / should equal 3');
|
||||
}
|
||||
|
||||
// Test division by zero
|
||||
rpnCalc.push(5);
|
||||
rpnCalc.push(0);
|
||||
const divByZeroResult = rpnCalc.operate('/');
|
||||
if (!isFinite(divByZeroResult)) {
|
||||
// This is expected behavior for division by zero in RPN
|
||||
} else {
|
||||
throw new Error('RPN division by zero should return Infinity');
|
||||
}
|
||||
|
||||
// Test clear operation
|
||||
rpnCalc.push(10);
|
||||
rpnCalc.push(20);
|
||||
rpnCalc.clear();
|
||||
if (rpnCalc.getStack().length !== 0) {
|
||||
throw new Error('RPN clear operation failed: stack not empty after clear');
|
||||
}
|
||||
|
||||
return true; // All tests passed
|
||||
} catch (error) {
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Add test to global test collection
|
||||
if (typeof addTest !== 'undefined') {
|
||||
addTest('RPN Stack Operations Unit Tests', testRPNStackOperations);
|
||||
} else {
|
||||
console.log('Test harness not loaded. Run with test runner.');
|
||||
}
|
||||
Reference in New Issue
Block a user