Build a Cash Register Project

My code for “Build a Cash Register Project” does not pass JS tests. I can’t figure out what the problem is.
The case is as I say in “manual” mode my code passes all tests, i.e. code performs all tests correctly when I just input them manually in corresponding fields of my html “interface” and click its corresponding button “purchase”. Every test passes correctly, i.e. I can see all nesessary messages. But when I click “Run tests” almost all JS tests do not pass.
I can’t figure out where the error is. What do I not see? Any help is appreciated! Thank you!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cash Register</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h1>Cash Register</h1>
    <div class="container">
        <label for="price">Enter Price:</label>
        <input type="number" id="price" step="0.01">
        <label for="cash">Enter Cash Provided:</label>
        <input type="number" id="cash" step="0.01">
        <br>
        <button id="purchase-btn">Purchase</button>
        <h2>Change Due:</h2>
        <div id="change-due" class="changeDue"></div>
        <h2>Cash in Drawer:</h2>
        <ul id="cid-display"></ul>
        <button id="reset-btn">Reset</button>
        <button id="clear-btn">Clear</button>
    </div>

    <script src="script.js"></script>
</body>
</html>
body {
    font-family: sans-serif;
    height: 80vh;
    color: #376a81;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 5px;
    background-image: linear-gradient(#ecf9ff, #fff,  #fff, #ecf9ff );
}

.container {
    width: 400px;
    margin: 5px 0;
    padding: 0;
    border: 1px solid #306686;
    justify-items: center;
    border-radius: 20px;
    background-color: #ecf9ff;
    padding: 30px;
    box-shadow: 0 0 10px rgba(34, 126, 151, 0.679);
    text-align: center;
}

h1, h2 {
    margin: 20px;
}

label {
    display: block;
    margin: 8px;
    font-weight: bold;
}

input[type="number"] {
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    width: 200px;
    box-sizing: border-box;
    margin-bottom: 20px;
}

button {
    margin-right: 5px;
    padding: 8px 15px;
    background-color: #4c91af;
    color: white;
    border: none;
    cursor: pointer;
    border-radius: 6px;
}

button:hover {
    background-color: #0056b3;
}

.drawer-info h2 {
    margin-top: 25px;
}

#cid-display {
    list-style: none;
    padding: 0;
    text-align: left;
    font-weight: bold;
}

#cid-display li {
    margin-bottom: 5px;
    color: #4c91af;
}

.status-closed, .status-nochange, .status-insufficient, .status-open {
    display: block;
    flex-direction: column;
    width: 150px;
    font-size: 18px;
    font-weight: bold;
    text-align: center;
}

.status-closed, .status-nochange {
    color: darkgoldenrod;
}

.status-closed {
    text-align: right;
}
.status-open {
    color: green;
    text-align: right;
}

.status-insufficient {
    color: red;
}
const priceInput = document.getElementById('price');
let cashInput = document.getElementById('cash');
let purchaseBtn = document.getElementById('purchase-btn');
let changeDueElement = document.getElementById('change-due');
let cidDisplay = document.getElementById('cid-display');

let price = 3.26;
priceInput.value = price; //Display price for the first time
let cid = [
    ["PENNY", 1.01],
    ["NICKEL", 2.05],
    ["DIME", 3.1],
    ["QUARTER", 4.25],
    ["ONE", 90],
    ["FIVE", 55],
    ["TEN", 20],
    ["TWENTY", 60],
    ["ONE HUNDRED", 100]
];

const currencyUnits = [
    ["ONE HUNDRED", 100],
    ["TWENTY", 20],
    ["TEN", 10],
    ["FIVE", 5],
    ["ONE", 1],
    ["QUARTER", 0.25],
    ["DIME", 0.1],
    ["NICKEL", 0.05],
    ["PENNY", 0.01]
];

function updateCIDDisplay() {   // Cash in drawer
    cidDisplay.innerHTML = '';
    cid.forEach(currency => {
        const listItem = document.createElement('li');
        listItem.textContent = `${currency[0]}: $${currency[1].toFixed(2)}`;
        cidDisplay.appendChild(listItem);
    });
}

updateCIDDisplay();                             // Initial display of cash in drawer

purchaseBtn.addEventListener('click', () => {   // Main event (when purchase is clicked)
    const cash = parseFloat(cashInput.value);   // Amount of cash provided by the customer
    price = parseFloat(priceInput.value);       // Update price on each click
    const changeDue = cash - price;             // Amount of change

    if (isNaN(cash)) {                          // If cash is none
        alert("Please enter the amount of cash provided by the customer.");
        return;
    }

    if (cash < price) {                         // If cash is less than price
        alert("Customer does not have enough money to purchase the item");
        changeDueElement.textContent = "";
        return;
    } else if (cash === price) {                // If cash is equal to price
        changeDueElement.className = "status-nochange";
        changeDueElement.textContent = "No change due - customer paid with exact cash";
        return;
    } else {                                    // If change is needed
        handlePurchase(changeDue, [...cid]);    // Pass a copy of cid
    }
});

function handlePurchase(changeDueAmount, currentCID) {  // Scenario if change is needed

    let changeToCustomer = [];
    let totalCID = currentCID.reduce((acc, curr) => acc + curr[1], 0);  // Available cash in drawer

    if (totalCID < changeDueAmount) {           // If cash in drawer less than change
        changeDueElement.textContent = "Status: INSUFFICIENT_FUNDS";
        changeDueElement.className = "status-insufficient";
        return;
    } else if (totalCID === changeDueAmount) {  // If cash in drawer equal to change
        changeDueElement.textContent = "Status: CLOSED " + currentCID.map(item => `${item[0]}: $${item[1].toFixed(2)}`).join(" ");
        changeDueElement.className = "status-closed";
        cid = currentCID.map(item => [item[0], 0]); // Clear CID
        updateCIDDisplay();
        return;
    } else {                                    // If cash in drawer is more than change
        for (let i = 0; i < currencyUnits.length; i++) { // Main loop. Search of the available cash at face value from higher to lower for issuance.
            const currencyName = currencyUnits[i][0];
            const currencyValue = currencyUnits[i][1];
            let availableAmount = currentCID.find(item => item[0] === currencyName)[1];
            let amountToReturn = 0;

            while (changeDueAmount >= currencyValue && availableAmount > 0) {
                changeDueAmount -= currencyValue;
                changeDueAmount = parseFloat(changeDueAmount.toFixed(2)); // Prevent floating point errors
                availableAmount -= currencyValue;
                amountToReturn += currencyValue;
            }

            if (amountToReturn > 0) {
                changeToCustomer.push([currencyName, amountToReturn]);
                currentCID = currentCID.map(item =>
                    item[0] === currencyName ? [currencyName, parseFloat((item[1] - amountToReturn).toFixed(2))] : item
                );
            }
        }

        if (changeDueAmount > 0) {                  // Change is more than avalible cash
            changeDueElement.textContent = "Status: INSUFFICIENT_FUNDS";
            changeDueElement.className = "status-insufficient";
        } else {                                    // Change to be given & renew available cash in drawer
            changeDueElement.textContent = "Status: OPEN " + changeToCustomer.map(item => `${item[0]}: $${item[1].toFixed(2)}`).join(" ");
            changeDueElement.className = "status-open";
            cid = currentCID;
            updateCIDDisplay();
        }
    }
}

document.getElementById("reset-btn").addEventListener('click', () => { // Reset everything
    priceInput.value = '11.95';
    changeDueElement.textContent = '';
    changeDueElement.className = '';
    cashInput.value = '';
    cid = [
        ["PENNY", 1.01],
        ["NICKEL", 2.05],
        ["DIME", 3.1],
        ["QUARTER", 4.25],
        ["ONE", 90],
        ["FIVE", 55],
        ["TEN", 20],
        ["TWENTY", 60],
        ["ONE HUNDRED", 100]
    ];

    updateCIDDisplay();
});

document.getElementById("clear-btn").addEventListener('click', () => { // Clear input
    changeDueElement.textContent = '';
    changeDueElement.className = '';
    cashInput.value = '';
    updateCIDDisplay();
});

https://forum.freecodecamp.org/t/build-a-cash-register-project-build-a-cash-register/743642/4?u=fcc4b6d10c4-b540-4e2

Take a look at this post from @ILM showing where you can find tests for this project.

CORRECTION: @ILM has created a file of code that simulates the tests for this project. You can add that code to the end of your script file, then check the output in the console to see what your code is generating compared to what the tests expect.

1 Like

not exactly, that’s a file I made myself with something similar to the test code, the test code is in the freeCodeCamp’s codebase

you can share the code or the file, you do not need to relink there

2 Likes
  • in test process, price is not input from UI input element, but global variable price

  • when printing change details, after checking requirements we can find:

  1. order by unit in descending
  2. ignore zeros
  3. omit tailing zero. For example, not 1.00, but 1. For this project, it is just ok to use simple toString() (or imciplictly console.log()) and no need to use toFixed()
1 Like

I did it. Thanks to everyone for the help!
I am writing late - my health is a bit weak (
The code in the link helped me figure it out, thanks for that. I found an error with the data type and remade one loop. At the same time, one problem arose (as far as I understand) because the flow of multiple tests does not restart the application/does not reload the page, so my code needed to be corrected. Whatever the case, the experience was useful.