Skip to content

Commit

Permalink
feat: new todo
Browse files Browse the repository at this point in the history
Signed-off-by: Ourchitecture <[email protected]>
  • Loading branch information
ourchitectureio committed Sep 22, 2023
1 parent dc039ec commit b6aa039
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 32 deletions.
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
**/.task-output
**/.wireit
**/node_modules
**/test-results/
**/playwright-report/
**/playwright/.cache/
**/test-results
**/playwright-report
**/playwright/.cache
6 changes: 0 additions & 6 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# If pushing tags, don't test anything.
grep "refs/tags/" </dev/stdin >/dev/null
if [ "$?" -eq "0" ]; then
exit 0
fi

pnpm test

# leave a little room before git messages
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var logger = require('morgan')
var session = require('express-session')

var indexRouter = require('./routes/index')
var usersRouter = require('./routes/users')
var createTodoRouter = require('./routes/createTodo')

var app = express()

Expand Down Expand Up @@ -35,7 +35,7 @@ app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))

app.use('/', indexRouter)
app.use('/users', usersRouter)
app.use('/todos/create', createTodoRouter)

// catch 404 and forward to error handler
app.use(function (req, res, next) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"command": "nodemon ./bin/www"
},
"test": {
"command": "playwright test"
"command": "playwright test --quiet"
},
"test:ui": {
"command": "playwright test --ui"
Expand All @@ -27,7 +27,8 @@
"express-session": "^1.17.3",
"http-errors": "~1.6.3",
"morgan": "~1.9.1",
"pug": "2.0.0-beta11"
"pug": "2.0.0-beta11",
"uid-safe": "^2.1.5"
},
"devDependencies": {
"@playwright/test": "^1.38.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ module.exports = defineConfig({
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
// Concise 'dot' for CI, default 'list' when running locally
reporter: process.env.CI ? 'dot' : 'list',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
body {
padding: 50px;
margin: auto;
padding: 1em;
font:
14px 'Lucida Grande',
Helvetica,
Arial,
sans-serif;
}

h1 {
font-size: 2em;
font-weight: bold;
}

main {
padding: 2em;
}

a {
color: #00b7ff;
}

ul {
list-style: circle;
}

li {
margin-left: 1.5em;
}

#error {
margin: 1em;
color: red;
font-style: italic;
}

input[name='new-todo'] {
margin-left: 0.5em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var express = require('express')
var uid = require('uid-safe')

var router = express.Router()

router.post('/', function (req, res, next) {
if (!req.session.todos) {
req.session.todos = []
}

const newTodoText = req.body['new-todo']

if (!newTodoText) {
res.redirect(
'/?e=' + encodeURIComponent('Missing new-todo field value')
)
return
}

if (newTodoText.trim().length === 0) {
res.redirect(
'/?e=' +
encodeURIComponent('Empty or whitespace new-todo field value')
)
return
}

req.session.todos.push({
id: uid.sync(18),
text: req.body['new-todo'],
})

res.redirect('/')
})

module.exports = router
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ router.get('/', function (req, res, next) {
res.render('index', {
title: 'Our Todos',
todos: req.session.todos,
error: req.query.e || null,
})
})

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,20 +1,132 @@
// @ts-check
const { test, expect } = require('@playwright/test')

const CONFIG = {
home: {
url: 'http://localhost:3000/',
titlePattern: /Our Todos/,
newTodo: {
selector: 'input[name="new-todo"]',
},
newTodoSubmit: {
text: 'Submit',
},
error: {
selector: '#error',
},
main: {
selector: '#main',
},
footer: {
selector: '#footer',
},
},
}

test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:3000/')
await page.goto(CONFIG.home.url)
})

test('has title', async ({ page }) => {
await expect(page).toHaveTitle(/Our Todos/)
await expect(page).toHaveTitle(CONFIG.home.titlePattern)
})

test('main is hidden without todos', async ({ page }) => {
await expect(page.locator('#main')).toHaveCount(0)
// When there are no todos, #main and #footer should be hidden.
// https://github.com/tastejs/todomvc/blob/master/app-spec.md#no-todos
test.describe('No todos', () => {
test('main is hidden without todos', async ({ page }) => {
await expect(page.locator(CONFIG.home.main.selector)).toHaveCount(0)
})

test('footer is hidden without todos', async ({ page }) => {
await expect(page.locator(CONFIG.home.footer.selector)).toHaveCount(0)
})
})

test('footer is hidden without todos', async ({ page }) => {
await expect(page.locator('#footer')).toHaveCount(0)
/*
New todos are entered in the input at the top of the app. The input element
should be focused when the page is loaded, preferably by using the autofocus
input attribute. Pressing Enter creates the todo, appends it to the todo
list, and clears the input. Make sure to .trim() the input and then check
that it's not empty before creating a new todo.
*/
// https://github.com/tastejs/todomvc/blob/master/app-spec.md#new-todo
test.describe('New todo', () => {
test('todo input exists', async ({ page }) => {
const newTodoElement = page.locator(CONFIG.home.newTodo.selector)

await expect(newTodoElement).toBeVisible()
await expect(newTodoElement).toHaveValue('')
})

test('todo input has focus', async ({ page }) => {
const newTodoElement = page.locator(CONFIG.home.newTodo.selector)

const newTodoAutoFocusValue =
await newTodoElement.getAttribute('autofocus')

await expect(newTodoAutoFocusValue).toBe('')
})

test('missing new todo displays error', async ({ page }) => {
const newTodoElement = page.locator(CONFIG.home.newTodo.selector)

// submits the form
await newTodoElement.press('Enter')

await page.waitForSelector(CONFIG.home.error.selector)

await page.screenshot({ path: 'empty-todo-error.png' })

const errorElement = page.locator(CONFIG.home.error.selector)

await expect(errorElement).toBeVisible()

// BUG: not sure why `toHaveText(...)` is failing
const errorText = await errorElement.textContent()

expect(errorText).toBe('Missing new-todo field value')
})

test('empty new todo displays error', async ({ page }) => {
const newTodoElement = page.locator(CONFIG.home.newTodo.selector)

// fill with whitespace
await newTodoElement.fill(' ')

// submits the form
await newTodoElement.press('Enter')

await page.waitForSelector(CONFIG.home.error.selector)

const errorElement = page.locator(CONFIG.home.error.selector)

await expect(errorElement).toBeVisible()

// BUG: not sure why `toHaveText(...)` is failing
const errorText = await errorElement.textContent()

expect(errorText).toBe('Empty or whitespace new-todo field value')
})

test('enter new todo; main and footer display', async ({ page }) => {
const newTodoElement = page.locator(CONFIG.home.newTodo.selector)
await newTodoElement.fill('testing a new todo')

// submits the form
await newTodoElement.press('Enter')

await page.waitForSelector(CONFIG.home.main.selector)

const newTodoElementAfterRedirect = page.locator(
CONFIG.home.newTodo.selector
)
await expect(newTodoElementAfterRedirect).toHaveValue('')

await expect(page.locator(CONFIG.home.error.selector)).not.toBeVisible()
await expect(page.locator(CONFIG.home.main.selector)).toBeVisible()
await expect(page.locator(CONFIG.home.footer.selector)).toBeVisible()
})
})

// test('get started link', async ({ page }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@ extends layout

block content
header
h1= title
h1=title
main
form(method="POST",action="/todos/create")
label New todo
input(name="new-todo",placeholder="Describe the todo",value="",autofocus)
if error
div#error= error
if todos.length > 0
section#main
ul
each todo in todos
li(data-id=todo.id) #{todo.text}
if todos.length > 0
main#main Some main content
footer#footer Some footer content

0 comments on commit b6aa039

Please sign in to comment.