Skip to content

Instantly share code, notes, and snippets.

@Dyrits
Last active May 10, 2023 08:09
Show Gist options
  • Save Dyrits/d434433dfd3bbd51be730c85b9f3204f to your computer and use it in GitHub Desktop.
Save Dyrits/d434433dfd3bbd51be730c85b9f3204f to your computer and use it in GitHub Desktop.
Quote Keeper: Model Layer

Quote Keeper: Model Layer

In this project, you will be completing the test suite for the Quote Keeper application. The suite currently has feature and server tests to verify that a user can enter quote information, post it to the server, and view the information in the returned webpage. Those tests are passing, but the app doesn’t organize and store the quote data anywhere.

To test-drive that functionality, we’ve added one failing server test. It will push you to the model layer, where you will begin the red, green, refactor cycle, setting up a database and defining a model using MongoDB, Node’s Mongoose package, and the Chai assertion module.

Your suite will test code at every level of your application. When you write code in a new file, remember to import it to every layer so that your tests have access to it.

// database.js
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const env = process.env.NODE_ENV || 'development';
const databaseUrl = process.env.DATABASE_URL || `mongodb://localhost/quote-keeper_${env}`;
const options= {
useMongoClient: true,
};
const connectAndDrop = async () => {
await mongoose.connect(databaseUrl, options);
await mongoose.connection.db.dropDatabase();
};
const disconnect = async () => {
await mongoose.disconnect();
};
module.exports = {
mongoose,
databaseUrl,
options,
connectAndDrop,
disconnect,
};
// test/routes/index-test.js
const {assert} = require('chai');
const request = require('supertest');
const {jsdom} = require('jsdom');
const Quote = require("../../models/quote");
const {connectAndDrop, disconnect} = require('../../database');
const app = require('../../app');
const parseTextFromHTML = (htmlAsString, selector) => {
const selectedElement = jsdom(htmlAsString).querySelector(selector);
if (selectedElement !== null) {
return selectedElement.textContent;
} else {
throw new Error(`No element with selector ${selector} found in HTML string`);
}
};
describe('/', () => {
beforeEach(connectAndDrop);
afterEach(disconnect);
describe('POST', () => {
it('responds with the quote', async () => {
const quote = 'Our deepest fear is not that we are inadequate. Our deepest fear is that we are powerful beyond measure.';
const attributed = 'Marianne Williamson';
const source = 'A Return to Love: Reflections on the Principles of A Course in Miracles';
const response = await request(app)
.post('/')
.type('form')
.send({quote, attributed, source});
assert.equal(response.status, 200);
assert.include(parseTextFromHTML(response.text, '#quotes'), quote);
assert.include(parseTextFromHTML(response.text, '#quotes'), attributed);
assert.include(parseTextFromHTML(response.text, '#quotes'), source);
});
it('stores the quote', async () => {
const quote = 'Nothing is so painful to the human mind as a great and sudden change.';
const attributed = 'Mary Shelley';
const source = 'Frankenstein';
const response = await request(app)
.post('/')
.type('form')
.send({quote, attributed, source});
const citation = await Quote.findOne({});
assert.strictEqual(citation.quote, quote);
assert.strictEqual(citation.attributed, attributed);
assert.strictEqual(citation.source, source);
});
});
});
// routes/index.js
const express = require("express");
const router = express.Router();
const Quote = require("../models/quote");
router.get("/", (req, res) => {
res.render("index");
});
router.post("/", async (req, res) => {
const {quote, attributed, source} = req.body;
await Quote.create({ quote, attributed, source });
res.render("index", { quote, attributed, source });
});
module.exports = router;
const { assert } = require("chai");
const { mongoose, databaseUrl, options } = require("../../database");
const Quote = require("../../models/quote");
const { connectAndDrop, disconnect } = require("../../database");
describe("Quote", () => {
beforeEach(connectAndDrop);
afterEach(disconnect);
describe("#quote", () => {
it("is a string", () => {
const quote = "It always seems impossible until it's done.";
const citation = new Quote({
quote: "It always seems impossible until it's done.",
});
assert.strictEqual(citation.quote, quote);
});
});
describe("#attributed", () => {
it("is a String", () => {
const attributed = "James Baldwin";
const citation = new Quote({ attributed });
assert.isString(citation.attributed);
assert.strictEqual(citation.attributed, attributed);
});
});
describe("#source", () => {
it("is a String", () => {
const source = "Notes of a Native Son";
const citation = new Quote({ source });
assert.isString(citation.source);
assert.strictEqual(citation.source, source);
});
});
});
// models/quote.js
const { mongoose } = require("../database");
const quoteSchema = new mongoose.Schema({
quote: String,
attributed: String,
source: String,
});
const Quote = mongoose.model("Quote", quoteSchema);
module.exports = Quote;
// wdio.conf.js
const app = require('./app');
const port = process.env.PORT || 4001;
const {connectAndDrop, disconnect} = require('./database');
let expressServer;
exports.config = {
specs: [
'test/features/*.js',
],
coloredLogs: true,
baseUrl: `http://localhost:${port}/`,
framework: 'mocha',
reporters: ['spec'],
waitforTimeout: 10 * 1000,
capabilities: [{
browserName: 'phantomjs',
}],
services: ['phantomjs'],
async onPrepare() {
connectAndDrop();
expressServer = app.listen(port);
},
async onComplete() {
disconnect();
await expressServer.close();
},
};
#!/usr/bin/env node
// bin/www
/**
* Module dependencies.
*/
const app = require('../app');
const debug = require('debug')('calculator-js:server');
const http = require('http');
const {mongoose, databaseUrl, options} = require('../database');
/**
* Get port from environment and store in Express.
*/
const port = normalizePort(process.env.PORT || 4001);
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
mongoose.connect(databaseUrl, options).then(()=> {
server.listen(port);
});
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
const port = parseInt(val, 10);
if (isNaN(port)) {
// Named pipe
return val;
}
if (port >= 0) {
// Port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
const addr = server.address();
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment