Skip to content
Related Articles

Related Articles

Create a shopping cart application and test case execution based on ReactJS

View Discussion
Improve Article
Save Article
Like Article
  • Difficulty Level : Medium
  • Last Updated : 29 Mar, 2022

Let us see a shopping cart application using JSX as the frontend. It is an XML/HTML-like syntax widely got used by React that extends ECMAScript and hence XML/HTML-like text also can be applied with JavaScript/React code. 

Creating React App and Installing Modules:

Step 1: Create the react application by using

npx create-react-app <your foldername>
Eg: npx create-react-app shoppingcart

Step 2: Move to the folder

cd shoppingcart

Step 3: Install the required dependencies

npm install @babel/core
npm i babel-runtime#Here i stands for install
npm i @testing-library/jest-dom
npm i @testing-library/react
npm i @testing-library/user-event
npm i autoprefixer
npm i enzyme
npm i enzyme-adapter-react-16
npm i react
npm i react-dom
npm i react-scripts

OR, Instead of doing one by one, we can specify everything inside the package.json as given below and from the command prompt we can give as:

npm install

It will take care of installing all the packages that got mentioned inside the dependencies. Packages that got used can be verified from package.json:

package.json

{
      "name": "shoppingcart",
      "version": "1.0.0",
      "description": "shoppingcart",
      "main": "app/main.jsx",
      "scripts": {
        "lint": "eslint 'app/**/*.@(js|jsx)'",
        "test": "react-scripts test",
        "start": "react-scripts start",
        "build": "react-scripts build",
        "eject": "react-scripts eject"
      },
      "dependencies": {
        "babel-runtime": "~6.2.0",
        "@testing-library/jest-dom": "^4.2.4",
        "@testing-library/react": "^9.5.0",
        "@testing-library/user-event": "^7.2.1",
        "autoprefixer": "^9.8.0",
        "enzyme": "^3.11.0",
        "enzyme-adapter-react-16": "^1.15.5",
        "react": "^16.14.0",
        "react-dom": "^16.14.0",
        "react-scripts": "3.4.3"
      },
      "eslintConfig": {
        "extends": "react-app"
      },
      "browserslist": {
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ],
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      },
      "keywords": [
        "react",
        "test",
        "enzyme"
      ],
      "pre-commit": [
        "lint"
      ],
      "devDependencies": {
        "babel-eslint": "~4.1.6",
        "chai": "^3.4.1",
        "html-webpack-plugin": "^5.3.2",
        "react-addons-test-utils": "^15.4.1",
        "webpack": "^5.55.1",
        "webpack-cli": "^4.8.0",
        "webpack-dev-server": "^4.3.0",
        "jsdom": "^7.2.2"
      }
}

Project folder structure: The project should look like this:

 

Example: Let’s Start the project:

App.js




import React from 'react';
import './App.css';
  
// That means it is referring the jsx file 
// present under src/app/components folder
import App1 from './app/components/App';
  
function App() {
    return (
        <div className="App">
            <App1 />
        </div>
    );
}
  
export default App;

App.css: For beautification of the project

App.css




.App {
    text-align: center;
}
  
.App-logo {
    height: 40vmin;
    pointer-events: none;
}
  
@media (prefers-reduced-motion: no-preference) {
    .App-logo {
        animation: App-logo-spin infinite 20s linear;
    }
}
  
.App-header {
    background-color: #282c34;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: calc(10px + 2vmin);
    color: white;
}
  
.App-link {
    color: #006400;
}
  
@keyframes App-logo-spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}
  
.main-wrapper {
    display: flex;
    justify-content: center;
}
  
table {
    margin: 1rem;
}
  
table th td,
table tr td {
    border: 1px solid black;
    border-collapse: collapse;
}
  
form {
    margin: 1rem;
}
  
input {
    line-height: 1.4rem;
    margin-left: 1rem;
}
  
h5 {
    display: block;
}
  
form button {
    line-height: 1.4rem;
    background-color: #ffffff;
    cursor: pointer;
}
  
.cities-wrapper {
    border: 1px solid black;
    margin: 1rem;
    padding: 1rem;
    align-items: flex-end;
    height: fit-content;
}
  
ul {
    float: center;
    margin-top: 0;
}
  
.center {
    margin-left: auto;
    margin-right: auto;
}
  
.shelf-wrapper {
    text-align: left;
    display: flex;
    justify-content: center;
}
  
.shelf-wrapper h4 {
    text-align: center;
}
  
.shelf-wrapper .shelf {
    width: 19%;
    border: 1px solid black;
    display: inline-block;
    min-height: 15rem;
}
  
.shelf span {
    /* width: 60%; */
}
  
.shelf button {
    float: right;
    /* width: 40%; */
}
  
.book-wrapper span {
    width: 100%;
}
  
.shelf table tr td {
    border: none;
}

ItemJSON.js: As this project does not involve any database, let the items be picked from “ItemJSON.js”. It is under src >> app >> Items >> ItemJSON.js

ItemJSON.js




import { EventEmitter } from 'events';
import assign from 'object-assign';
  
// Initially specifying the constant items just as an sample
const ProductStore = assign({}, EventEmitter.prototype, {
    items: {
        products: [
            { productId: 0, productName: 'Samsung'
                productPrice: 10000, productQuantity: 2 },
            { productId: 1, productName: 'Motorola'
                productPrice: 7000, productQuantity: 3 },
            { productId: 2, productName: 'Redmi'
                productPrice: 8000, productQuantity: 4 },
        ]
    },
  
    nextproductId: 3,
      
    // To get all the items and display in the screen
    getAll: function getAll() {
        return this.items;
    },
  
    emitChange: function emitChange() {
        this.emit('change');
    },
      
    // When an item is added
    addChangeListener: function addChangeListener(callback) {
        this.on('change', callback);
    },
      
    // When an item is removed
    removeChangeListener: function removeChangeListener(callback) {
        this.removeListener('change', callback);
    },
  
    addNewProducts: function addNewProducts(product) {
        const products = this.items.products;
        if (!products || 
            typeof this.items.products.length !== 'number') {
            this.items.products = [];
        }
        product.productId = this.nextproductId++;
        product.done = false;
        this.items.products.push(product);
    },
  
    deleteProducts: function deleteProducts(productId) {
        this.items.products = this.items.products.filter(
            (product) => product.productId !== productId);
    }
});
  
export default ProductStore;

Start The Application: Write the below command to start the application. The project starts in 3000 port

npm start

Output:

 

In continuation, the Required code can be found from App.jsx

App.jsx




import React from 'react';
import AddItems from './AddItems';
import List from './List';
  
export default class App extends React.Component {
    render() {
        return (
            <div>
                <h1>Available Products</h1>
                // List.jsx is enclosed
                <List />
                <AddItems.jsx is enclosed
                <AddItems />
            </div>
        );
    }
}

List.jsx: We have the option to add the items as well as deletion the items

List.jsx




import React from 'react';
import ItemJSON from '../Items/ItemJSON';
import ListItems from './ListItems';
  
export default class ProductList extends React.Component {
    constructor(props) {
        super(props);
        this.state = ItemJSON.getAll();
    }
  
    componentDidMount() {
        ItemJSON.addChangeListener(this._onChange.bind(this));
    }
  
    componentWillUnmount() {
        ItemJSON.removeChangeListener(this._onChange.bind(this));
    }
  
    _onChange() {
        this.setState(ItemJSON.getAll());
    }
  
    render() {
        const ListItemsList = this.state.products.map(
        product => {
            return (
                <ListItems key={product.productId} 
                    product={product} />
            );
        });
        return (
            <center>
                  // All the items present in 
                // ItemJSON.js is displayed here
                <ul>{ListItemsList}</ul>
            </center>
        );
    }
}

Output:

 

On entering product details and clicking of “add” button, the below functionality occurs:

Javascript




import React from 'react';
import ItemJSON from '../Items/ItemJSON';
  
export default class AddItems extends React.Component {
    //will pick the added product and added
    addItems() {
        const newProductName = this.refs.product.value;
        const newPrice = this.refs.price.value;
        const newQuantity = this.refs.quantity.value;
        if (newProductName) {
            ItemJSON.addNewProducts({
                productName: newProductName,
                productPrice: newPrice,
                productQuantity: newQuantity
            });
            ItemJSON.emitChange();
            this.refs.product.value = '';
            this.refs.price.value = '';
            this.refs.quantity.value = '';
        }
    }
  
    render() {
        return (
            <center>
                <div className="add-todo">
                    <table >
                        <thead>
                            <tr>
                                <th>Product Name</th>
                                <th>Price</th>
                                <th>Quantity</th>
                                <th>Action</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td><input type="text" 
                                    placeholder="Add Product Name" 
                                    ref="product" /></td>
                                <td><input type="text" 
                                    placeholder="Add Price" 
                                    ref="price" /></td>
                                <td><input type="text" 
                                    placeholder="Add Quantity" 
                                    ref="quantity" /></td>
                                <td><button className="add-button" 
                                    onClick={this.addItems.bind(this)}>
                                    Add
                                </button></td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </center>
        );
    }
}

deletion of a product: Let us try to delete Redmi from the above list. Required code for doing deletion is in ListItems.jsx

ListItems.jsx




import React from 'react';
import ItemJSON from '../Items/ItemJSON';
export default class ListItems extends React.Component {
    // This code is meant for deletion 
    deleteProduct(e) {
        e.preventDefault();
        ItemJSON.deleteProducts(this.props.product.productId);
        ItemJSON.emitChange();
    }
  
    render() {
        const product = this.props.product;
  
        return (
            //    displaying available products and it 
            // is having delete action
            <li>
                <table border="3">
                    <tbody>
                        <tr>
                            <td width="100px">
                                <span className={`todo-text`} >
                                {product.productName} </span>
                            </td>
                            <td width="100px"><span 
                                className={`todo-text`}> 
                                {product.productPrice}</span>
                            </td>
                            <td width="100px"><span 
                                className={`todo-text`}>
                                {product.productQuantity}</span>
                            </td>
                            <td width="100px"><button 
                                className="delete" 
                                onClick={this.deleteProduct.bind(this)}> 
                                Delete </button>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </li>
        );
    }
}

Output:

 

We can test the functionality of the project as below:

App.test.js




import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import { expect } from 'chai';
import { shallow, mount, configure } from 'enzyme';
import TestUtils from 'react-dom/test-utils';
import App from './app/components/App';
import jsdom from 'jsdom';
import { findDOMNode } from 'react-dom';
  
configure({ adapter: new Adapter() });
  
beforeAll(() => {
    global.fetch = jest.fn();
    // window.fetch = jest.fn(); if running browser environment
});
  
let wrapper;
beforeEach(() => {
    wrapper = shallow(< App />, { disableLifecycleMethods: true });
});
  
afterEach(() => {
    wrapper.unmount();
});
  
if (typeof document === 'undefined') {
    global.document = jsdom.jsdom(
        '<!doctype html><html><body></body></html>');
    global.window = document.defaultView;
    global.navigator = global.window.navigator;
}
  
  
describe('DOM Rendering', function () {
    it('Add functionality to add new products 
        by clicking add', function () {
        const app = TestUtils.renderIntoDocument(<App />);
        const appDOM = findDOMNode(app);
        let productItemsLength = 
            appDOM.querySelectorAll('.todo-text').length;
  
  
        let addInput = appDOM.querySelector('input');
        addInput.value = 'Add item';
        let addButton = appDOM.querySelector('.add-todo button');
        TestUtils.Simulate.click(addButton);
        console.log(appDOM.querySelectorAll('.todo-text').length);
        expect(appDOM.querySelectorAll('.todo-text')
            .length).to.be.equal(productItemsLength + 3);  
            // As after adding we will get additional value 3.
    });
});
  
describe('DOM Rendering', function () {
    it('On deleting, the item should get deleted', function () {
        const app = TestUtils.renderIntoDocument(<App />);
        let productItems = TestUtils
            .scryRenderedDOMComponentsWithTag(app, 'li');
        let productLength = productItems.length;
        let deleteButton = productItems[0]
            .querySelector('button');
        TestUtils.Simulate.click(deleteButton);
        let productItemsAfterClick = TestUtils
            .scryRenderedDOMComponentsWithTag(app, 'li');
        expect(productItemsAfterClick.length)
            .to.equal(productLength - 1);
    });
});
  
describe('Enzyme Shallow', function () {
    it('App\'s title should be Available Products', function () {
        let app = shallow(<App />);
        expect(app.find('h1').text())
            .to.equal('Available Products');
    });
});
  
describe('Enzyme Mount', function () {
    it('Delete An Item', function () {
        let app = mount(<App />);
        let itemLength = app.find('li').length;
        app.find('button.delete').at(0).simulate('click');
        expect(app.find('li').length).to.equal(itemLength - 1);
    });
});

The test script can be tested in the below way:

npm test

Output:

 


My Personal Notes arrow_drop_up
Recommended Articles
Page :

Start Your Coding Journey Now!