forked from pact-foundation/pact-js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor!: allow hooks to run only once per interaction
Breaking change: Current behaviour will run the beforeEach and afterEach hooks multiple times if there are several provider states defined in an interaction. This change will ensure each of those hooks is run only once, regardless of how many provider states are defined in an interaction. Fixes pact-foundation#1068
- Loading branch information
Showing
3 changed files
with
108 additions
and
26 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { expect } from 'chai'; | ||
import { stub } from 'sinon'; | ||
|
||
import { registerBeforeHook, registerAfterHook } from './hooks'; | ||
|
||
describe('Verifier', () => { | ||
describe('#registerBeforeHook', () => { | ||
describe('when the state setup routine is called multiple times before the next teardown', () => { | ||
it('it executes the afterEach hook only once', (done) => { | ||
const mockHook = stub().resolves(); | ||
|
||
const requestHandler = registerBeforeHook(mockHook); | ||
|
||
const mockRequest = { | ||
app: {}, | ||
body: { | ||
action: 'setup', | ||
}, | ||
}; | ||
|
||
requestHandler(mockRequest as any, null as any, () => { | ||
requestHandler(mockRequest as any, null as any, () => { | ||
try { | ||
expect(mockHook).to.be.calledOnce; | ||
done(); | ||
} catch (err) { | ||
done(err); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('#registerAfterHook', () => { | ||
describe('when the state teardown routine is called multiple times before the next setup', () => { | ||
it('it executes the afterEach hook only once', (done) => { | ||
const mockHook = stub().resolves(); | ||
|
||
const requestHandler = registerAfterHook(mockHook); | ||
|
||
const mockRequest = { | ||
app: {}, | ||
body: { | ||
action: 'teardown', | ||
}, | ||
}; | ||
|
||
requestHandler(mockRequest as any, null as any, () => { | ||
requestHandler(mockRequest as any, null as any, () => { | ||
try { | ||
expect(mockHook).to.be.calledOnce; | ||
done(); | ||
} catch (err) { | ||
done(err); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
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 |
---|---|---|
@@ -1,50 +1,62 @@ | ||
import express from 'express'; | ||
/* eslint-disable no-param-reassign */ | ||
import { RequestHandler, Application, Request } from 'express'; | ||
|
||
import logger from '../../../common/logger'; | ||
import { ProxyOptions } from './types'; | ||
import { Hook } from './types'; | ||
|
||
export const registerBeforeHook = ( | ||
app: express.Express, | ||
config: ProxyOptions, | ||
stateSetupPath: string | ||
): void => { | ||
if (config.beforeEach) logger.trace("registered 'beforeEach' hook"); | ||
app.use(async (req, res, next) => { | ||
if (req.path === stateSetupPath && config.beforeEach) { | ||
type ApplicationWithHookMetadata = Application & { | ||
beforeEachExecuted?: boolean; | ||
afterEachExecuted?: boolean; | ||
}; | ||
|
||
export const registerBeforeHook = | ||
(beforeEach: Hook): RequestHandler => | ||
async ( | ||
{ body, app }: Request & { app: ApplicationWithHookMetadata }, | ||
res, | ||
next | ||
) => { | ||
if (body?.action === 'setup' && !app.beforeEachExecuted) { | ||
logger.debug("executing 'beforeEach' hook"); | ||
try { | ||
await config.beforeEach(); | ||
await beforeEach(); | ||
app.beforeEachExecuted = true; | ||
next(); | ||
} catch (e) { | ||
logger.error(`error executing 'beforeEach' hook: ${e.message}`); | ||
logger.debug(`Stack trace was: ${e.stack}`); | ||
next(new Error(`error executing 'beforeEach' hook: ${e.message}`)); | ||
} | ||
} else if (body?.action === 'teardown') { | ||
app.beforeEachExecuted = false; | ||
next(); | ||
} else { | ||
next(); | ||
} | ||
}); | ||
}; | ||
}; | ||
|
||
export const registerAfterHook = ( | ||
app: express.Express, | ||
config: ProxyOptions, | ||
stateSetupPath: string | ||
): void => { | ||
if (config.afterEach) logger.trace("registered 'afterEach' hook"); | ||
app.use(async (req, res, next) => { | ||
if (req.path !== stateSetupPath && config.afterEach) { | ||
export const registerAfterHook = | ||
(afterEach: Hook): RequestHandler => | ||
async ( | ||
{ body, app }: Request & { app: ApplicationWithHookMetadata }, | ||
res, | ||
next | ||
) => { | ||
if (body?.action === 'teardown' && !app.afterEachExecuted) { | ||
logger.debug("executing 'afterEach' hook"); | ||
try { | ||
await config.afterEach(); | ||
await afterEach(); | ||
app.afterEachExecuted = true; | ||
next(); | ||
} catch (e) { | ||
logger.error(`error executing 'afterEach' hook: ${e.message}`); | ||
logger.debug(`Stack trace was: ${e.stack}`); | ||
next(new Error(`error executing 'afterEach' hook: ${e.message}`)); | ||
} | ||
} else if (body?.action === 'setup') { | ||
app.afterEachExecuted = false; | ||
next(); | ||
} else { | ||
next(); | ||
} | ||
}); | ||
}; | ||
}; |
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