Fixed same product - diff opts in cart. Added confirm on cart empty.
parent
799ed301f2
commit
5d4c469e38
|
@ -7244,6 +7244,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"object-hash": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-HgcGMooY4JC2PBt9sdUdJ6PMzpin+YtY3r/7wg0uTifP+HJWW8rammseSEHuyt0UeShI183UGssCJqm1bJR7QA=="
|
||||||
|
},
|
||||||
"object-is": {
|
"object-is": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz",
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
"node-cron": "^2.0.3",
|
"node-cron": "^2.0.3",
|
||||||
"nodemailer": "^4.7.0",
|
"nodemailer": "^4.7.0",
|
||||||
"numeral": "^2.0.6",
|
"numeral": "^2.0.6",
|
||||||
|
"object-hash": "^2.0.1",
|
||||||
"paypal-rest-sdk": "^1.6.9",
|
"paypal-rest-sdk": "^1.6.9",
|
||||||
"rand-token": "^0.4.0",
|
"rand-token": "^0.4.0",
|
||||||
"rimraf": "^2.7.1",
|
"rimraf": "^2.7.1",
|
||||||
|
|
|
@ -377,15 +377,17 @@ $(document).ready(function (){
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// On empty cart click
|
||||||
$(document).on('click', '#empty-cart', function(e){
|
$(document).on('click', '#empty-cart', function(e){
|
||||||
$.ajax({
|
$('#confirmModal').modal('show');
|
||||||
method: 'POST',
|
$('#buttonConfirm').attr('data-func', 'emptyCart');
|
||||||
url: '/product/emptycart'
|
|
||||||
})
|
|
||||||
.done(function(msg){
|
|
||||||
showNotification(msg.message, 'success', true);
|
|
||||||
updateCartDiv();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#buttonConfirm', function(e){
|
||||||
|
// Get the function and run it
|
||||||
|
var func = $(e.target).attr('data-func');
|
||||||
|
window[func]();
|
||||||
|
$('#confirmModal').modal('hide');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.qty-btn-minus').on('click', function(){
|
$('.qty-btn-minus').on('click', function(){
|
||||||
|
@ -449,7 +451,9 @@ function deleteFromCart(element){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/product/removefromcart',
|
url: '/product/removefromcart',
|
||||||
data: { productId: element.attr('data-id') }
|
data: {
|
||||||
|
cartId: element.attr('data-cartid')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.done(function(msg){
|
.done(function(msg){
|
||||||
showNotification(msg.message, 'success');
|
showNotification(msg.message, 'success');
|
||||||
|
@ -476,6 +480,7 @@ function updateCart(element){
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '/product/updatecart',
|
url: '/product/updatecart',
|
||||||
data: {
|
data: {
|
||||||
|
cartId: element.attr('data-cartid'),
|
||||||
productId: element.attr('data-id'),
|
productId: element.attr('data-id'),
|
||||||
quantity: element.val()
|
quantity: element.val()
|
||||||
}
|
}
|
||||||
|
@ -596,14 +601,14 @@ function updateCartDiv(){
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<button class="btn btn-primary btn-qty-minus" type="button">-</button>
|
<button class="btn btn-primary btn-qty-minus" type="button">-</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" class="form-control cart-product-quantity text-center" id="${productId}-qty" data-id="${productId}" maxlength="2" value="${item.quantity}">
|
<input type="number" class="form-control cart-product-quantity text-center" data-cartid="${productId}" data-id="${item.id}" maxlength="2" value="${item.quantity}">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-primary btn-qty-add" type="button">+</button>
|
<button class="btn btn-primary btn-qty-add" type="button">+</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 col-md-2 no-pad-left">
|
<div class="col-4 col-md-2 no-pad-left">
|
||||||
<button class="btn btn-danger btn-delete-from-cart" data-id="${productId}" type="button"><i class="far fa-trash-alt" data-id="${productId}" aria-hidden="true"></i></button>
|
<button class="btn btn-danger btn-delete-from-cart" data-cartid="${productId}" type="button"><i class="far fa-trash-alt" data-cartid="${productId}" aria-hidden="true"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-md-4 align-self-center text-right">
|
<div class="col-8 col-md-4 align-self-center text-right">
|
||||||
<strong class="my-auto">${result.currencySymbol}${productTotalAmount}</strong>
|
<strong class="my-auto">${result.currencySymbol}${productTotalAmount}</strong>
|
||||||
|
@ -663,3 +668,15 @@ function upperFirst(value){
|
||||||
return chr.toUpperCase();
|
return chr.toUpperCase();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
function emptyCart(){
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/product/emptycart'
|
||||||
|
})
|
||||||
|
.done(function(msg){
|
||||||
|
showNotification(msg.message, 'success', true);
|
||||||
|
updateCartDiv();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,7 +8,7 @@
|
||||||
width: 500px;
|
width: 500px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 9999;
|
z-index: 999;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 9998;
|
z-index: 998;
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
-webkit-animation: fade 500ms;
|
-webkit-animation: fade 500ms;
|
||||||
animation: fade 500ms;
|
animation: fade 500ms;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*! Pushy - v1.0.0 - 2016-3-1
|
/*! Pushy - v1.0.0 - 2016-3-1
|
||||||
* Pushy is a responsive off-canvas navigation menu using CSS transforms & transitions.
|
* Pushy is a responsive off-canvas navigation menu using CSS transforms & transitions.
|
||||||
* https://github.com/christophery/pushy/
|
* https://github.com/christophery/pushy/
|
||||||
* by Christopher Yee */.pushy{position:fixed;width:500px;height:100%;top:0;z-index:9999;background:#fff;overflow:auto;visibility:hidden;-webkit-overflow-scrolling:touch}.pushy ul:first-child{margin-top:10px}.pushy.pushy-left{left:0}.pushy.pushy-right{right:0}.pushy-left{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left #container,.pushy-open-left .push{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-right{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-open-right #container,.pushy-open-right .push{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left .pushy,.pushy-open-right .pushy{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}#container,.push,.pushy{transition:transform .2s cubic-bezier(.16,.68,.43,.99)}.site-overlay{display:none}.pushy-open-left .site-overlay,.pushy-open-right .site-overlay{display:block;position:fixed;top:0;right:0;bottom:0;left:0;z-index:9998;background-color:rgba(0,0,0,.5);-webkit-animation:fade .5s;animation:fade .5s}@keyframes fade{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fade{0%{opacity:0}100%{opacity:1}}.pushy-submenu ul{padding-left:15px;transition:max-height .2s ease-in-out}.pushy-submenu ul .pushy-link{transition:opacity .2s ease-in-out}.pushy-submenu>a{position:relative}.pushy-submenu>a::after{content:'';display:block;height:11px;width:8px;position:absolute;top:50%;right:15px;background:url(../img/arrow.svg) no-repeat;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);transition:transform .2s}.pushy-submenu-closed ul{max-height:0;overflow:hidden}.pushy-submenu-closed .pushy-link{opacity:0}.pushy-submenu-open ul{max-height:1000px}.pushy-submenu-open .pushy-link{opacity:1}.pushy-submenu-open a::after{-webkit-transform:translateY(-50%) rotate(90deg);-ms-transform:translateY(-50%) rotate(90deg);transform:translateY(-50%) rotate(90deg)}.no-csstransforms3d .pushy-submenu-closed ul{max-height:none;display:none}
|
* by Christopher Yee */.pushy{position:fixed;width:500px;height:100%;top:0;z-index:999;background:#fff;overflow:auto;visibility:hidden;-webkit-overflow-scrolling:touch}.pushy ul:first-child{margin-top:10px}.pushy.pushy-left{left:0}.pushy.pushy-right{right:0}.pushy-left{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left #container,.pushy-open-left .push{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-right{-webkit-transform:translate3d(500px,0,0);-ms-transform:translate3d(500px,0,0);transform:translate3d(500px,0,0)}.pushy-open-right #container,.pushy-open-right .push{-webkit-transform:translate3d(-500px,0,0);-ms-transform:translate3d(-500px,0,0);transform:translate3d(-500px,0,0)}.pushy-open-left .pushy,.pushy-open-right .pushy{-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}#container,.push,.pushy{transition:transform .2s cubic-bezier(.16,.68,.43,.99)}.site-overlay{display:none}.pushy-open-left .site-overlay,.pushy-open-right .site-overlay{display:block;position:fixed;top:0;right:0;bottom:0;left:0;z-index:998;background-color:rgba(0,0,0,.5);-webkit-animation:fade .5s;animation:fade .5s}@keyframes fade{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes fade{0%{opacity:0}100%{opacity:1}}.pushy-submenu ul{padding-left:15px;transition:max-height .2s ease-in-out}.pushy-submenu ul .pushy-link{transition:opacity .2s ease-in-out}.pushy-submenu>a{position:relative}.pushy-submenu>a::after{content:'';display:block;height:11px;width:8px;position:absolute;top:50%;right:15px;background:url(../img/arrow.svg) no-repeat;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);transition:transform .2s}.pushy-submenu-closed ul{max-height:0;overflow:hidden}.pushy-submenu-closed .pushy-link{opacity:0}.pushy-submenu-open ul{max-height:1000px}.pushy-submenu-open .pushy-link{opacity:1}.pushy-submenu-open a::after{-webkit-transform:translateY(-50%) rotate(90deg);-ms-transform:translateY(-50%) rotate(90deg);transform:translateY(-50%) rotate(90deg)}.no-csstransforms3d .pushy-submenu-closed ul{max-height:none;display:none}
|
|
@ -1,6 +1,7 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const colors = require('colors');
|
const colors = require('colors');
|
||||||
|
const hash = require('object-hash');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const {
|
const {
|
||||||
|
@ -362,12 +363,12 @@ router.post('/product/updatecart', async (req, res, next) => {
|
||||||
|
|
||||||
if(productQuantity === 0){
|
if(productQuantity === 0){
|
||||||
// quantity equals zero so we remove the item
|
// quantity equals zero so we remove the item
|
||||||
delete req.session.cart[cartItem.productId];
|
delete req.session.cart[cartItem.cartId];
|
||||||
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const product = await db.products.findOne({ _id: getId(cartItem.productId) });
|
const product = await db.products.findOne({ _id: getId(req.session.cart[cartItem.cartId].productId) });
|
||||||
if(!product){
|
if(!product){
|
||||||
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
return;
|
return;
|
||||||
|
@ -382,14 +383,14 @@ router.post('/product/updatecart', async (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const productPrice = parseFloat(product.productPrice).toFixed(2);
|
const productPrice = parseFloat(product.productPrice).toFixed(2);
|
||||||
if(!req.session.cart[cartItem.productId]){
|
if(!req.session.cart[cartItem.cartId]){
|
||||||
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
res.status(400).json({ message: 'There was an error updating the cart', totalCartItems: Object.keys(req.session.cart).length });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the cart
|
// Update the cart
|
||||||
req.session.cart[cartItem.productId].quantity = productQuantity;
|
req.session.cart[cartItem.cartId].quantity = productQuantity;
|
||||||
req.session.cart[cartItem.productId].totalItemPrice = productPrice * productQuantity;
|
req.session.cart[cartItem.cartId].totalItemPrice = productPrice * productQuantity;
|
||||||
|
|
||||||
// update total cart amount
|
// update total cart amount
|
||||||
await updateTotalCart(req, res);
|
await updateTotalCart(req, res);
|
||||||
|
@ -410,12 +411,12 @@ router.post('/product/removefromcart', async (req, res, next) => {
|
||||||
const db = req.app.db;
|
const db = req.app.db;
|
||||||
|
|
||||||
// Check for item in cart
|
// Check for item in cart
|
||||||
if(!req.session.cart[req.body.productId]){
|
if(!req.session.cart[req.body.cartId]){
|
||||||
return res.status(400).json({ message: 'Product not found in cart' });
|
return res.status(400).json({ message: 'Product not found in cart' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove item from cart
|
// remove item from cart
|
||||||
delete req.session.cart[req.body.productId];
|
delete req.session.cart[req.body.cartId];
|
||||||
|
|
||||||
// If not items in cart, empty it
|
// If not items in cart, empty it
|
||||||
if(Object.keys(req.session.cart).length === 0){
|
if(Object.keys(req.session.cart).length === 0){
|
||||||
|
@ -535,19 +536,25 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
}catch(ex){}
|
}catch(ex){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Product with options hash
|
||||||
|
const productHash = hash({
|
||||||
|
productId: product._id.toString(),
|
||||||
|
options
|
||||||
|
});
|
||||||
|
|
||||||
// if exists we add to the existing value
|
// if exists we add to the existing value
|
||||||
let cartQuantity = 0;
|
let cartQuantity = 0;
|
||||||
if(req.session.cart[product._id]){
|
if(req.session.cart[productHash]){
|
||||||
cartQuantity = parseInt(req.session.cart[product._id].quantity) + productQuantity;
|
cartQuantity = parseInt(req.session.cart[productHash].quantity) + productQuantity;
|
||||||
req.session.cart[product._id].quantity = cartQuantity;
|
req.session.cart[productHash].quantity = cartQuantity;
|
||||||
req.session.cart[product._id].totalItemPrice = productPrice * parseInt(req.session.cart[product._id].quantity);
|
req.session.cart[productHash].totalItemPrice = productPrice * parseInt(req.session.cart[productHash].quantity);
|
||||||
}else{
|
}else{
|
||||||
// Set the card quantity
|
// Set the card quantity
|
||||||
cartQuantity = productQuantity;
|
cartQuantity = productQuantity;
|
||||||
|
|
||||||
// new product deets
|
// new product deets
|
||||||
const productObj = {};
|
const productObj = {};
|
||||||
productObj.productId = req.body.productId;
|
productObj.productId = product._id;
|
||||||
productObj.title = product.productTitle;
|
productObj.title = product.productTitle;
|
||||||
productObj.quantity = productQuantity;
|
productObj.quantity = productQuantity;
|
||||||
productObj.totalItemPrice = productPrice * productQuantity;
|
productObj.totalItemPrice = productPrice * productQuantity;
|
||||||
|
@ -562,7 +569,7 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge into the current cart
|
// merge into the current cart
|
||||||
req.session.cart[product._id] = productObj;
|
req.session.cart[productHash] = productObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update cart to the DB
|
// Update cart to the DB
|
||||||
|
@ -580,7 +587,11 @@ router.post('/product/addtocart', async (req, res, next) => {
|
||||||
req.session.cartSubscription = product.productSubscription;
|
req.session.cartSubscription = product.productSubscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({ message: 'Cart successfully updated', totalCartItems: req.session.totalCartItems });
|
return res.status(200).json({
|
||||||
|
message: 'Cart successfully updated',
|
||||||
|
cartId: productHash,
|
||||||
|
totalCartItems: req.session.totalCartItems
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// search products
|
// search products
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
import{ serial as test }from'ava';
|
||||||
|
const {
|
||||||
|
runBefore,
|
||||||
|
g
|
||||||
|
} = require('../helper');
|
||||||
|
|
||||||
|
test.before(async () => {
|
||||||
|
await runBefore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Success] Add subscripton product to cart', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[7]._id,
|
||||||
|
productQuantity: 1,
|
||||||
|
productOptions: {}
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
const sessions = await g.db.cart.find({}).toArray();
|
||||||
|
if(!sessions || sessions.length === 0){
|
||||||
|
t.fail();
|
||||||
|
}
|
||||||
|
t.deepEqual(res.body.message, 'Cart successfully updated');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Fail] Add product to cart when subscription already added', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[1]._id,
|
||||||
|
productQuantity: 1,
|
||||||
|
productOptions: JSON.stringify(g.products[1].productOptions)
|
||||||
|
})
|
||||||
|
.expect(400);
|
||||||
|
t.deepEqual(res.body.message, 'Subscription already existing in cart. You cannot add more.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Fail] Add quantity which exceeds the maxQuantity', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[4]._id,
|
||||||
|
productQuantity: 75,
|
||||||
|
productOptions: {}
|
||||||
|
})
|
||||||
|
.expect(400);
|
||||||
|
t.deepEqual(res.body.message, 'The quantity exceeds the max amount. Please contact us for larger orders.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Success] Empty cart', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/emptycart')
|
||||||
|
.expect(200);
|
||||||
|
t.deepEqual(res.body.message, 'Cart successfully emptied');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Success] Add product to cart', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[0]._id,
|
||||||
|
productQuantity: 1,
|
||||||
|
productOptions: JSON.stringify(g.products[0].productOptions)
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
const sessions = await g.db.cart.find({}).toArray();
|
||||||
|
if(!sessions || sessions.length === 0){
|
||||||
|
t.fail();
|
||||||
|
}
|
||||||
|
t.deepEqual(res.body.message, 'Cart successfully updated');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Success] Update cart', async t => {
|
||||||
|
const cart = await g.request
|
||||||
|
.get('/cart/retrieve')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const cartId = Object.keys(cart.body.cart)[0];
|
||||||
|
const productId = cart.body.cart[cartId].id;
|
||||||
|
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/updatecart')
|
||||||
|
.send({
|
||||||
|
productId: productId,
|
||||||
|
cartId: cartId,
|
||||||
|
quantity: 10
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
t.deepEqual(res.body.message, 'Cart successfully updated');
|
||||||
|
|
||||||
|
const checkCart = await g.request
|
||||||
|
.get('/cart/retrieve')
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
// Check new quantity and total price has been updated
|
||||||
|
t.deepEqual(checkCart.body.cart[cartId].quantity, 10);
|
||||||
|
t.deepEqual(checkCart.body.cart[cartId].totalItemPrice, cart.body.cart[cartId].totalItemPrice * 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Fail] Cannot add subscripton when other product in cart', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[7]._id,
|
||||||
|
productQuantity: 1,
|
||||||
|
productOptions: {}
|
||||||
|
})
|
||||||
|
.expect(400);
|
||||||
|
t.deepEqual(res.body.message, 'You cannot combine subscription products with existing in your cart. Empty your cart and try again.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Fail] Add product to cart with not enough stock', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[0]._id,
|
||||||
|
productQuantity: 20,
|
||||||
|
productOptions: JSON.stringify(g.products[0].productOptions)
|
||||||
|
})
|
||||||
|
.expect(400);
|
||||||
|
t.deepEqual(res.body.message, 'There is insufficient stock of this product.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Fail] Add incorrect product to cart', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: 'fake_product_id',
|
||||||
|
productQuantity: 20,
|
||||||
|
productOptions: JSON.stringify(g.products[0].productOptions)
|
||||||
|
})
|
||||||
|
.expect(400);
|
||||||
|
t.deepEqual(res.body.message, 'Error updating cart. Please try again.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Success] Remove item previously added to cart', async t => {
|
||||||
|
// Add a second product to cart
|
||||||
|
const cart = await g.request
|
||||||
|
.post('/product/addtocart')
|
||||||
|
.send({
|
||||||
|
productId: g.products[1]._id,
|
||||||
|
productQuantity: 1,
|
||||||
|
productOptions: JSON.stringify(g.products[1].productOptions)
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/removefromcart')
|
||||||
|
.send({
|
||||||
|
cartId: cart.body.cartId
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
t.deepEqual(res.body.message, 'Product successfully removed');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[Fail] Try remove an item which is not in the cart', async t => {
|
||||||
|
const res = await g.request
|
||||||
|
.post('/product/removefromcart')
|
||||||
|
.send({
|
||||||
|
cartId: 'bogus_product_id'
|
||||||
|
})
|
||||||
|
.expect(400);
|
||||||
|
t.deepEqual(res.body.message, 'Product not found in cart');
|
||||||
|
});
|
|
@ -19,160 +19,6 @@ test('[Success] Get products JSON', async t => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[Success] Add subscripton product to cart', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[7]._id,
|
|
||||||
productQuantity: 1,
|
|
||||||
productOptions: {}
|
|
||||||
})
|
|
||||||
.expect(200);
|
|
||||||
const sessions = await g.db.cart.find({}).toArray();
|
|
||||||
if(!sessions || sessions.length === 0){
|
|
||||||
t.fail();
|
|
||||||
}
|
|
||||||
t.deepEqual(res.body.message, 'Cart successfully updated');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Fail] Add product to cart when subscription already added', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[1]._id,
|
|
||||||
productQuantity: 1,
|
|
||||||
productOptions: JSON.stringify(g.products[1].productOptions)
|
|
||||||
})
|
|
||||||
.expect(400);
|
|
||||||
t.deepEqual(res.body.message, 'Subscription already existing in cart. You cannot add more.');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Fail] Add quantity which exceeds the maxQuantity', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[4]._id,
|
|
||||||
productQuantity: 75,
|
|
||||||
productOptions: {}
|
|
||||||
})
|
|
||||||
.expect(400);
|
|
||||||
t.deepEqual(res.body.message, 'The quantity exceeds the max amount. Please contact us for larger orders.');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Success] Empty cart', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/emptycart')
|
|
||||||
.expect(200);
|
|
||||||
t.deepEqual(res.body.message, 'Cart successfully emptied');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Success] Add product to cart', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[0]._id,
|
|
||||||
productQuantity: 1,
|
|
||||||
productOptions: JSON.stringify(g.products[0].productOptions)
|
|
||||||
})
|
|
||||||
.expect(200);
|
|
||||||
const sessions = await g.db.cart.find({}).toArray();
|
|
||||||
if(!sessions || sessions.length === 0){
|
|
||||||
t.fail();
|
|
||||||
}
|
|
||||||
t.deepEqual(res.body.message, 'Cart successfully updated');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Success] Update cart', async t => {
|
|
||||||
const cart = await g.request
|
|
||||||
.get('/cart/retrieve')
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const productId = g.products[0]._id;
|
|
||||||
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/updatecart')
|
|
||||||
.send({
|
|
||||||
productId: productId,
|
|
||||||
quantity: 10
|
|
||||||
})
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
t.deepEqual(res.body.message, 'Cart successfully updated');
|
|
||||||
|
|
||||||
const checkCart = await g.request
|
|
||||||
.get('/cart/retrieve')
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
// Check new quantity and total price has been updated
|
|
||||||
t.deepEqual(checkCart.body.cart[productId].quantity, 10);
|
|
||||||
t.deepEqual(checkCart.body.cart[productId].totalItemPrice, cart.body.cart[productId].totalItemPrice * 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Fail] Cannot add subscripton when other product in cart', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[7]._id,
|
|
||||||
productQuantity: 1,
|
|
||||||
productOptions: {}
|
|
||||||
})
|
|
||||||
.expect(400);
|
|
||||||
t.deepEqual(res.body.message, 'You cannot combine subscription products with existing in your cart. Empty your cart and try again.');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Fail] Add product to cart with not enough stock', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[0]._id,
|
|
||||||
productQuantity: 20,
|
|
||||||
productOptions: JSON.stringify(g.products[0].productOptions)
|
|
||||||
})
|
|
||||||
.expect(400);
|
|
||||||
t.deepEqual(res.body.message, 'There is insufficient stock of this product.');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Fail] Add incorrect product to cart', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
id: 'fake_product_id',
|
|
||||||
state: false
|
|
||||||
})
|
|
||||||
.expect(400);
|
|
||||||
t.deepEqual(res.body.message, 'Error updating cart. Please try again.');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Success] Remove item previously added to cart', async t => {
|
|
||||||
// Add a second product to cart
|
|
||||||
await g.request
|
|
||||||
.post('/product/addtocart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[1]._id,
|
|
||||||
productQuantity: 1,
|
|
||||||
productOptions: JSON.stringify(g.products[1].productOptions)
|
|
||||||
})
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/removefromcart')
|
|
||||||
.send({
|
|
||||||
productId: g.products[0]._id
|
|
||||||
})
|
|
||||||
.expect(200);
|
|
||||||
t.deepEqual(res.body.message, 'Product successfully removed');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Fail] Try remove an item which is not in the cart', async t => {
|
|
||||||
const res = await g.request
|
|
||||||
.post('/product/removefromcart')
|
|
||||||
.send({
|
|
||||||
cartId: 'bogus_product_id'
|
|
||||||
})
|
|
||||||
.expect(400);
|
|
||||||
t.deepEqual(res.body.message, 'Product not found in cart');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[Success] Search products', async t => {
|
test('[Success] Search products', async t => {
|
||||||
const res = await g.request
|
const res = await g.request
|
||||||
.get('/category/backpack?json=true')
|
.get('/category/backpack?json=true')
|
||||||
|
|
|
@ -208,5 +208,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{> partials/confirmModal}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="modal fade" id="confirmModal" tabindex="-1" role="dialog" aria-labelledby="confirmModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title text-danger" id="confirmModalLabel">Confirm</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary mr-auto" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" id="buttonConfirm" class="btn btn-danger">Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -31,14 +31,14 @@
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<button class="btn btn-primary btn-qty-minus" type="button">-</button>
|
<button class="btn btn-primary btn-qty-minus" type="button">-</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="number" class="form-control cart-product-quantity text-center" id="{{../this.productId}}-qty" data-id="{{../this.productId}}" maxlength="2" value="{{../this.quantity}}">
|
<input type="number" class="form-control cart-product-quantity text-center" data-cartid="{{../this.productId}}" data-id="{{../this.id}}" maxlength="2" value="{{../this.quantity}}">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-primary btn-qty-add" type="button">+</button>
|
<button class="btn btn-primary btn-qty-add" type="button">+</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 col-md-2 no-pad-left">
|
<div class="col-4 col-md-2 no-pad-left">
|
||||||
<button class="btn btn-danger btn-delete-from-cart" data-id="{{../this.productId}}" type="button"><i class="far fa-trash-alt" data-id="{{../this.productId}}" aria-hidden="true"></i></button>
|
<button class="btn btn-danger btn-delete-from-cart" data-id="{{../this.productId}}" data-cartid="{{../this.productId}}" type="button"><i class="far fa-trash-alt" aria-hidden="true"></i></button>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="col-12 col-md-8 no-pad-left mb-2"></div>
|
<div class="col-12 col-md-8 no-pad-left mb-2"></div>
|
||||||
|
|
Loading…
Reference in New Issue