-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(roc-package-web-app): Upgrade package to use Koa@2
BREAKING CHANGE: Middlewares need to be defined as an async funcions instead of generators.
- Loading branch information
Showing
8 changed files
with
204 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
packages/roc-package-web-app/src/app/defaultKoaMiddlewares.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import koaErrorMock from 'koa-error'; | ||
import koaHelmetMock from 'koa-helmet'; | ||
import koaEtagMock from 'koa-etag'; | ||
import koaLoggerMock from 'koa-logger'; | ||
import koaCompressMock from 'koa-compress'; | ||
import koaFaviconMock from 'koa-favicon'; | ||
|
||
import accessLogMock from './middlewares/accesslog'; | ||
import getKoaMiddlewares from './defaultKoaMiddlewares'; | ||
|
||
jest.mock('koa-error', () => jest.fn(() => function koaError() {})); | ||
jest.mock('koa-helmet', () => jest.fn(() => function koaHelmet() {})); | ||
jest.mock('koa-etag', () => jest.fn(() => function koaEtag() {})); | ||
jest.mock('koa-logger', () => jest.fn(() => function koaLogger() {})); | ||
jest.mock('koa-compress', () => jest.fn(() => function koaCompress() {})); | ||
jest.mock('koa-favicon', () => jest.fn(() => function koaFavicon() {})); | ||
jest.mock('./middlewares/accesslog', () => jest.fn(() => function accessLog() {})); | ||
|
||
|
||
describe('defaultKoaMiddleware', () => { | ||
afterEach(() => { | ||
koaErrorMock.mockClear(); | ||
koaHelmetMock.mockClear(); | ||
koaEtagMock.mockClear(); | ||
koaLoggerMock.mockClear(); | ||
koaCompressMock.mockClear(); | ||
}); | ||
|
||
it('should return default array of middlewares even when all options are falsy', () => { | ||
const middlewares = getKoaMiddlewares({}, { dev: false, dist: false }); | ||
|
||
expect(middlewares.length).toEqual(3); | ||
expect(middlewares.map(f => f.name)).toEqual(['koaHelmet', 'koaEtag', 'koaLogger']); | ||
expect(koaHelmetMock).toHaveBeenCalled(); | ||
expect(koaEtagMock).toHaveBeenCalled(); | ||
expect(koaLoggerMock).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should include koa-error when dev is set to true', () => { | ||
const middlewares = getKoaMiddlewares({}, { dev: true, dist: false }); | ||
|
||
expect(middlewares.length).toEqual(4); | ||
expect(middlewares.map(f => f.name)).toEqual(['koaError', 'koaHelmet', 'koaEtag', 'koaLogger']); | ||
expect(koaErrorMock).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should include koa-compress and accessLog instead of koa-logger when dist is set to true', () => { | ||
const middlewares = getKoaMiddlewares({}, { dev: false, dist: true }); | ||
|
||
expect(middlewares.length).toEqual(4); | ||
expect(middlewares.map(f => f.name)).toEqual(['koaHelmet', 'koaEtag', 'koaCompress', 'accessLog']); | ||
expect(koaCompressMock).toHaveBeenCalled(); | ||
expect(accessLogMock).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should include koa-favicon when favicon is specified in config', () => { | ||
const config = { favicon: './pathToFavicon.ico' }; | ||
const middlewares = getKoaMiddlewares(config, { dev: false, dist: false }); | ||
|
||
expect(middlewares.length).toEqual(4); | ||
expect(middlewares.map(f => f.name)).toEqual(['koaHelmet', 'koaEtag', 'koaFavicon', 'koaLogger']); | ||
expect(koaFaviconMock).toHaveBeenCalledWith(config.favicon); | ||
}); | ||
}); |
18 changes: 18 additions & 0 deletions
18
packages/roc-package-web-app/src/app/middlewares/accesslog.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import util from 'util'; | ||
|
||
import moment from 'moment'; | ||
|
||
/* | ||
Implementation based on koa-accesslog | ||
*/ | ||
export default function accesslog(stream = process.stdout) { | ||
return async function(ctx, next) { | ||
await next(); | ||
|
||
const format = '%s - - [%s] "%s %s HTTP/1.X" %d %s\n'; | ||
const length = ctx.length ? ctx.length.toString() : '-'; | ||
const date = moment().format('D/MMM/YYYY:HH:mm:ss ZZ'); | ||
|
||
stream.write(util.format(format, ctx.ip, date, ctx.method, ctx.path, ctx.status, length)); | ||
}; | ||
} |
52 changes: 52 additions & 0 deletions
52
packages/roc-package-web-app/src/app/middlewares/accesslog.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { format as formatMock } from 'util'; | ||
|
||
import createAccessLogMiddleware from './accesslog'; | ||
|
||
jest.mock('util', () => ({ | ||
format: jest.fn(), | ||
})); | ||
|
||
describe('middlewares/accesslog', () => { | ||
const next = async () => {}; | ||
|
||
afterEach(() => { | ||
formatMock.mockClear(); | ||
}); | ||
|
||
it('should call write method of passed stream', async () => { | ||
const streamWriteMock = jest.fn(); | ||
const streamMock = { | ||
write: streamWriteMock, | ||
}; | ||
const accessLogMiddleware = createAccessLogMiddleware(streamMock); | ||
await accessLogMiddleware({}, next); | ||
|
||
expect(streamWriteMock).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should call util.format with certain ctx properties', async () => { | ||
const streamWriteMock = jest.fn(); | ||
const streamMock = { | ||
write: streamWriteMock, | ||
}; | ||
const accessLogMiddleware = createAccessLogMiddleware(streamMock); | ||
const ctx = { | ||
length: 42, | ||
ip: '127.0.0.1', | ||
method: 'GET', | ||
path: '/app', | ||
status: 200, | ||
}; | ||
await accessLogMiddleware(ctx, next); | ||
|
||
expect(formatMock).toHaveBeenCalledWith( | ||
'%s - - [%s] \"%s %s HTTP/1.X\" %d %s\n', | ||
ctx.ip, | ||
expect.anything(), | ||
ctx.method, | ||
ctx.path, | ||
ctx.status, | ||
ctx.length.toString(), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
packages/roc-package-web-app/src/app/middlewares/basepathSupport.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import createBasepathSupportMiddleware from './basepathSupport'; | ||
|
||
describe('middlewares/basepathSupport', () => { | ||
const next = async () => {}; | ||
|
||
it('should throw an error when basepath does not start with "/"', () => { | ||
const veryWrongBasepath = 'veryWrongBasepath'; | ||
expect(() => { | ||
createBasepathSupportMiddleware(veryWrongBasepath); | ||
}).toThrowError(`The basepath must start with "/", was ${veryWrongBasepath}`); | ||
}); | ||
|
||
it('should set new ctx.path with eased basepath', async () => { | ||
const basepath = '/basepath'; | ||
const ctx = { path: '/basepath/application' }; | ||
const basepathMiddleware = createBasepathSupportMiddleware(basepath); | ||
await basepathMiddleware(ctx, next); | ||
|
||
expect(ctx.path).toEqual('/application'); | ||
}); | ||
|
||
it('should set new ctx.path to "/" if its the same as basepath', async () => { | ||
const basepath = '/basepath'; | ||
const ctx = { path: '/basepath' }; | ||
const basepathMiddleware = createBasepathSupportMiddleware(basepath); | ||
await basepathMiddleware(ctx, next); | ||
|
||
expect(ctx.path).toEqual('/'); | ||
}); | ||
|
||
it('should return an undefined when basepath is different than "/" and no new path is set', async () => { | ||
const basepath = '/'; | ||
const ctx = { path: '/' }; | ||
const basepathMiddleware = createBasepathSupportMiddleware(basepath); | ||
const result = await basepathMiddleware(ctx, next); | ||
|
||
expect(result).toEqual(undefined); | ||
}); | ||
}); |