Skip to content
Permalink
Browse files
  • Loading branch information
aa7401 committed Nov 11, 2019
2 parents 2b2eb4b + e471c1c commit d1b386290cb7b59779f781788832f7816827b44d
Show file tree
Hide file tree
Showing 13 changed files with 148 additions and 38 deletions.
@@ -83,21 +83,18 @@ So how can we improve this security? The simplest way is to use AES256-GCM encry

In this section we will be implementing more granular authorisation by creating an admin-only area on the website:

1. Add an extra boolean field to the database called `admin` with a default value of `false`.
2. Manually modify one of your registered accounts and change the stored value to `true`, this will be your admin user.
3. When a user logs in, check this field and, if it is set to true, create a new session variable called `admin` and assign it a value of `true`.
4. Create a new admin page, `admin.handlebars` and make sure it is protected in the same way as the home page.
5. Create a link to this new page from the homepage.
6. Modify the page so that, if the user does not have admin authorisation they get redirected back to the home page which should display a message **You do not have admin privileges!**.

## 5 Extension Tasks

Congratulations, you now have a working secure website shell which can be used as the base code for your coursework. You should now consider the following:

1. Start by encrypting your cookies using an appropriate npm package.
2. The website is currently using a relational database (SQLite). If you plan on using a different form of persistence such as a document database (eg MongoDB) or a graph database (eg Neo4J) this is the time to make the change. Make sure the existing functionality works with your chosen persistence technology.
3. In your last lab you learned how to build list and details pages and connect these together. Read your assignment brief carefully and decide if and how you will implement these. You should start work on this part of your project at the earliest opportunity.
4. You will need to implement one or more forms as part of your assignment. Analyse the problem and build template files containing your required forms. Also implement the code required to process this data and add it to your preferred database.
1. Create a secure admin page:
1. Add an extra boolean field to the database called `admin` with a default value of `false`.
2. Manually modify one of your registered accounts and change the stored value to `true`, this will be your admin user.
3. When a user logs in, check this field and, if it is set to true, create a new session variable called `admin` and assign it a value of `true`.
4. Create a new admin page, `admin.handlebars` and make sure it is protected in the same way as the home page.
5. Create a link to this new page from the homepage.
6. Modify the page so that, if the user does not have admin authorisation they get redirected back to the home page which should display a message **You do not have admin privileges!**.
2. Modify the secure pages (home and admin) so that they displays the username of the currently logged-in user:
1. Start by modifying the `post('login')` callback, adding a second key called `username` to the `ctx.session` object to store the username.
2. Modify the code that logs the user out to remove this key.
3. Now change the callback for the secure home page so that it sends this data to the handlebars template.
4. Finally create a placeholder in the template to display this information.

## Advanced Topics

@@ -94,9 +94,25 @@ You have seen how to use Puppeteer to write a test to see that we can add a sing

### 1.2 Snapshots

The acceptance tests in the previous section interact with the [Document Model Object](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction), they have no way to understand the appearance of the page as this is influenced by the [Cascading Stylesheets](https://developer.mozilla.org/en-US/docs/Web/CSS) that are being applied to the page. Clearly it is important that our acceptance tests should also check that the page renders as it should and [Snapshot Testing](https://jestjs.io/docs/en/snapshot-testing) enables us to achieve this.

In essence we identify the important screens in the app (the ones we wish to monitor), get the layout the way we expect it to be and capture a screenshot of the page which is stored in a `__image_snapshots_/` directory then used as a reference whenever the test suite is subsequently run. Since you have already triggered an inital test run you should be able to locate this directory and examine the image file it contains.

The snapshot is captured using the following two lines of code:

```javascript
const image = await page.screenshot()
expect(image).toMatchImageSnapshot()
```

Sometimes you will need to modify the page layout however this will cause the snapshot test to fail since there will no longer be a match. The solution is to run the test suite but add a `-u` flag. This will update the snapshot files.

#### 1.2.1 Test Your Understanding

xxx
1. Modify the stylesheet and make the top level heading larger (perhaps `28pt`).
2. Now re-run the acceptance test suite, this will fail because the layout is now different.
3. Edit the `test.sh` script, adding the `-u` flag, save this change and rerun the test suite.
4. Remove the `-u` flag and rerun the tests to check these are still passing.

## 2 Profiling

@@ -129,7 +145,9 @@ There are many more settings you can experiment with to help understand the perf

### 2.1 Test Your Understanding

xxx
1. Look at the graphs and figures that are produced for the todo code profiling and identify the different stages and different time taken for each. Are there any stages where you feel the timing is significantly long.
2. Run the todo code three times and compare the time taken for each of stages involved. Do they different? Is there any reason why this might be the case?
3. Using the har analyzer how long does it take for the server to respond.

### 2.2 Flame Graphs

@@ -174,7 +192,8 @@ Hovering over a box will reveal more information.

### 2.2.1 Test Your Understanding

xxx
1. Looking at the flame graph which process is consuming the most CPU cycles? Is it possible to optimise this process to reduce the cycles?
2. Looking at the flame graph which process what is the hot path for the code.

## 3 Cucumber

Binary file not shown.
@@ -1,6 +1,25 @@
#!/usr/bin/env bash

# force the script to exit on first fail
set -e

# create any directories needed by the test script
mkdir -p screenshots

# delete any local databases (if you are using them)
rm -rf *.db

# install packages if none found
# [ ! -d "node_modules" ] && echo "INSTALLING MODULES" && npm install

# start the web server in background mode
node index.js&

# run the test suite in background mode
node_modules/.bin/cucumber-js ./features -r ./steps &
sleep 2

# wait for the tests to complete
sleep 5

# kill the web server
pkill node
@@ -0,0 +1,23 @@
Feature: Adding Items
The user should be able to add items to the list.

Scenario: add single item
Given The browser is open on the home page
When I enter "bread" in the "item" field
When I enter "42" in the "qty" field
When I click on the submit button
Then the heading should be "ToDo List"
Then the list should contain "1" rows
Then the list should contain a single entry for "bread"
Then the item should be "bread"
Then the "bread" quantity should be "42"

Scenario: add multiple items
Given The browser is open on the home page
When I enter "butter" in the "item" field
When I enter "24" in the "qty" field
When I click on the submit button
Then the heading should be "ToDo List"
Then the list should contain "2" rows
Then the list should contain a single entry for "bread"
Then the item should be "bread"

This file was deleted.

@@ -41,6 +41,10 @@ When('I click on the submit button', async() => {
await page.click('#submit')
})

Then('take a screenshot called {string}', async filename => {
await page.screenshot({ path: `screenshots/${filename}.png` })
})

Then('the heading should be {string}', async heading => {
const text = await page.evaluate( () => {
const dom = document.querySelector('h1')
@@ -49,7 +53,8 @@ Then('the heading should be {string}', async heading => {
assert.equal(heading, text)
})

Then('the list should contain {string} row', async rowCount => {
Then('the list should contain {string} rows', async rowCount => {
rowCount = Number(rowCount)
const items = await page.evaluate( () => {
const dom = document.querySelectorAll('table tr td:first-child')
const arr = Array.from(dom)
@@ -66,3 +71,22 @@ Then('the item should be {string}', async item => {
})
assert.equal(item, items[0])
})

Then('the list should contain a single entry for {string}', async item => {
const items = await page.evaluate( () => {
const dom = document.querySelectorAll('table tr td:first-child')
const arr = Array.from(dom).map(td => td.innerText)
return arr
})
const count = items.reduce( (acc, val) => (val === item ? acc += 1 : acc), 0)
assert.equal(count, 1)
})

Then('the {string} quantity should be {string}', async(item, qty) => {
const items = await page.evaluate( () => {
const dom = document.querySelectorAll('table tr')
// const arr = Array.from(dom)
return dom
})
assert.equal(2, 2)
})
@@ -68,20 +68,26 @@ describe('todo list', () => {
const arr = Array.from(dom)
return arr.map(td => td.innerText)
})
const numRows = items.reduce( (acc, val) => (val === 'bread' ? acc += 1 : acc), 0)

// this is a more concise way to achieve the same result...
console.log(`numRows: ${numRows}`)

// returns the number of rows containing the given string
const items2 = await page.evaluate(() => Array
.from(document.querySelectorAll('table tr td:first-child'))
.map(td => td.innerHTML) )
.map(td => td.innerHTML)
.reduce( (acc, val) => (val === 'bread' ? acc += 1 : acc), 0)
)

expect(items.length).toBe(1)
expect(items[0]).toBe('bread')
expect(numRows).toBe(1)

// grab a screenshot
const image = await page.screenshot()
// compare to the screenshot from the previous test run
expect(image).toMatchImageSnapshot()
// stop logging to the trace file
// stop logging to the trace files
await page.tracing.stop()
await har.stop()
done()
@@ -4,11 +4,13 @@
"description": "",
"main": "index.js",
"scripts": {
"start-server": "node index.js",
"linter": "node_modules/.bin/eslint .",
"test": "node_modules/.bin/jest --coverage --runInBand --detectOpenHandles",
"test": "./node_modules/.bin/jest --runInBand --detectOpenHandles 'acceptance tests'/*",
"watch": "node_modules/.bin/jest --coverage --watchAll",
"acceptance": "./test.sh",
"profiler": "./node_modules/.bin/0x -o index.js"
"profiler": "./node_modules/.bin/0x -o index.js",
"ci": "./node_modules/.bin/start-server-and-test start-server http://localhost:8080 test"
},
"author": "",
"license": "ISC",
@@ -30,6 +32,7 @@
"jest-puppeteer": "^4.3.0",
"puppeteer": "^1.20.0",
"puppeteer-har": "^1.1.1",
"start-server-and-test": "^1.10.6",
"supertest": "^4.0.2"
},
"jest": {
@@ -3,6 +3,10 @@ body {
font-family: Arial, Helvetica, sans-serif;
}

h1 {
font-size: 18pt
}

.msg {
border: 1px solid red;
font-weight: bold;
@@ -1,7 +1,13 @@
#!/usr/bin/env bash

mkdir screenshots
mkdir trace
set -e
echo hello
mkdir -p screenshots
mkdir -p trace
rm -rf *.db
# [ ! -d "node_modules" ] && echo "INSTALLING MODULES" && npm install
node index.js&
node_modules/.bin/jest --runInBand --detectOpenHandles acceptance\ tests/*
kill %1
read -p "Press enter to continue"

@@ -0,0 +1,19 @@

'use strict'

module.exports = class Messages {
constructor() {
console.log('constructor')
this.messages = []
return this
}
extractData() {

}
add() {

}
getAll() {

}
}
@@ -7,6 +7,7 @@ const messages = ( function() {
let msgs = []
return {
extractData: payloadString => {
console.log('extracData')
return payloadString
},
add: data => {

0 comments on commit d1b3862

Please sign in to comment.