From ddf1cd14734bd2d7db06a5b6736e47e39cb4867e Mon Sep 17 00:00:00 2001 From: Muhammad Noor Ul Ain Roy Date: Thu, 13 Nov 2025 16:22:12 +0100 Subject: [PATCH 1/2] Muhammad Noor Ul Ain Roy: add comprehensive test suite for calculator order of operations --- .../activities/__tests__/Calculator.test.js | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 src/pages/activities/__tests__/Calculator.test.js diff --git a/src/pages/activities/__tests__/Calculator.test.js b/src/pages/activities/__tests__/Calculator.test.js new file mode 100644 index 0000000..705560b --- /dev/null +++ b/src/pages/activities/__tests__/Calculator.test.js @@ -0,0 +1,250 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import Calculator from '../Calculator'; + +describe('Calculator Order of Operations', () => { + describe('PEMDAS/BODMAS - Multiplication and Division before Addition and Subtraction', () => { + test('should multiply before adding: 2 + 3 × 4 = 14', () => { + render(); + + // Clear calculator first + fireEvent.click(screen.getByText('AC')); + + // Enter: 2 + 3 × 4 = + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('+')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('4')); + fireEvent.click(screen.getByText('=')); + + const display = screen.getByText('14'); + expect(display).toBeInTheDocument(); + }); + + test('should divide before subtracting: 10 - 8 / 2 = 6', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 10 - 8 / 2 = + fireEvent.click(screen.getByText('1')); + fireEvent.click(screen.getByText('0')); + fireEvent.click(screen.getByText('-')); + fireEvent.click(screen.getByText('8')); + fireEvent.click(screen.getByText('/')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('6'); + }); + + test('should multiply before adding (reversed): 5 + 2 × 3 = 11', () => { + render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 5 + 2 × 3 = + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('+')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('=')); + + const display = screen.getByText('11'); + expect(display).toBeInTheDocument(); + }); + + test('should divide before subtracting: 12 - 3 / 3 = 11', () => { + render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 12 - 3 / 3 = + fireEvent.click(screen.getByText('1')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('-')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('/')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('=')); + + const display = screen.getByText('11'); + expect(display).toBeInTheDocument(); + }); + }); + + describe('Multiple operations with mixed precedence', () => { + test('should handle: 2 + 3 × 4 - 5 = 9', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 2 + 3 × 4 - 5 = + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('+')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('4')); + fireEvent.click(screen.getByText('-')); + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('9'); + }); + + test('should handle: 5 + 2 × 3 - 4 / 2 = 9', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 5 + 2 × 3 - 4 / 2 = + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('+')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('-')); + fireEvent.click(screen.getByText('4')); + fireEvent.click(screen.getByText('/')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('9'); + }); + }); + + describe('Same precedence operations (left to right)', () => { + test('should process left to right for same precedence: 10 - 5 - 2 = 3', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 10 - 5 - 2 = + fireEvent.click(screen.getByText('1')); + fireEvent.click(screen.getByText('0')); + fireEvent.click(screen.getByText('-')); + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('-')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('3'); + }); + + test('should handle division and multiplication left to right: 12 / 3 × 2 = 8', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 12 / 3 × 2 = + fireEvent.click(screen.getByText('1')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('/')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('8'); + }); + + test('should handle multiplication and division left to right: 2 × 6 / 3 = 4', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 2 × 6 / 3 = + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('6')); + fireEvent.click(screen.getByText('/')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('4'); + }); + }); + + describe('Edge cases and special scenarios', () => { + test('should handle decimal operations with order of operations: 5 + 2.5 × 2 = 10', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: 5 + 2.5 × 2 = + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('+')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('.')); + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('10'); + }); + + test('should handle negative numbers: -5 + 3 × 2 = 1', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + + // Enter: -5 + 3 × 2 = + fireEvent.click(screen.getByText('-')); + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('+')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('x')); + fireEvent.click(screen.getByText('2')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('1'); + }); + }); + + describe('Basic calculator functionality', () => { + test('should display initial value of 0', () => { + const { container } = render(); + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('0'); + }); + + test('should clear display when AC is clicked', () => { + const { container } = render(); + + // Enter some numbers + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('8')); + + // Clear + fireEvent.click(screen.getByText('AC')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('0'); + }); + + test('should handle simple addition: 5 + 3 = 8', () => { + const { container } = render(); + + fireEvent.click(screen.getByText('AC')); + fireEvent.click(screen.getByText('5')); + fireEvent.click(screen.getByText('+')); + fireEvent.click(screen.getByText('3')); + fireEvent.click(screen.getByText('=')); + + const display = container.querySelector('#display'); + expect(display).toHaveTextContent('8'); + }); + }); +}); + From 38f96d107f094d5c2f44a7483931bec335e6e1af Mon Sep 17 00:00:00 2001 From: Muhammad Noor Ul Ain Roy Date: Thu, 13 Nov 2025 16:22:32 +0100 Subject: [PATCH 2/2] Muhammad Noor Ul Ain Roy: implement proper PEMDAS/BODMAS operator precedence and remove console.log statements --- src/pages/activities/Calculator.js | 40 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/pages/activities/Calculator.js b/src/pages/activities/Calculator.js index 8583bf2..91cfe05 100644 --- a/src/pages/activities/Calculator.js +++ b/src/pages/activities/Calculator.js @@ -15,7 +15,6 @@ class Calculator extends React.Component { let op = []; let temp = this.state.screenValue1.toString(); if(a === "=") { - console.log(temp) for (let j = 0; j < temp.length; j++) { if(temp[j] !== '/' && temp[j] !== 'x' && temp[j] !== '-' && temp[j] !== '+') { r = `${r}${temp[j]}` @@ -26,7 +25,6 @@ class Calculator extends React.Component { r = '' } } - console.log(arr) arr.push(r) if(arr[0] === '' && op[0] === '-') { arr[1]=`${op[0]}${arr[1]}` @@ -36,13 +34,10 @@ class Calculator extends React.Component { if(arr[0] === '') { arr[0] = 0 } - console.log(arr) r = ''; for(let x1 = 0; x1 < arr.length; x1++) { arr[x1] = parseFloat(arr[x1]) } - console.log(arr) - console.log(op) for(let x3 = 0; x3 < arr.length; x3++) { if(isNaN(arr[x3])) { arr[x3+1] = -arr[x3+1] @@ -53,16 +48,35 @@ class Calculator extends React.Component { if(op.length === 0){ return } + + // Two-pass algorithm for order of operations (PEMDAS/BODMAS) + // First pass: Process multiplication and division (left to right) for(let b = 0; b < op.length; b++) { - if(op[b] === '/') {r = arr[0] / arr[1]; arr.shift(); arr[0] = r} - if(op[b] === 'x') {r = arr[0] * arr[1]; arr.shift(); arr[0] = r} - if(op[b] === '-') {r = arr[0] - arr[1]; arr.shift(); arr[0] = r} - if(op[b] === '+') {r = arr[0] + arr[1]; arr.shift(); arr[0] = r} - console.log(r) + if(op[b] === '/' || op[b] === 'x') { + if(op[b] === '/') { + r = arr[b] / arr[b+1]; + } else { + r = arr[b] * arr[b+1]; + } + // Replace the two operands with the result + arr.splice(b, 2, r); + // Remove the operator + op.splice(b, 1); + // Adjust index since we removed an element + b--; + } } - console.log(r) - if(this.state.screenValue1[0] === 0) { - console.log('doin something funny') + + // Second pass: Process addition and subtraction (left to right) + for(let b = 0; b < op.length; b++) { + if(op[b] === '-') { + r = arr[b] - arr[b+1]; + } else if(op[b] === '+') { + r = arr[b] + arr[b+1]; + } + arr.splice(b, 2, r); + op.splice(b, 1); + b--; } r = r.toString() while(r.length > 15) {