Skip to content

Commit

Permalink
Merge pull request #622 from alphagov/fix-tests-colliding-with-dev-se…
Browse files Browse the repository at this point in the history
…rver

Listen for development server on different port for tests
  • Loading branch information
NickColley authored Mar 28, 2018
2 parents ffc2ca6 + 145c7c6 commit a4860b9
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 145 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ New features:
Internal:
- Update check script for new components and tweak docs
(PR [#589](https://github.com/alphagov/govuk-frontend/pull/589))
- Listen for development server on different port for tests
(PR [#622](https://github.com/alphagov/govuk-frontend/pull/622))

- Fix date-input default example
(PR [#623](https://github.com/alphagov/govuk-frontend/pull/623))
Expand Down
19 changes: 11 additions & 8 deletions app/__tests__/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,60 @@ const cheerio = require('cheerio')

const lib = require('../../lib/file-helper')

const configPaths = require('../../config/paths.json')
const PORT = configPaths.ports.test

const requestParamsHomepage = {
url: 'http://localhost:3000/',
url: `http://localhost:${PORT}/`,
headers: {
'Content-Type': 'text/plain'
}
}

const requestParamsExampleAllComponents = {
url: 'http://localhost:3000/examples/all-components',
url: `http://localhost:${PORT}/examples/all-components`,
headers: {
'Content-Type': 'text/plain'
}
}

const requestParamsExampleFormAlignment = {
url: 'http://localhost:3000/examples/form-alignment',
url: `http://localhost:${PORT}/examples/form-alignment`,
headers: {
'Content-Type': 'text/plain'
}
}

const requestParamsExampleFormElements = {
url: 'http://localhost:3000/examples/form-elements',
url: `http://localhost:${PORT}/examples/form-elements`,
headers: {
'Content-Type': 'text/plain'
}
}

const requestParamsExampleGrid = {
url: 'http://localhost:3000/examples/grid',
url: `http://localhost:${PORT}/examples/grid`,
headers: {
'Content-Type': 'text/plain'
}
}

const requestParamsExampleLinks = {
url: 'http://localhost:3000/examples/links',
url: `http://localhost:${PORT}/examples/links`,
headers: {
'Content-Type': 'text/plain'
}
}

const requestParamsExampleProseScope = {
url: 'http://localhost:3000/examples/prose-scope',
url: `http://localhost:${PORT}/examples/prose-scope`,
headers: {
'Content-Type': 'text/plain'
}
}

const requestParamsExampleTypography = {
url: 'http://localhost:3000/examples/typography',
url: `http://localhost:${PORT}/examples/typography`,
headers: {
'Content-Type': 'text/plain'
}
Expand Down
246 changes: 123 additions & 123 deletions app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const app = express()
const nunjucks = require('nunjucks')
const fs = require('fs')
const path = require('path')
const port = (process.env.PORT || 3000)
const yaml = require('js-yaml')

const helperFunctions = require('../lib/helper-functions')
Expand All @@ -18,136 +17,137 @@ const appViews = [
configPaths.src
]

// Configure nunjucks
let env = nunjucks.configure(appViews, {
autoescape: true, // output with dangerous characters are escaped automatically
express: app, // the express app that nunjucks should install to
noCache: true, // never use a cache and recompile templates each time
trimBlocks: true, // automatically remove trailing newlines from a block/tag
lstripBlocks: true, // automatically remove leading whitespace from a block/tag
watch: true // reload templates when they are changed. needs chokidar dependency to be installed
})

// make the function available as a filter for all templates
env.addFilter('componentNameToMacroName', helperFunctions.componentNameToMacroName)

// Set view engine
app.set('view engine', 'njk')

// Set up middleware to serve static assets
app.use('/public', express.static(configPaths.public))

// serve html5-shiv from node modules
app.use('/vendor/html5-shiv/', express.static('node_modules/html5shiv/dist/'))
app.use('/icons', express.static(path.join(configPaths.src, 'icons')))

const server = app.listen(port, () => {
console.log('Listening on port ' + port + ' url: http://localhost:' + port)
})

// Define routes

// Index page - render the component list template
app.get('/', function (req, res) {
Promise.all([
directoryToObject(path.resolve(configPaths.src)),
directoryToObject(path.resolve(configPaths.examples))
]).then(result => {
const [components, examples] = result

// filter out globals, all and icons package
const {globals, all, icons, ...filteredComponents} = components

res.render('index', {
componentsDirectory: filteredComponents,
examplesDirectory: examples
module.exports = (options) => {
const nunjucksOptions = options ? options.nunjucks : {}

// Configure nunjucks
let env = nunjucks.configure(appViews, {
autoescape: true, // output with dangerous characters are escaped automatically
express: app, // the express app that nunjucks should install to
noCache: true, // never use a cache and recompile templates each time
trimBlocks: true, // automatically remove trailing newlines from a block/tag
lstripBlocks: true, // automatically remove leading whitespace from a block/tag
watch: true, // reload templates when they are changed. needs chokidar dependency to be installed
...nunjucksOptions // merge any additional options and overwrite defaults above.
})

// make the function available as a filter for all templates
env.addFilter('componentNameToMacroName', helperFunctions.componentNameToMacroName)

// Set view engine
app.set('view engine', 'njk')

// Set up middleware to serve static assets
app.use('/public', express.static(configPaths.public))

// serve html5-shiv from node modules
app.use('/vendor/html5-shiv/', express.static('node_modules/html5shiv/dist/'))
app.use('/icons', express.static(path.join(configPaths.src, 'icons')))

// Define routes

// Index page - render the component list template
app.get('/', function (req, res) {
Promise.all([
directoryToObject(path.resolve(configPaths.src)),
directoryToObject(path.resolve(configPaths.examples))
]).then(result => {
const [components, examples] = result

// filter out globals, all and icons package
const {globals, all, icons, ...filteredComponents} = components

res.render('index', {
componentsDirectory: filteredComponents,
examplesDirectory: examples
})
})
})

// Whenever the route includes a :component parameter, read the component data
// from its YAML file
app.param('component', function (req, res, next, componentName) {
let yamlPath = configPaths.src + `${componentName}/${componentName}.yaml`

try {
res.locals.componentData = yaml.safeLoad(
fs.readFileSync(yamlPath, 'utf8'), { json: true }
)
next()
} catch (e) {
next(new Error('failed to load component YAML file'))
}
})

// Component 'README' page
app.get('/components/:component', function (req, res, next) {
// make variables available to nunjucks template
res.locals.componentPath = req.params.component

res.render(`${req.params.component}/index`, function (error, html) {
if (error) {
next(error)
} else {
res.send(html)
}
})
})
})

// Whenever the route includes a :component parameter, read the component data
// from its YAML file
app.param('component', function (req, res, next, componentName) {
let yamlPath = configPaths.src + `${componentName}/${componentName}.yaml`
// Component example preview
app.get('/components/:component/:example*?/preview', function (req, res, next) {
// Find the data for the specified example (or the default example)
let componentName = req.params.component
let requestedExampleName = req.params.example || 'default'

try {
res.locals.componentData = yaml.safeLoad(
fs.readFileSync(yamlPath, 'utf8'), { json: true }
let exampleConfig = res.locals.componentData.examples.find(
example => example.name === requestedExampleName
)
next()
} catch (e) {
next(new Error('failed to load component YAML file'))
}
})

// Component 'README' page
app.get('/components/:component', function (req, res, next) {
// make variables available to nunjucks template
res.locals.componentPath = req.params.component

res.render(`${req.params.component}/index`, function (error, html) {
if (error) {
next(error)
} else {
res.send(html)

if (!exampleConfig) {
next()
}
})
})

// Component example preview
app.get('/components/:component/:example*?/preview', function (req, res, next) {
// Find the data for the specified example (or the default example)
let componentName = req.params.component
let requestedExampleName = req.params.example || 'default'
// Construct and evaluate the component with the data for this example
let macroName = helperFunctions.componentNameToMacroName(componentName)
let macroParameters = JSON.stringify(exampleConfig.data, null, '\t')

let exampleConfig = res.locals.componentData.examples.find(
example => example.name === requestedExampleName
)
res.locals.componentView = env.renderString(
`{% from '${componentName}/macro.njk' import ${macroName} %}
{{ ${macroName}(${macroParameters}) }}`
)

if (!exampleConfig) {
next()
}

// Construct and evaluate the component with the data for this example
let macroName = helperFunctions.componentNameToMacroName(componentName)
let macroParameters = JSON.stringify(exampleConfig.data, null, '\t')

res.locals.componentView = env.renderString(
`{% from '${componentName}/macro.njk' import ${macroName} %}
{{ ${macroName}(${macroParameters}) }}`
)

let bodyClasses = ''
if (req.query.iframe) {
bodyClasses = 'app-iframe-in-component-preview'
}

res.render('component-preview', { bodyClasses })
})

// Example view
app.get('/examples/:example', function (req, res, next) {
res.render(`${req.params.example}/index`, function (error, html) {
if (error) {
next(error)
} else {
res.send(html)
let bodyClasses = ''
if (req.query.iframe) {
bodyClasses = 'app-iframe-in-component-preview'
}

res.render('component-preview', { bodyClasses })
})
})

// Disallow search index indexing
app.use(function (req, res, next) {
// none - Equivalent to noindex, nofollow
// noindex - Do not show this page in search results and do not show a "Cached" link in search results.
// nofollow - Do not follow the links on this page
res.setHeader('X-Robots-Tag', 'none')
next()
})

app.get('/robots.txt', function (req, res) {
res.type('text/plain')
res.send('User-agent: *\nDisallow: /')
})

module.exports = server

// Example view
app.get('/examples/:example', function (req, res, next) {
res.render(`${req.params.example}/index`, function (error, html) {
if (error) {
next(error)
} else {
res.send(html)
}
})
})

// Disallow search index indexing
app.use(function (req, res, next) {
// none - Equivalent to noindex, nofollow
// noindex - Do not show this page in search results and do not show a "Cached" link in search results.
// nofollow - Do not follow the links on this page
res.setHeader('X-Robots-Tag', 'none')
next()
})

app.get('/robots.txt', function (req, res) {
res.type('text/plain')
res.send('User-agent: *\nDisallow: /')
})

return app
}
8 changes: 8 additions & 0 deletions app/start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const configPaths = require('../config/paths.json')
const PORT = process.env.PORT || configPaths.ports.app

const app = require('./app.js')()

app.listen(PORT, () => {
console.log('Server started at http://localhost:' + PORT)
})
6 changes: 5 additions & 1 deletion config/paths.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@
"dist": "dist/",
"packages": "packages/",
"public": "public/",
"src": "src/"
"src": "src/",
"ports": {
"app": 3000,
"test": 8888
}
}
2 changes: 1 addition & 1 deletion docs/development-and-publish-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This application used a number of a number of NPM scripts that run the applicati

## Express app only

To simply run the Express app without gulp tasks being triggered, simply run `node app.js`.
To simply run the Express app without gulp tasks being triggered, simply run `node app/start.js`.

## NPM script aliases

Expand Down
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ gulp.task('dev', cb => {

gulp.task('serve', ['watch'], () => {
return nodemon({
script: 'app/app.js'
script: 'app/start.js'
})
})

Expand Down
Loading

0 comments on commit a4860b9

Please sign in to comment.