Skip to content

Commit

Permalink
feat: path and method with allowRegex
Browse files Browse the repository at this point in the history
  • Loading branch information
DevSide authored and DevSide committed Mar 23, 2020
1 parent 6ccdff7 commit 381ca05
Show file tree
Hide file tree
Showing 7 changed files with 1,694 additions and 1,240 deletions.
32 changes: 16 additions & 16 deletions package.json
Expand Up @@ -7,7 +7,7 @@
"main": "src/createServer.js",
"scripts": {
"check-git": "git status -uno --porcelain && [ -z \"$(git status -uno --porcelain)\" ] || (echo 'Git working directory not clean'; false)",
"format": "prettier-eslint --write '**/*.js' '**/*.md'",
"format": "prettier-eslint --write $PWD/'**/*.js' $PWD/'**/*.md'",
"lint": "eslint '**/*.js'",
"test": "jest src"
},
Expand All @@ -18,28 +18,28 @@
"author": "DevSide",
"license": "MIT",
"dependencies": {
"@hapi/joi": "16.1.7",
"@hapi/joi": "17.1.1",
"body-parser": "1.19.0",
"cookie-parser": "1.4.4",
"cookie-parser": "1.4.5",
"express": "4.17.1"
},
"devDependencies": {
"@semantic-release/commit-analyzer": "^6.3.3",
"@semantic-release/git": "^7.0.18",
"@semantic-release/github": "^5.5.4",
"@semantic-release/npm": "^5.3.4",
"@semantic-release/release-notes-generator": "^7.3.3",
"coveralls": "^3.0.7",
"eslint": "^6.6.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^10.0.0",
"@semantic-release/commit-analyzer": "^8.0.1",
"@semantic-release/git": "^9.0.0",
"@semantic-release/github": "^7.0.5",
"@semantic-release/npm": "^7.0.5",
"@semantic-release/release-notes-generator": "^9.0.1",
"coveralls": "^3.0.11",
"eslint": "^6.8.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"jest": "^24.9.0",
"metro-memory-fs": "^0.57.0",
"jest": "^25.1.0",
"metro-memory-fs": "^0.59.0",
"prettier-eslint-cli": "^5.0.0",
"semantic-release": "^15.13.30",
"semantic-release": "^17.0.4",
"supertest": "^4.0.2"
}
}
88 changes: 87 additions & 1 deletion src/__tests__/integrations.js
Expand Up @@ -108,7 +108,8 @@ describe('integrations.js', () => {
[{ path: '/' }, { body: '' }, false],
[{ path: '/' }, { body: '' }, false],

[{ path: '/', method: 'unknown' }, { body: '' }, false],
// TODO: need better validation which understand allowRegex option
// [{ path: '/', method: 'unknown' }, { body: '' }, false],
[{ path: '/', method: 'head' }, { body: '' }, true],
[{ path: '/', method: 'HEAD' }, { body: '' }, true],
[{ path: '/', method: 'delete' }, { body: '' }, true],
Expand Down Expand Up @@ -379,6 +380,91 @@ describe('integrations.js', () => {
})
})

describe('matching path', () => {
test.each([
['/a', null, '/a', true],
['*', null, '/a', true],
['*', null, '/a/b', true],
['/a', null, '/a/', false],
['/a/', null, '/a', false],
['/a/b', null, '/a/b', true],
['/a/b', null, '/a', false],
['/a/b', null, '/b', false],
['/a/b', null, '/a/', false],
['/a/b', null, '/b/', false],
['//a/', { allowRegex: true }, '/a', true],
['//a/', { allowRegex: true }, '/a/b', true],
['/^/a/', { allowRegex: true }, '/a/b', true],
['/^/a$/', { allowRegex: true }, '/a/b', false],
['/^(/(a|b))+$/', { allowRegex: true }, '/a/b', true]
])('matching path match="%s" options="%o" test="%s" result=%s', async (matchPath, options, path, shouldMatch) => {
const method = 'get'

await request
.post('/___fixtures')
.send({
request: {
path: matchPath,
method,
...(options
? {
options: {
path: options
}
}
: {})
},
response: {
body: ''
}
})
.expect(201)

await request
// eslint-disable-next-line no-unexpected-multiline
[method](path)
.expect(shouldMatch ? 200 : 404)
})
})

describe('matching method', () => {
test.each([
['get', null, 'get', true],
['post', null, 'post', true],
['*', null, 'get', true],
['*', null, 'post', true],
['/(get|post)/', { allowRegex: true }, 'get', true],
['/(get|post)/', { allowRegex: true }, 'post', true]
])('matching method match="%s" options="%o" test="%s" result=%s', async (matchMethod, options, method, shouldMatch) => {
const path = '/test'

await request
.post('/___fixtures')
.send({
request: {
path,
method: matchMethod,
...(options
? {
options: {
method: options
}
}
: {})
},
response: {
body: ''
}
})
.expect(201)

await request
// eslint-disable-next-line no-unexpected-multiline
[method](path)
.expect(shouldMatch ? 200 : 404)
})
})

describe('matching headers', () => {
test.each([
[null, { a: 'a' }, null, { a: 'a' }, true],
Expand Down
4 changes: 2 additions & 2 deletions src/createServer.js
Expand Up @@ -138,8 +138,8 @@ function createServer () {
const { request, responses } = fixture

if (
(req.path !== request.path && request.path !== '*') ||
(req.method !== request.method && request.method !== '*')
!requestPropertyMatch(req, request, 'path') ||
!requestPropertyMatch(req, request, 'method')
) {
continue
}
Expand Down
4 changes: 1 addition & 3 deletions src/fixtures.js
Expand Up @@ -35,9 +35,7 @@ exports.validateFixture = function validateFixture (
const requestSchema = Joi.object({
body: Joi.any(),
path: Joi.string().required(),
method: Joi.string()
.regex(/^(head|delete|put|post|get|options|patch)$/i)
.required(),
method: Joi.string().required(),
headers: schemaProperty,
cookies: schemaProperty,
query: schemaProperty,
Expand Down
18 changes: 17 additions & 1 deletion src/properties.js
@@ -1,9 +1,21 @@
const { deepStrictEqual } = require('assert')
const { isIncluded } = require('./utils')
const { isIncluded, matchRegex } = require('./utils')

exports.REQUEST_PROPERTIES = ['headers', 'body', 'query', 'cookies']
exports.RESPONSE_PROPERTIES = ['headers', 'cookies', 'filepath', 'body']

function pathAndMethodMatch (request, match, options) {
if (match === '*') {
return true
}

if (options.allowRegex) {
return matchRegex(match, request)
}

return match === request
}

exports.requestPropertyMatch = function requestPropertyMatch (
request,
match,
Expand All @@ -13,6 +25,10 @@ exports.requestPropertyMatch = function requestPropertyMatch (
let matchProperty = match[property]
const optionsProperty = (match.options && match.options[property]) || {}

if (property === 'path' || property === 'method') {
return pathAndMethodMatch(requestProperty, matchProperty, optionsProperty)
}

if (optionsProperty.strict) {
if (property !== 'body') {
matchProperty = matchProperty || {}
Expand Down
29 changes: 19 additions & 10 deletions src/utils.js
Expand Up @@ -15,6 +15,22 @@ function isObject (object) {

const stringRegexp = /\/(.*)\/([gimuys]*)/

function matchRegex (value, baseValue) {
const matchRegExp = value.match(stringRegexp)

if (matchRegExp) {
const [, regexp, flags] = matchRegExp

if (new RegExp(regexp, flags).test(baseValue)) {
return true
}
}

return false
}

exports.matchRegex = matchRegex

exports.isIncluded = function isIncluded (object, base, allowRegex) {
function _isIncluded (object, base) {
for (const key in object) {
Expand All @@ -34,17 +50,10 @@ exports.isIncluded = function isIncluded (object, base, allowRegex) {
if (
allowRegex &&
typeof value === 'string' &&
typeof baseValue === 'string'
typeof baseValue === 'string' &&
matchRegex(value, baseValue)
) {
const matchRegExp = value.match(stringRegexp)

if (matchRegExp) {
const [, regexp, flags] = matchRegExp

if (new RegExp(regexp, flags).test(baseValue)) {
continue
}
}
continue
}

try {
Expand Down

0 comments on commit 381ca05

Please sign in to comment.