Skip to content

Commit

Permalink
Merge with master
Browse files Browse the repository at this point in the history
  • Loading branch information
aslakhellesoy committed Oct 10, 2017
2 parents 13fac46 + c907557 commit dd89cca
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 77 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules
local.log
browserstack.err
.DS_Store
todo-subsecond.sqlite
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# TODO Subsecond

## Install Software

* [Node.js](https://nodejs.org/en/download/)

## Create Database

If you have Postgres installed you can use that. Otherwise you can use SQlite.

### Postgres

createdb todo-subsecond

### SQlite

export DATABASE_URL=sqlite:./todo-subsecond.sqlite

## Install dependencies

npm install

## Run tests

npm test

Or:

./features/run/all # See other scripts in the same directory

### Change browser

The `webdriver-*` assemblies will use Chrome by default. You can change to another browser if you want:

export SELENIUM_BROWSER=firefox
brew install geckodriver

## Run the server

npm start
8 changes: 4 additions & 4 deletions features/step_definitions/todo_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ const { Given, When, Then, setDefaultTimeout } = require('cucumber')

setDefaultTimeout(30000)

Given('there is/are already {int} todo(s)', async function (count) {
Given('there is/are already {int} todo(s)', async function(count) {
const todoList = await this.contextTodoList()
for (let i = 0; i < count; i++)
await todoList.addTodo({ text: `Todo ${i}` })
})

When('I add {string}', async function (text) {
When('I add {string}', async function(text) {
const todoList = await this.actionTodoList()
await todoList.addTodo({ text })
})

Then('the text of the {int}st/nd/th todo should be {string}', async function (todoNumber, text) {
Then('the text of the {ordinal} todo should be {string}', async function(index, text) {
const todoList = await this.outcomeTodoList()
const todos = await todoList.getTodos()
assert.equal(todos[todoNumber-1].text, text)
assert.equal(todos[index].text, text)
})
9 changes: 9 additions & 0 deletions features/support/parameter_types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { defineParameterType } = require('cucumber')

defineParameterType({
name: 'ordinal',
regexp: /(\d+)(?:st|nd|rd|th)/,
transformer(s) {
return parseInt(s) - 1
}
})
70 changes: 35 additions & 35 deletions features/support/world.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const fs = require('fs')
const path = require('path')

const { setWorldConstructor, Before, After } = require('cucumber')
const memoize = require('map-memo')
const { setWorldConstructor, After } = require('cucumber')
const DatabaseTodoList = require('../../lib/server/DatabaseTodoList')
const HttpTodoList = require('../../lib/client/HttpTodoList')
const BrowserApp = require('../../lib/client/BrowserApp')
Expand All @@ -12,7 +11,7 @@ const DomTodoList = require('../../test_support/DomTodoList')
const WebDriverTodoList = require('../../test_support/WebDriverTodoList')
const BrowserStackTodoList = require('../../test_support/BrowserStackTodoList')

if(process.env.CUCUMBER_DOM === 'true') {
if (process.env.CUCUMBER_DOM === 'true') {
// This is primarily for debugging - cucumber-electron doesn't always provide
// good error messages (because of Electron?)
const { JSDOM } = require("jsdom")
Expand All @@ -28,9 +27,9 @@ class TodoWorld {
this._truncate = true
this._stoppables = []

const memoryTodoList = memoize(async () => new MemoryTodoList())
const memoryTodoList = new MemoryTodoList()

const domTodoList = async todoList => {
const makeDomTodoList = async todoList => {
const publicIndexHtmlPath = path.join(__dirname, '..', '..', 'public', 'index.html')
const html = fs.readFileSync(publicIndexHtmlPath, 'utf-8')
const domNode = document.createElement('div')
Expand All @@ -40,88 +39,89 @@ class TodoWorld {
return new DomTodoList(domNode)
}

const databaseTodoList = async() => {
const makeDatabaseTodoList = async () => {
const databaseTodoList = new DatabaseTodoList()
await databaseTodoList.start(this._truncate)
this._truncate = false
return databaseTodoList
}

const httpTodoList = memoize(async webApppTodoList => {
const makeHttpTodoList = async webApppTodoList => {
const webApp = new WebApp({ todoList: webApppTodoList, serveClient: false })
const port = await webApp.listen(0)
this._stoppables.push(webApp)
return new HttpTodoList(`http://localhost:${port}`)
})
}

const webDriverTodoList = memoize(async webApppTodoList => {
const makeWebDriverTodoList = async webApppTodoList => {
const webApp = new WebApp({ todoList: webApppTodoList, serveClient: true })
const port = await webApp.listen(0)
this._stoppables.push(webApp)
const webDriverTodoList = new WebDriverTodoList(`http://localhost:${port}`)
await webDriverTodoList.start()
this._stoppables.push(webDriverTodoList)
return webDriverTodoList
})
}

const browserStackTodoList = memoize(async webApppTodoList => {
const makeBrowserStackTodoList = async webApppTodoList => {
const webApp = new WebApp({ todoList: webApppTodoList, serveClient: true })
const port = await webApp.listen(0)
this._stoppables.push(webApp)
const browserStackTodoList = new BrowserStackTodoList(`http://localhost:${port}`)
await browserStackTodoList.start()
this._stoppables.push(browserStackTodoList)
return browserStackTodoList
})
}

const assemblies = {
'memory': {
contextTodoList: async () => memoryTodoList(),
actionTodoList: async () => memoryTodoList(),
outcomeTodoList: async () => memoryTodoList(),
contextTodoList: async () => memoryTodoList,
actionTodoList: async () => memoryTodoList,
outcomeTodoList: async () => memoryTodoList,
},
'database': {
contextTodoList: async () => databaseTodoList(),
actionTodoList: async () => databaseTodoList(),
outcomeTodoList: async () => databaseTodoList(),
contextTodoList: async () => makeDatabaseTodoList(),
actionTodoList: async () => makeDatabaseTodoList(),
outcomeTodoList: async () => makeDatabaseTodoList(),
},
'http-memory': {
contextTodoList: async () => memoryTodoList(),
actionTodoList: async () => httpTodoList(await memoryTodoList()),
outcomeTodoList: async () => httpTodoList(await memoryTodoList()),
contextTodoList: async () => memoryTodoList,
actionTodoList: async () => makeHttpTodoList(memoryTodoList),
outcomeTodoList: async () => makeHttpTodoList(memoryTodoList),
},
'dom-memory': {
contextTodoList: async () => memoryTodoList(),
actionTodoList: async () => domTodoList(await memoryTodoList()),
outcomeTodoList: async () => domTodoList(await memoryTodoList()),
contextTodoList: async () => memoryTodoList,
actionTodoList: async () => makeDomTodoList(memoryTodoList),
outcomeTodoList: async () => makeDomTodoList(memoryTodoList),
},
'dom-http-memory': {
contextTodoList: async () => memoryTodoList(),
actionTodoList: async () => domTodoList(await httpTodoList(await memoryTodoList())),
outcomeTodoList: async () => domTodoList(await httpTodoList(await memoryTodoList())),
contextTodoList: async () => memoryTodoList,
actionTodoList: async () => makeDomTodoList(await makeHttpTodoList(memoryTodoList)),
outcomeTodoList: async () => makeDomTodoList(await makeHttpTodoList(memoryTodoList)),
},
'webdriver-database': {
contextTodoList: async () => databaseTodoList(),
actionTodoList: async () => webDriverTodoList(await databaseTodoList()),
outcomeTodoList: async () => webDriverTodoList(await databaseTodoList())
contextTodoList: async () => makeDatabaseTodoList(),
actionTodoList: async () => makeWebDriverTodoList(await makeDatabaseTodoList()),
outcomeTodoList: async () => makeWebDriverTodoList(await makeDatabaseTodoList())
},
'webdriver-memory': {
contextTodoList: async () => memoryTodoList(),
actionTodoList: async () => webDriverTodoList(await memoryTodoList()),
outcomeTodoList: async () => webDriverTodoList(await memoryTodoList())
actionTodoList: async () => makeWebDriverTodoList(memoryTodoList),
outcomeTodoList: async () => makeWebDriverTodoList(memoryTodoList)
},
'browserstack-memory': {
contextTodoList: async () => memoryTodoList(),
actionTodoList: async () => browserStackTodoList(await memoryTodoList()),
outcomeTodoList: async () => browserStackTodoList(await memoryTodoList())
actionTodoList: async () => makeBrowserStackTodoList(memoryTodoList),
outcomeTodoList: async () => makeBrowserStackTodoList(memoryTodoList)
}
}

Object.assign(this, assemblies[assembly])
}
}

setWorldConstructor(TodoWorld)

After(async function () {
After(async function() {
return Promise.all(this._stoppables.map(stoppable => stoppable.stop()))
})
4 changes: 2 additions & 2 deletions lib/client/HttpTodoList.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ module.exports = class HttpTodoList {
},
body: JSON.stringify({ text })
})
if(res.status !== 201)
if (res.status !== 201)
throw new Error('Failed to create TODO')
}

async getTodos() {
const res = await this._fetch(`${this._baseUrl}/todos`)
if(res.status !== 200)
if (res.status !== 200)
throw new Error('Failed to get TODOs')
return res.json()
}
Expand Down
34 changes: 12 additions & 22 deletions lib/server/DatabaseTodoList.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
const Sequelize = require('sequelize');

let sequelize

if (process.env.DATABASE_URL) {
sequelize = new Sequelize(process.env.DATABASE_URL, {
dialect: 'postgres',
protocol: 'postgres'
})
} else {
sequelize = new Sequelize('todo-subsecond', '', '', {
dialect: 'postgres',
logging: false
})
}
module.exports = class DatabaseTodoList {

const Todo = sequelize.define('todo', {
text: Sequelize.STRING,
})
constructor() {
const databaseUrl = process.env.DATABASE_URL || 'postgres://localhost/todo-subsecond'
this._sequelize = new Sequelize(databaseUrl, { logging: false })

module.exports = class DatabaseTodoList {
this.Todo = this._sequelize.define('todo', {
text: Sequelize.STRING,
})
}

async start(truncate) {
await sequelize.sync()
if(truncate) {
await Todo.truncate()
if (truncate) {
await this._sequelize.sync({ force: true })
}
}

async addTodo({ text }) {
await Todo.create({ text })
await this.Todo.create({ text })
}

async getTodos() {
return Todo.all().map(record => ({
return this.Todo.all().map(record => ({
text: record.text
}))
}
Expand Down
2 changes: 1 addition & 1 deletion lib/server/WebApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = class WebApp {

app.use(express.static('./public'))

if(this._serveClient) {
if (this._serveClient) {
const browserify = require('browserify-middleware')
app.get('/client.js', browserify(__dirname + '/../client/client.js'))
}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"cucumber-electron": "^2.3.1",
"electron": "^1.7.6",
"jsdom": "^11.2.0",
"map-memo": "^2.1.1",
"selenium-webdriver": "^3.5.0"
},
"scripts": {
Expand All @@ -29,6 +28,7 @@
"node-fetch": "^1.7.3",
"pg": "^7.3.0",
"pg-hstore": "^2.3.2",
"sequelize": "^4.11.1"
"sequelize": "^4.11.1",
"sqlite3": "^3.1.13"
}
}
8 changes: 4 additions & 4 deletions test_support/BrowserStackTodoList.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ module.exports = class BrowserStackTodoList extends WebDriverTodoList {
return builder
.usingServer('http://hub-cloud.browserstack.com/wd/hub')
.withCapabilities({
'browserName' : 'firefox',
'browserstack.user' : process.env.BROWSERSTACK_USER,
'browserstack.key' : process.env.BROWSERSTACK_KEY,
'browserstack.local' : 'true'
'browserName': 'firefox',
'browserstack.user': process.env.BROWSERSTACK_USER,
'browserstack.key': process.env.BROWSERSTACK_KEY,
'browserstack.local': 'true'
})
}

Expand Down
2 changes: 1 addition & 1 deletion test_support/DomTodoList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module.exports = class DomTodoList {
}

async addTodo({ text }) {
return new Promise((resolve) => {
return new Promise(resolve => {
this._domNode.addEventListener('todos:todo:added', () => {
resolve()
})
Expand Down
15 changes: 9 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1909,10 +1909,6 @@ lower-case@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"

map-memo@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/map-memo/-/map-memo-2.1.1.tgz#f2017656955896ab7b75a501d5644c7b5df3ffac"

map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
Expand Down Expand Up @@ -2078,7 +2074,7 @@ mz@^2.4.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"

nan@^2.3.0:
nan@^2.3.0, nan@~2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"

Expand All @@ -2099,7 +2095,7 @@ node-fetch@^1.7.3:
encoding "^0.1.11"
is-stream "^1.0.1"

node-pre-gyp@^0.6.36:
node-pre-gyp@^0.6.36, node-pre-gyp@~0.6.38:
version "0.6.38"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz#e92a20f83416415bb4086f6d1fb78b3da73d113d"
dependencies:
Expand Down Expand Up @@ -2952,6 +2948,13 @@ split@^1.0.0:
dependencies:
through "2"

sqlite3@^3.1.13:
version "3.1.13"
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-3.1.13.tgz#d990a05627392768de6278bafd1a31fdfe907dd9"
dependencies:
nan "~2.7.0"
node-pre-gyp "~0.6.38"

sshpk@^1.7.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
Expand Down

0 comments on commit dd89cca

Please sign in to comment.