Skip to content

Commit

Permalink
fix(net-stubbing): match cy.intercept(url) globs against url+path (#1…
Browse files Browse the repository at this point in the history
…4241)

Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
  • Loading branch information
flotwig and jennifer-shehane authored Dec 21, 2020
1 parent 05b4516 commit 4669b8e
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 68 deletions.
57 changes: 53 additions & 4 deletions packages/driver/cypress/integration/commands/net_stubbing_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe('network stubbing', { retries: 2 }, function () {
type: 'glob',
value: url,
},
matchUrlAgainstPath: true,
},
staticResponse: {
body: 'bar',
Expand All @@ -68,7 +69,7 @@ describe('network stubbing', { retries: 2 }, function () {
}

const expectedRoute = {
options: { url },
options: { url, matchUrlAgainstPath: true },
handler,
}

Expand All @@ -87,12 +88,13 @@ describe('network stubbing', { retries: 2 }, function () {
type: 'glob',
value: url,
},
matchUrlAgainstPath: true,
},
hasInterceptor: true,
}

const expectedRoute = {
options: { url },
options: { url, matchUrlAgainstPath: true },
handler,
}

Expand Down Expand Up @@ -124,7 +126,6 @@ describe('network stubbing', { retries: 2 }, function () {
quuz: /(.*)quux/gi,
},
url: 'http://foo.invalid',
webSocket: false,
}

const expectedEvent = {
Expand Down Expand Up @@ -181,7 +182,6 @@ describe('network stubbing', { retries: 2 }, function () {
type: 'glob',
value: options.url,
},
webSocket: options.webSocket,
},
hasInterceptor: true,
}
Expand Down Expand Up @@ -406,6 +406,32 @@ describe('network stubbing', { retries: 2 }, function () {
},
})
})

it('must set `url` with `matchUrlAgainstPath`', function (done) {
cy.on('fail', function (err) {
expect(err.message).to.include('`matchUrlAgainstPath` requires a `url` to be specified.')

done()
})

cy.intercept({
matchUrlAgainstPath: true,
})
})

it('must not set `path` with `matchUrlAgainstPath`', function (done) {
cy.on('fail', function (err) {
expect(err.message).to.include('`matchUrlAgainstPath` and `path` cannot both be set.')

done()
})

cy.intercept({
matchUrlAgainstPath: true,
path: '*',
url: '*',
})
})
})

context('with invalid handler', function () {
Expand Down Expand Up @@ -1121,6 +1147,29 @@ describe('network stubbing', { retries: 2 }, function () {
})
.wait('@foo')
})

// @see https://github.com/cypress-io/cypress/issues/9379
context('falls back to matching by path if plain string is passed', function () {
it('matches globs against path', function (done) {
cy.intercept('/foo/*', (req) => {
expect(req.url).to.include('/foo/1')
done()
})
.then(() => {
$.ajax({ url: '/foo/1', cache: true })
})
})

it('matches nested globs against path', function (done) {
cy.intercept('/foo/*/bar', (req) => {
expect(req.url).to.match(/\/foo\/1\/bar$/)
done()
})
.then(() => {
$.get({ url: '/foo/1/bar', cache: true })
})
})
})
})

context('with StaticResponse shorthand', function () {
Expand Down
22 changes: 19 additions & 3 deletions packages/driver/src/cy/net-stubbing/add-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function annotateMatcherOptionsTypes (options: RouteMatcherOptions) {
}
})

const noAnnotationRequiredFields = ['https', 'port', 'webSocket']
const noAnnotationRequiredFields: (keyof RouteMatcherOptions)[] = ['https', 'port', 'matchUrlAgainstPath']

_.extend(ret, _.pick(options, noAnnotationRequiredFields))

Expand Down Expand Up @@ -113,8 +113,12 @@ function validateRouteMatcherOptions (routeMatcher: RouteMatcherOptions): { isVa
}
}

if (_.has(routeMatcher, 'https') && !_.isBoolean(routeMatcher.https)) {
return err('`https` must be a boolean.')
const booleanProps = ['https', 'matchUrlAgainstPath']

for (const prop of booleanProps) {
if (_.has(routeMatcher, prop) && !_.isBoolean(routeMatcher[prop])) {
return err(`\`${prop}\` must be a boolean.`)
}
}

if (_.has(routeMatcher, 'port') && !isNumberMatcher(routeMatcher.port)) {
Expand All @@ -133,6 +137,16 @@ function validateRouteMatcherOptions (routeMatcher: RouteMatcherOptions): { isVa
}
}

if (routeMatcher.matchUrlAgainstPath) {
if (!routeMatcher.url) {
return err('`matchUrlAgainstPath` requires a `url` to be specified.')
}

if (routeMatcher.path) {
return err('`matchUrlAgainstPath` and `path` cannot both be set.')
}
}

return { isValid: true }
}

Expand Down Expand Up @@ -280,6 +294,7 @@ export function addCommand (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
handler = arg2

return {
matchUrlAgainstPath: true,
method: matcher,
url,
}
Expand All @@ -288,6 +303,7 @@ export function addCommand (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
if (isStringMatcher(matcher)) {
// url, handler
return {
matchUrlAgainstPath: true,
url: matcher,
}
}
Expand Down
11 changes: 6 additions & 5 deletions packages/net-stubbing/lib/external-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,9 @@ export interface RouteMap { [key: string]: Route }
*/
export type RouteMatcher = StringMatcher | RouteMatcherOptions

export interface RouteMatcherCompatOptions {
response?: string | object
}

export type RouteMatcherOptions = RouteMatcherOptionsGeneric<StringMatcher>

export interface RouteMatcherOptionsGeneric<S> extends RouteMatcherCompatOptions {
export interface RouteMatcherOptionsGeneric<S> {
/**
* Match against the username and password used in HTTP Basic authentication.
*/
Expand All @@ -254,6 +250,11 @@ export interface RouteMatcherOptionsGeneric<S> extends RouteMatcherCompatOptions
* If 'false', only HTTP requests will be matched.
*/
https?: boolean
/**
* If `true`, will match the supplied `url` against incoming `path`s.
* Requires a `url` argument. Cannot be used with a `path` argument.
*/
matchUrlAgainstPath?: boolean
/**
* Match against the request's HTTP method.
* @default '*'
Expand Down
2 changes: 1 addition & 1 deletion packages/net-stubbing/lib/server/driver-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function _restoreMatcherOptionsTypes (options: AnnotatedRouteMatcherOptio
_.set(ret, field, value)
})

const noAnnotationRequiredFields = ['https', 'port', 'webSocket']
const noAnnotationRequiredFields: (keyof AnnotatedRouteMatcherOptions)[] = ['https', 'port', 'matchUrlAgainstPath']

_.extend(ret, _.pick(options, noAnnotationRequiredFields))

Expand Down
26 changes: 18 additions & 8 deletions packages/net-stubbing/lib/server/route-matching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,28 @@ export function _doesRouteMatch (routeMatcher: RouteMatcherOptions, req: Cypress
let matcher = _.get(routeMatcher, field)
let value = _.get(matchable, field, '')

// for convenience, attempt to match `url` against `path`?
const shouldTryMatchingPath = field === 'url' && routeMatcher.matchUrlAgainstPath

const stringMatch = (value: string, matcher: string) => {
return (
value === matcher ||
minimatch(value, matcher, { matchBase: true }) ||
(field === 'url' && (
// substring match
value.includes(matcher) ||
// be nice and match paths that are missing leading slashes
(value[0] === '/' && matcher[0] !== '/' && stringMatch(value, `/${matcher}`))
))
)
}

if (typeof value !== 'string') {
value = String(value)
}

if (matcher.test) {
if (!matcher.test(value)) {
if (!matcher.test(value) && (!shouldTryMatchingPath || !matcher.test(matchable.path))) {
return false
}

Expand All @@ -42,13 +58,7 @@ export function _doesRouteMatch (routeMatcher: RouteMatcherOptions, req: Cypress
matcher = matcher.toLowerCase()
}

if (field === 'url') {
if (value.includes(matcher)) {
continue
}
}

if (!minimatch(value, matcher, { matchBase: true })) {
if (!stringMatch(value, matcher) && (!shouldTryMatchingPath || !stringMatch(matchable.path, matcher))) {
return false
}
}
Expand Down
Loading

4 comments on commit 4669b8e

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4669b8e Dec 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.2.0/circle-develop-4669b8e22a0e4c0411d0187326f02f403bde44be/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4669b8e Dec 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppVeyor has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.2.0/appveyor-develop-4669b8e22a0e4c0411d0187326f02f403bde44be/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4669b8e Dec 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppVeyor has built the win32 ia32 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.2.0/appveyor-develop-4669b8e22a0e4c0411d0187326f02f403bde44be/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 4669b8e Dec 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.2.0/circle-develop-4669b8e22a0e4c0411d0187326f02f403bde44be/cypress.tgz

Please sign in to comment.