Started adding some tests

master
Mark Moffat 2018-02-22 18:41:24 +01:00
parent 245c67188b
commit 61c59c8997
18 changed files with 4246 additions and 145 deletions

15
app.js
View File

@ -315,7 +315,13 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
// select DB
const dbUriObj = mongodbUri.parse(config.databaseConnectionString);
const db = client.db(dbUriObj.database);
let db;
// if in testing, set the testing DB
if(process.env.NODE_ENV === 'test'){
db = client.db('testingdb');
}else{
db = client.db(dbUriObj.database);
}
// setup the collections
db.users = db.collection('users');
@ -326,17 +332,20 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
db.customers = db.collection('customers');
// add db to app for routes
app.dbClient = client;
app.db = db;
app.config = config;
app.port = app.get('port');
// run indexing
common.runIndexing(app)
.then(common.testData(db, app))
.then(app.listen(app.get('port')))
.then(() => {
// lift the app
app.emit('appStarted');
console.log(colors.green('expressCart running on host: http://localhost:' + app.get('port')));
})
.catch(() => {
.catch((err) => {
console.error(colors.red('Error setting up indexes:' + err));
process.exit(2);
});

View File

@ -14,7 +14,7 @@
"productImage": "/uploads/duckworth-jacket/woolfill-jacket_6c39ae23-c0c8-4821-85f4-4b5d64333c62_grande.jpg"
},
{
"productPermalink": "5-panel-cap",
"productPermalink": "5-panel-camp-cap",
"productTitle": "5 Panel Camp Cap",
"productPrice": "48",
"productDescription": "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility;\">A classic 5 panel hat with our United By Blue logo on the front and an adjustable strap to keep fit and secure. Made with recycled polyester and organic cotton mix.<\/p><ul style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility;\"><li style=\"margin-bottom: 0px;\">Made in&nbsp;New Jersey<\/li><li style=\"margin-bottom: 0px;\">7oz Eco-Twill fabric:&nbsp;35% organic cotton, 65% recycled PET&nbsp;(plastic water and soda bottles)&nbsp;<\/li><li style=\"margin-bottom: 0px;\">Embossed leather patch<\/li><\/ul><ul class=\"tabs\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility; color: rgb(28, 29, 29); font-family: Arapey, serif; line-height: 25.008px;\"><\/ul>",
@ -79,7 +79,7 @@
"productImage" : "/uploads/hudderton-backpack/hudderton-backpack_dc8afb13-448b-49d9-a042-5a163a97de8f_590x.jpg"
},
{
"productPermalink" : "Ayres",
"productPermalink" : "ayres-chambray",
"productTitle" : "Ayres Chambray",
"productPrice" : "77",
"productDescription" : "<p style=\"margin-bottom: 25px; text-rendering: optimizeLegibility; color: rgb(34, 35, 35); font-family: Arapey, serif;\">Comfortable and practical, our chambray button down is perfect for travel or days spent on the go. The Ayres Chambray has a rich, washed out indigo color suitable to throw on for any event. Made with sustainable soft chambray featuring two chest pockets with sturdy and scratch resistant corozo buttons.</p><ul class=\"tabs-content\" style=\"margin-right: 0px; margin-bottom: 25px; margin-left: 20px; padding: 0px; text-rendering: optimizeLegibility; color: rgb(34, 35, 35); font-family: Arapey, serif;\"><li style=\"margin-bottom: 0px;\"><span style=\"line-height: 1.4;\">100% Organic Cotton Chambray, 4.9 oz Fabric.</span></li><li style=\"margin-bottom: 0px;\"><span style=\"line-height: 1.4;\">Natural Corozo Buttons.</span></li></ul>",
@ -92,6 +92,28 @@
"productImage" : "/uploads/Ayres Chambray/chambray_5f232530-4331-492a-872c-81c225d6bafd_590x.jpg"
}
],
"customers": [
{
"email" : "test@test.com",
"firstName" : "Testy",
"lastName" : "Cles",
"address1" : "1 test street",
"address2" : "testvile",
"country" : "Netherlands",
"state" : "",
"postcode" : "2000TW",
"phone" : "123456789",
"password" : "$2a$10$kKjnX1J/CAdgdmLI0WuPY.ILH1c7N8mD0H/ZyUXEfee1mJxJvZIS."
}
],
"users": [
{
"usersName" : "test",
"userEmail" : "test@test.com",
"userPassword" : "$2a$10$7jQx/hQOWrRni531b/dHRuH8o1ZP8Yo8g..GpTOF4M7RrEH/pzTMy",
"isAdmin" : "false"
}
],
"menu": {
"items": [
{

View File

@ -600,25 +600,69 @@ exports.runIndexing = (app) => {
});
};
exports.testData = (db, app) => {
db.products.count({})
exports.dropTestData = (db) => {
Promise.all([
db.products.drop(),
db.users.drop(),
db.customers.drop()
])
.then((err) => {
return Promise.resolve();
})
.catch((err) => {
console.log('Error dropping test data', err);
});
};
exports.sampleData = (app) => {
const db = app.db;
db.products.count()
.then((products) => {
if(products > 0){
if(products !== 0){
return Promise.resolve();
}
console.info(colors.cyan('No products, inserting test data'));
console.log('Inserting sample data');
const testData = fs.readFileSync('./bin/testdata.json', 'utf-8');
const jsonData = JSON.parse(testData);
// Add sample data
return Promise.all([
db.products.insertMany(jsonData.products),
db.menu.insertOne(jsonData.menu),
exports.runIndexing(app)
db.menu.insertOne(jsonData.menu)
]);
});
};
exports.testData = async (app) => {
const db = app.db;
const testData = fs.readFileSync('./bin/testdata.json', 'utf-8');
const jsonData = JSON.parse(testData);
// TODO: A bit ugly, needs fixing
return new Promise((resolve, reject) => {
Promise.all([
db.users.remove({}, {}),
db.customers.remove({}, {}),
db.products.remove({}, {})
])
.then(() => {
Promise.all([
db.users.insertMany(jsonData.users),
db.customers.insertMany(jsonData.customers),
db.products.insertMany(jsonData.products),
db.menu.insertOne(jsonData.menu)
])
.then(() => {
resolve();
})
.catch((err) => {
reject(err);
});
})
.catch((err) => {
console.info(colors.red('Error inserting test data. Check `/bin/testdata.json` is correctly formatted.', err));
process.exit(2);
reject(err);
});
});
};

3851
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,8 @@
"private": false,
"scripts": {
"start": "node app.js",
"deploy": "gulp deploy"
"deploy": "gulp deploy",
"test": "ava test/**/*.js --verbose"
},
"dependencies": {
"ajv": "^6.0.0",
@ -16,7 +17,7 @@
"body-parser": "^1.17.2",
"cheerio": "^0.22.0",
"colors": "^1.1.2",
"connect-mongodb-session": "^1.3.0",
"connect-mongodb-session": "^1.4.0",
"cookie-parser": "^1.4.3",
"express": "^4.15.3",
"express-handlebars": "^3.0.0",
@ -27,8 +28,7 @@
"lodash": "^4.13.1",
"lunr": "^2.1.5",
"moment": "^2.15.2",
"mongodb": "^3.0.1",
"mongodb-uri": "^0.9.7",
"mongodb": "2.2.3",
"morgan": "^1.9.0",
"multer": "^1.1.0",
"nodemailer": "^4.4.1",
@ -42,6 +42,7 @@
"uglifycss": "0.0.27"
},
"devDependencies": {
"ava": "^0.25.0",
"eslint": "^3.19.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-config-standard": "^10.2.1",
@ -53,7 +54,9 @@
"gulp-clean-css": "^3.9.2",
"gulp-minify": "^2.1.0",
"gulp-rename": "^1.2.2",
"run-sequence": "^2.2.1"
"mongodb-uri": "^0.9.7",
"run-sequence": "^2.2.1",
"wait.for": "^0.6.6"
},
"main": "app.js",
"keywords": [

View File

@ -344,6 +344,27 @@ $(document).ready(function (){
}
});
$('#loginForm').on('click', function(e){
if(!e.isDefaultPrevented()){
e.preventDefault();
$.ajax({
method: 'POST',
url: '/admin/login_action',
data: {
email: $('#email').val(),
password: $('#password').val()
}
})
.done(function(msg){
window.location = '/admin';
})
.fail(function(msg){
showNotification(msg.responseJSON.message, 'danger');
});
}
e.preventDefault();
});
// call update settings API
$('#customerLogin').on('click', function(e){
if(!e.isDefaultPrevented()){

File diff suppressed because one or more lines are too long

View File

@ -39,7 +39,7 @@ router.get('/admin/login', (req, res) => {
res.render('login', {
title: 'Login',
referringUrl: req.header('Referer'),
config: common.getConfig(),
config: req.app.config,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
@ -59,17 +59,13 @@ router.post('/admin/login_action', (req, res) => {
db.users.findOne({userEmail: req.body.email}, (err, user) => {
if(err){
req.session.message = 'Cannot find user.';
req.session.messageType = 'danger';
res.redirect('/admin/login');
res.status(400).json({message: 'A user with that email does not exist.'});
return;
}
// check if user exists with that email
if(user === undefined || user === null){
req.session.message = 'A user with that email does not exist.';
req.session.messageType = 'danger';
res.redirect('/admin/login');
res.status(400).json({message: 'A user with that email does not exist.'});
}else{
// we have a user under that email so we compare the password
bcrypt.compare(req.body.password, user.userPassword)
@ -79,12 +75,10 @@ router.post('/admin/login_action', (req, res) => {
req.session.usersName = user.usersName;
req.session.userId = user._id.toString();
req.session.isAdmin = user.isAdmin;
res.redirect('/admin');
res.status(200).json({message: 'Login successful'});
}else{
// password is not correct
req.session.message = 'Access denied. Check password and try again.';
req.session.messageType = 'danger';
res.redirect('/admin/login');
res.status(400).json({message: 'Access denied. Check password and try again.'});
}
});
}
@ -106,7 +100,7 @@ router.get('/admin/setup', (req, res) => {
req.session.needsSetup = true;
res.render('setup', {
title: 'Setup',
config: common.getConfig(),
config: req.app.config,
helpers: req.handlebars.helpers,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
@ -165,9 +159,9 @@ router.get('/admin/settings', common.restrict, (req, res) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
footerHtml: typeof common.getConfig().footerHtml !== 'undefined' ? escape.decode(common.getConfig().footerHtml) : null,
googleAnalytics: typeof common.getConfig().googleAnalytics !== 'undefined' ? escape.decode(common.getConfig().googleAnalytics) : null
config: req.app.config,
footerHtml: typeof req.app.config.footerHtml !== 'undefined' ? escape.decode(req.app.config.footerHtml) : null,
googleAnalytics: typeof req.app.config.googleAnalytics !== 'undefined' ? escape.decode(req.app.config.googleAnalytics) : null
});
});
@ -218,7 +212,7 @@ router.get('/admin/settings/menu', common.restrict, async (req, res) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
config: req.app.config,
menu: common.sortMenu(await common.getMenu(db))
});
});
@ -239,7 +233,7 @@ router.get('/admin/settings/pages', common.restrict, (req, res) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
config: req.app.config,
menu: common.sortMenu(await common.getMenu(db))
});
});
@ -257,7 +251,7 @@ router.get('/admin/settings/pages/new', common.restrict, common.checkAccess, asy
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
config: req.app.config,
menu: common.sortMenu(await common.getMenu(db))
});
});
@ -281,14 +275,14 @@ router.get('/admin/settings/pages/edit/:page', common.restrict, common.checkAcce
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig(),
config: req.app.config,
menu
});
}else{
// 404 it!
res.status(404).render('error', {
title: '404 Error - Page not found',
config: common.getConfig(),
config: req.app.config,
message: '404 Error - Page not found',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
@ -478,7 +472,7 @@ router.post('/admin/file/upload', common.restrict, common.checkAccess, upload.si
// delete a file via ajax request
router.post('/admin/testEmail', common.restrict, (req, res) => {
let config = common.getConfig();
let config = req.app.config;
// TODO: Should fix this to properly handle result
common.sendEmail(config.emailAddress, 'expressCart test email', 'Your email settings are working');
res.status(200).json({message: 'Test email sent'});

View File

@ -74,7 +74,7 @@ router.get('/admin/customer/view/:id?', common.restrict, (req, res) => {
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
config: common.getConfig(),
config: req.app.config,
editor: true,
helpers: req.handlebars.helpers
});
@ -94,7 +94,7 @@ router.get('/admin/customers', common.restrict, (req, res) => {
helpers: req.handlebars.helpers,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
config: common.getConfig()
config: req.app.config
});
});
});
@ -119,7 +119,7 @@ router.get('/admin/customers/filter/:search', common.restrict, (req, res, next)
title: 'Customer results',
customers: customers,
admin: true,
config: common.getConfig(),
config: req.app.config,
session: req.session,
searchTerm: searchTerm,
message: common.clearSessionValue(req.session, 'message'),
@ -130,7 +130,7 @@ router.get('/admin/customers/filter/:search', common.restrict, (req, res, next)
});
// login the customer and check the password
router.post('/customer/login_action', (req, res) => {
router.post('/customer/login_action', async (req, res) => {
let db = req.app.db;
db.customers.findOne({email: req.body.loginEmail}, (err, customer) => { // eslint-disable-line
@ -178,7 +178,7 @@ router.get('/customer/forgotten', (req, res) => {
title: 'Forgotten',
route: 'customer',
forgotType: 'customer',
config: common.getConfig(),
config: req.app.config,
helpers: req.handlebars.helpers,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
@ -189,7 +189,7 @@ router.get('/customer/forgotten', (req, res) => {
// forgotten password
router.post('/customer/forgotten_action', (req, res) => {
const db = req.app.db;
const config = common.getConfig();
const config = req.app.config;
let passwordToken = randtoken.generate(30);
// find the user
@ -240,7 +240,7 @@ router.get('/customer/reset/:token', (req, res) => {
title: 'Reset password',
token: req.params.token,
route: 'customer',
config: common.getConfig(),
config: req.app.config,
message: common.clearSessionValue(req.session, 'message'),
message_type: common.clearSessionValue(req.session, 'message_type'),
show_footer: 'show_footer',

View File

@ -6,10 +6,9 @@ const _ = require('lodash');
const common = require('../lib/common');
// These is the customer facing routes
router.get('/payment/:orderId', async (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
let config = req.app.config;
// render the payment complete message
db.orders.findOne({_id: common.getId(req.params.orderId)}, async (err, result) => {
@ -18,7 +17,7 @@ router.get('/payment/:orderId', async (req, res, next) => {
}
res.render(`${config.themeViews}payment_complete`, {
title: 'Payment complete',
config: common.getConfig(),
config: req.app.config,
session: req.session,
pageCloseBtn: common.showCartCloseBtn('payment'),
result: result,
@ -32,7 +31,7 @@ router.get('/payment/:orderId', async (req, res, next) => {
});
router.get('/checkout', async (req, res, next) => {
let config = common.getConfig();
let config = req.app.config;
// if there is no items in the cart then render a failure
if(!req.session.cart){
@ -45,7 +44,7 @@ router.get('/checkout', async (req, res, next) => {
// render the checkout
res.render(`${config.themeViews}checkout`, {
title: 'Checkout',
config: common.getConfig(),
config: req.app.config,
session: req.session,
pageCloseBtn: common.showCartCloseBtn('checkout'),
checkout: 'hidden',
@ -58,7 +57,7 @@ router.get('/checkout', async (req, res, next) => {
});
router.get('/pay', async (req, res, next) => {
const config = common.getConfig();
const config = req.app.config;
// if there is no items in the cart then render a failure
if(!req.session.cart){
@ -71,7 +70,7 @@ router.get('/pay', async (req, res, next) => {
// render the payment page
res.render(`${config.themeViews}pay`, {
title: 'Pay',
config: common.getConfig(),
config: req.app.config,
paymentConfig: common.getPaymentConfig(),
pageCloseBtn: common.showCartCloseBtn('pay'),
session: req.session,
@ -85,14 +84,14 @@ router.get('/pay', async (req, res, next) => {
});
router.get('/cartPartial', (req, res) => {
const config = common.getConfig();
const config = req.app.config;
res.render(`${config.themeViews}cart`, {
pageCloseBtn: common.showCartCloseBtn(req.query.path),
page: req.query.path,
layout: false,
helpers: req.handlebars.helpers,
config: common.getConfig(),
config: req.app.config,
session: req.session
});
});
@ -100,7 +99,7 @@ router.get('/cartPartial', (req, res) => {
// show an individual product
router.get('/product/:id', (req, res) => {
let db = req.app.db;
let config = common.getConfig();
let config = req.app.config;
db.products.findOne({$or: [{_id: common.getId(req.params.id)}, {productPermalink: req.params.id}]}, (err, result) => {
// render 404 if page is not published
@ -115,6 +114,12 @@ router.get('/product/:id', (req, res) => {
productOptions = JSON.parse(result.productOptions);
}
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(result);
return;
}
// show the view
common.getImages(result._id, req, res, async (images) => {
res.render(`${config.themeViews}product`, {
@ -223,58 +228,59 @@ router.post('/product/addtocart', (req, res, next) => {
db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => {
if(err){
console.error(colors.red('Error adding to cart', err));
return res.status(400).json({message: 'Error updating cart. Please try again.'});
}
// We item is found, add it to the cart
if(product){
let productPrice = parseFloat(product.productPrice).toFixed(2);
// No product found
if(!product){
return res.status(400).json({message: 'Error updating cart. Please try again.'});
}
// Doc used to test if existing in the cart with the options. If not found, we add new.
let options = {};
if(req.body.productOptions){
options = JSON.parse(req.body.productOptions);
}
let findDoc = {
productId: req.body.productId,
options: options
};
let productPrice = parseFloat(product.productPrice).toFixed(2);
// if exists we add to the existing value
let cartIndex = _.findIndex(req.session.cart, findDoc);
if(cartIndex > -1){
req.session.cart[cartIndex].quantity = parseInt(req.session.cart[cartIndex].quantity) + productQuantity;
req.session.cart[cartIndex].totalItemPrice = productPrice * parseInt(req.session.cart[cartIndex].quantity);
}else{
// Doesnt exist so we add to the cart session
req.session.cartTotalItems = req.session.cartTotalItems + productQuantity;
// Doc used to test if existing in the cart with the options. If not found, we add new.
let options = {};
if(req.body.productOptions){
options = JSON.parse(req.body.productOptions);
}
let findDoc = {
productId: req.body.productId,
options: options
};
// new product deets
let productObj = {};
productObj.productId = req.body.productId;
productObj.title = product.productTitle;
productObj.quantity = productQuantity;
productObj.totalItemPrice = productPrice * productQuantity;
productObj.options = options;
productObj.productImage = product.productImage;
if(product.productPermalink){
productObj.link = product.productPermalink;
}else{
productObj.link = product._id;
}
// merge into the current cart
req.session.cart.push(productObj);
}
// update total cart amount
common.updateTotalCartAmount(req, res);
// update how many products in the shopping cart
req.session.cartTotalItems = Object.keys(req.session.cart).length;
res.status(200).json({message: 'Cart successfully updated', totalCartItems: Object.keys(req.session.cart).length});
// if exists we add to the existing value
let cartIndex = _.findIndex(req.session.cart, findDoc);
if(cartIndex > -1){
req.session.cart[cartIndex].quantity = parseInt(req.session.cart[cartIndex].quantity) + productQuantity;
req.session.cart[cartIndex].totalItemPrice = productPrice * parseInt(req.session.cart[cartIndex].quantity);
}else{
res.status(400).json({message: 'Error updating cart. Please try again.'});
// Doesnt exist so we add to the cart session
req.session.cartTotalItems = req.session.cartTotalItems + productQuantity;
// new product deets
let productObj = {};
productObj.productId = req.body.productId;
productObj.title = product.productTitle;
productObj.quantity = productQuantity;
productObj.totalItemPrice = productPrice * productQuantity;
productObj.options = options;
productObj.productImage = product.productImage;
if(product.productPermalink){
productObj.link = product.productPermalink;
}else{
productObj.link = product._id;
}
// merge into the current cart
req.session.cart.push(productObj);
}
// update total cart amount
common.updateTotalCartAmount(req, res);
// update how many products in the shopping cart
req.session.cartTotalItems = Object.keys(req.session.cart).length;
return res.status(200).json({message: 'Cart successfully updated', totalCartItems: Object.keys(req.session.cart).length});
});
});
@ -283,7 +289,7 @@ router.get('/search/:searchTerm/:pageNum?', (req, res) => {
let db = req.app.db;
let searchTerm = req.params.searchTerm;
let productsIndex = req.app.productsIndex;
let config = common.getConfig();
let config = req.app.config;
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
let lunrIdArray = [];
@ -301,12 +307,18 @@ router.get('/search/:searchTerm/:pageNum?', (req, res) => {
common.getMenu(db)
])
.then(([results, menu]) => {
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
res.render(`${config.themeViews}index`, {
title: 'Results',
results: results.data,
filtered: true,
session: req.session,
metaDescription: common.getConfig().cartTitle + ' - Search term: ' + searchTerm,
metaDescription: req.app.config.cartTitle + ' - Search term: ' + searchTerm,
searchTerm: searchTerm,
pageCloseBtn: common.showCartCloseBtn('search'),
message: common.clearSessionValue(req.session, 'message'),
@ -331,7 +343,7 @@ router.get('/category/:cat/:pageNum?', (req, res) => {
let db = req.app.db;
let searchTerm = req.params.cat;
let productsIndex = req.app.productsIndex;
let config = common.getConfig();
let config = req.app.config;
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
let lunrIdArray = [];
@ -351,13 +363,19 @@ router.get('/category/:cat/:pageNum?', (req, res) => {
.then(([results, menu]) => {
const sortedMenu = common.sortMenu(menu);
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
res.render(`${config.themeViews}index`, {
title: 'Category',
results: results.data,
filtered: true,
session: req.session,
searchTerm: searchTerm,
metaDescription: common.getConfig().cartTitle + ' - Category: ' + searchTerm,
metaDescription: req.app.config.cartTitle + ' - Category: ' + searchTerm,
pageCloseBtn: common.showCartCloseBtn('category'),
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
@ -380,7 +398,7 @@ router.get('/category/:cat/:pageNum?', (req, res) => {
// return sitemap
router.get('/sitemap.xml', (req, res, next) => {
let sm = require('sitemap');
let config = common.getConfig();
let config = req.app.config;
common.addSitemapProducts(req, res, (err, products) => {
if(err){
@ -412,7 +430,7 @@ router.get('/sitemap.xml', (req, res, next) => {
router.get('/page/:pageNum', (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
let config = req.app.config;
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
Promise.all([
@ -420,15 +438,21 @@ router.get('/page/:pageNum', (req, res, next) => {
common.getMenu(db)
])
.then(([results, menu]) => {
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
res.render(`${config.themeViews}index`, {
title: 'Shop',
results: results.data,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
metaDescription: common.getConfig().cartTitle + ' - Products page: ' + req.params.pageNum,
metaDescription: req.app.config.cartTitle + ' - Products page: ' + req.params.pageNum,
pageCloseBtn: common.showCartCloseBtn('page'),
config: common.getConfig(),
config: req.app.config,
productsPerPage: numberProducts,
totalProductCount: results.totalProducts,
pageNum: req.params.pageNum,
@ -446,7 +470,7 @@ router.get('/page/:pageNum', (req, res, next) => {
// The main entry point of the shop
router.get('/:page?', (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
let config = req.app.config;
let numberProducts = config.productsPerPage ? config.productsPerPage : 6;
// if no page is specified, just render page 1 of the cart
@ -456,6 +480,12 @@ router.get('/:page?', (req, res, next) => {
common.getMenu(db)
])
.then(([results, menu]) => {
// If JSON query param return json instead
if(req.query.json === 'true'){
res.status(200).json(results.data);
return;
}
res.render(`${config.themeViews}index`, {
title: `${config.cartTitle} - Shop`,
theme: config.theme,
@ -464,7 +494,7 @@ router.get('/:page?', (req, res, next) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
pageCloseBtn: common.showCartCloseBtn('page'),
config: common.getConfig(),
config: req.app.config,
productsPerPage: numberProducts,
totalProductCount: results.totalProducts,
pageNum: 1,
@ -497,8 +527,8 @@ router.get('/:page?', (req, res, next) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
pageCloseBtn: common.showCartCloseBtn('page'),
config: common.getConfig(),
metaDescription: common.getConfig().cartTitle + ' - ' + page,
config: req.app.config,
metaDescription: req.app.config.cartTitle + ' - ' + page,
helpers: req.handlebars.helpers,
showFooter: 'showFooter',
menu: common.sortMenu(await common.getMenu(db))
@ -506,7 +536,7 @@ router.get('/:page?', (req, res, next) => {
}else{
res.status(404).render('error', {
title: '404 Error - Page not found',
config: common.getConfig(),
config: req.app.config,
message: '404 Error - Page not found',
helpers: req.handlebars.helpers,
showFooter: 'showFooter',

View File

@ -15,7 +15,7 @@ router.get('/admin/orders', common.restrict, (req, res, next) => {
title: 'Cart',
orders: orders,
admin: true,
config: common.getConfig(),
config: req.app.config,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
@ -45,7 +45,7 @@ router.get('/admin/orders/bystatus/:orderstatus', common.restrict, (req, res, ne
admin: true,
filteredOrders: true,
filteredStatus: req.params.orderstatus,
config: common.getConfig(),
config: req.app.config,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
@ -69,7 +69,7 @@ router.get('/admin/order/view/:id', common.restrict, (req, res) => {
title: 'View order',
result: result,
productOptions: productOptions,
config: common.getConfig(),
config: req.app.config,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
@ -100,7 +100,7 @@ router.get('/admin/orders/filter/:search', common.restrict, (req, res, next) =>
title: 'Order results',
orders: orders,
admin: true,
config: common.getConfig(),
config: req.app.config,
session: req.session,
searchTerm: searchTerm,
message: common.clearSessionValue(req.session, 'message'),

View File

@ -7,7 +7,7 @@ const router = express.Router();
// The homepage of the site
router.post('/checkout_action', (req, res, next) => {
const db = req.app.db;
const config = common.getConfig();
const config = req.app.config;
const authorizenetConfig = common.getPaymentConfig();
let authorizeUrl = 'https://api.authorize.net/xml/v1/request.api';

View File

@ -10,7 +10,7 @@ router.get('/checkout_cancel', (req, res, next) => {
router.get('/checkout_return', (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
let config = req.app.config;
let paymentId = req.session.paymentId;
let payerId = req.query['PayerID'];
@ -107,7 +107,7 @@ router.get('/checkout_return', (req, res, next) => {
// The homepage of the site
router.post('/checkout_action', (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
let config = req.app.config;
let paypalConfig = common.getPaymentConfig();
// setup the payment object

View File

@ -7,7 +7,7 @@ let router = express.Router();
// The homepage of the site
router.post('/checkout_action', (req, res, next) => {
let db = req.app.db;
let config = common.getConfig();
let config = req.app.config;
let stripeConfig = common.getPaymentConfig();
// charge via stripe

View File

@ -18,7 +18,7 @@ router.get('/admin/products', common.restrict, (req, res, next) => {
top_results: topResults,
session: req.session,
admin: true,
config: common.getConfig(),
config: req.app.config,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers
@ -45,7 +45,7 @@ router.get('/admin/products/filter/:search', (req, res, next) => {
title: 'Results',
results: results,
admin: true,
config: common.getConfig(),
config: req.app.config,
session: req.session,
searchTerm: searchTerm,
message: common.clearSessionValue(req.session, 'message'),
@ -69,7 +69,7 @@ router.get('/admin/product/new', common.restrict, common.checkAccess, (req, res)
editor: true,
admin: true,
helpers: req.handlebars.helpers,
config: common.getConfig()
config: req.app.config
});
});
@ -167,7 +167,7 @@ router.get('/admin/product/edit/:id', common.restrict, common.checkAccess, (req,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
config: common.getConfig(),
config: req.app.config,
editor: true,
helpers: req.handlebars.helpers
});

View File

@ -15,7 +15,7 @@ router.get('/admin/users', common.restrict, (req, res) => {
title: 'Users',
users: users,
admin: true,
config: common.getConfig(),
config: req.app.config,
isAdmin: req.session.isAdmin,
helpers: req.handlebars.helpers,
session: req.session,
@ -49,7 +49,7 @@ router.get('/admin/user/edit/:id', common.restrict, (req, res) => {
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers,
config: common.getConfig()
config: req.app.config
});
});
});
@ -63,7 +63,7 @@ router.get('/admin/user/new', common.restrict, (req, res) => {
helpers: req.handlebars.helpers,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
config: common.getConfig()
config: req.app.config
});
});

147
test/test.js Normal file
View File

@ -0,0 +1,147 @@
const test = require('ava');
const axios = require('axios');
const fs = require('fs');
const app = require('../app');
const common = require('../lib/common');
// Get test data to compare in tests
const rawTestData = fs.readFileSync('./bin/testdata.json', 'utf-8');
const testData = JSON.parse(rawTestData);
let config;
let db;
let baseUrl;
let products;
let customers;
let users;
// Start up app and wait for it to be ready
test.before.cb(t => {
app.on('appStarted', async () => {
// Set some stuff now we have the app started
config = app.config;
db = app.db;
baseUrl = `http://localhost:${app.port}`;
await common.testData(app);
products = await db.products.find({}).toArray();
customers = await db.customers.find({}).toArray();
users = await db.users.find({}).toArray();
t.end();
});
});
test('[Success] Get products JSON', t => {
return new Promise((resolve, reject) => {
axios.get(`${baseUrl}?json=true`)
.then((response) => {
if(response.data.length < config.productsPerPage){
t.is(response.data.length, testData.products.length);
}else{
t.is(response.data.length, config.productsPerPage);
}
t.pass();
resolve();
})
.catch((error) => {
reject(new Error('Should not be allowed'));
});
});
});
test('[Success] User Login', t => {
return new Promise((resolve, reject) => {
axios.post(`${baseUrl}/admin/login_action`, {
email: users[0].userEmail,
password: 'test'
})
.then((response) => {
t.deepEqual(response.data.message, 'Login successful');
resolve();
})
.catch((error) => {
reject(new Error('Should not be allowed'));
});
});
});
test('[Fail] Incorrect user password', t => {
return new Promise((resolve, reject) => {
axios.post(`${baseUrl}/admin/login_action`, {
email: users[0].userEmail,
password: 'test1'
})
.then((response) => {
reject(new Error('Should not be allowed'));
})
.catch((error) => {
t.deepEqual(error.response.data.message, 'Access denied. Check password and try again.');
resolve();
});
});
});
test('[Fail] Customer login with incorrect email', t => {
return new Promise((resolve, reject) => {
axios.post(`${baseUrl}/customer/login_action`, {
loginEmail: 'test1@test.com',
loginPassword: 'test'
})
.then((response) => {
reject(new Error('Should not be allowed'));
})
.catch((error) => {
t.deepEqual(error.response.data.err, 'A customer with that email does not exist.');
resolve();
});
});
});
test('[Success] Customer login with correct email', t => {
return new Promise((resolve, reject) => {
axios.post(`${baseUrl}/customer/login_action`, {
loginEmail: 'test@test.com',
loginPassword: 'test'
})
.then((response) => {
t.deepEqual(response.data.message, 'Successfully logged in');
resolve();
})
.catch((error) => {
reject(new Error('Should not be allowed'));
});
});
});
test('[Success] Add product to cart', t => {
return new Promise((resolve, reject) => {
axios.post(`${baseUrl}/product/addtocart`, {
productId: products[0]._id,
productQuantity: 1,
productOptions: JSON.stringify(products[0].productOptions)
})
.then((response) => {
t.deepEqual(response.data.message, 'Cart successfully updated');
resolve();
})
.catch((error) => {
reject(new Error('Should not be allowed'));
});
});
});
test('[Fail] Add incorrect product to cart', t => {
return new Promise((resolve, reject) => {
axios.post(`${baseUrl}/product/addtocart`, {
productId: 'someid'
})
.then((response) => {
t.deepEqual(response.data.message, 'Successfully logged in');
reject(new Error('Should not be allowed'));
resolve();
})
.catch((error) => {
t.deepEqual(error.response.data.message, 'Error updating cart. Please try again.');
resolve();
});
});
});

View File

@ -1,13 +1,13 @@
<div class="col-md-offset-4 col-md-4 col-lg-offset-4 col-lg-4" style="padding-top: 100px" >
<form class="form-signin" action="login_action" method="post" role="form" data-toggle="validator">
<form class="form-signin" method="post" role="form" data-toggle="validator">
<input type="hidden" name="frm_referringUrl" value="{{referringUrl}}">
<h2 class="form-signin-heading">Please sign in</h2>
<div class="form-group">
<input type="email" name="email" class="form-control" placeholder="email address" required autofocus>
<input type="email" id="email" name="email" class="form-control" placeholder="email address" required autofocus>
</div>
<div class="form-group">
<input type="password" name="password" class="form-control" placeholder="Password" required>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
<button class="btn btn-lg btn-primary btn-block" id="loginForm" type="submit">Sign in</button>
</form>
</div>