Crear una aplicación de carrito de compras y ejecución de casos de prueba basada en ReactJS

Veamos una aplicación de carrito de compras que usa JSX como interfaz. Es una sintaxis similar a XML/HTML ampliamente utilizada por React que amplía ECMAScript y, por lo tanto, el texto similar a XML/HTML también se puede aplicar con código JavaScript/React. 

Creación de la aplicación React e instalación de módulos:

Paso 1: Cree la aplicación de reacción usando

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

Paso 2: Mover a la carpeta

cd shoppingcart

Paso 3: Instala las dependencias requeridas

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

O, en lugar de hacerlo uno por uno, podemos especificar todo dentro del paquete.json como se indica a continuación y desde el símbolo del sistema podemos dar como:

npm install

Se encargará de instalar todos los paquetes que se mencionaron dentro de las dependencias. Los paquetes que se usaron se pueden verificar desde package.json:

paquete.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"
      }
}

Estructura de la carpeta del proyecto: El proyecto debería verse así:

 

Ejemplo: Comencemos el proyecto:

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: para el embellecimiento del proyecto

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: como este proyecto no involucra ninguna base de datos, deje que los elementos se seleccionen de «ItemJSON.js». Está bajo src >> aplicación >> Elementos >> 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;

Inicie la aplicación: escriba el siguiente comando para iniciar la aplicación. El proyecto se inicia en el puerto 3000

npm start

Producción:

 

A continuación, el código requerido se puede encontrar en 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: tenemos la opción de agregar los elementos y eliminarlos

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>
        );
    }
}

Producción:

 

Al ingresar los detalles del producto y hacer clic en el botón «agregar», se produce la siguiente funcionalidad:

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>
        );
    }
}

eliminación de un producto: Intentemos eliminar Redmi de la lista anterior. El código requerido para hacer la eliminación está en 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>
        );
    }
}

Producción:

 

Podemos probar la funcionalidad del proyecto de la siguiente manera:

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);
    });
});

El script de prueba se puede probar de la siguiente manera:

npm test

Producción:

 

Publicación traducida automáticamente

Artículo escrito por priyarajtt y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *