Skip to content

Commit

Permalink
feat: support for WHATWG URLs (#2437)
Browse files Browse the repository at this point in the history
Co-authored-by: Ryan Wheale <ryan.wheale@anark.com>
Co-authored-by: Matt R. Wilson <github@mattw.co>
  • Loading branch information
3 people committed Jan 10, 2023
1 parent 4318e35 commit 3375382
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 11 deletions.
8 changes: 7 additions & 1 deletion README.md
Expand Up @@ -159,14 +159,20 @@ If you don’t want interceptors to be removed as they are used, you can use the

### Specifying hostname

The request hostname can be a string or a RegExp.
The request hostname can be a string, URL, or a RegExp.

```js
const scope = nock('http://www.example.com')
.get('/resource')
.reply(200, 'domain matched')
```

```js
const scope = nock(new URL('http://www.example.com'))
.get('/resource')
.reply(200, 'domain matched')
```

```js
const scope = nock(/example\.com/)
.get('/resource')
Expand Down
47 changes: 43 additions & 4 deletions lib/scope.js
Expand Up @@ -11,6 +11,7 @@ const debug = require('debug')('nock.scope')
const { EventEmitter } = require('events')
const Interceptor = require('./interceptor')

const { URL, Url: LegacyUrl } = url
let fs

try {
Expand All @@ -20,7 +21,46 @@ try {
}

/**
* @param {string|RegExp|url.url} basePath
* Normalizes the passed url for consistent internal processing
* @param {string|LegacyUrl|URL} u
*/
function normalizeUrl(u) {
if (!(u instanceof URL)) {
if (u instanceof LegacyUrl) {
return normalizeUrl(new URL(url.format(u)))
}
// If the url is invalid, let the URL library report it
return normalizeUrl(new URL(u))
}

if (!/https?:/.test(u.protocol)) {
throw new TypeError(
`Protocol '${u.protocol}' not recognized. This commonly occurs when a hostname and port are included without a protocol, producing a URL that is valid but confusing, and probably not what you want.`
)
}

return {
href: u.href,
origin: u.origin,
protocol: u.protocol,
username: u.username,
password: u.password,
host: u.host,
hostname:
// strip brackets from IPv6
typeof u.hostname === 'string' && u.hostname.startsWith('[')
? u.hostname.slice(1, -1)
: u.hostname,
port: u.port || (u.protocol === 'http:' ? 80 : 443),
pathname: u.pathname,
search: u.search,
searchParams: u.searchParams,
hash: u.hash,
}
}

/**
* @param {string|RegExp|LegacyUrl|URL} basePath
* @param {Object} options
* @param {boolean} options.allowUnmocked
* @param {string[]} options.badheaders
Expand Down Expand Up @@ -52,9 +92,8 @@ class Scope extends EventEmitter {
let logNamespace = String(basePath)

if (!(basePath instanceof RegExp)) {
this.urlParts = url.parse(basePath)
this.port =
this.urlParts.port || (this.urlParts.protocol === 'http:' ? 80 : 443)
this.urlParts = normalizeUrl(basePath)
this.port = this.urlParts.port
this.basePathname = this.urlParts.pathname.replace(/\/$/, '')
this.basePath = `${this.urlParts.protocol}//${this.urlParts.hostname}:${this.port}`
logNamespace = this.urlParts.host
Expand Down
34 changes: 28 additions & 6 deletions tests/test_scope.js
Expand Up @@ -34,19 +34,41 @@ describe('`Scope#constructor`', () => {
scope.done()
})

// TODO: https://github.com/nock/nock/pull/1879
it.skip('accepts a WHATWG URL instance', async () => {
it('accepts a WHATWG URL instance', async () => {
const scope = nock(new url.URL('http://example.test')).get('/').reply()

const { statusCode } = await got('http://example.test')
expect(statusCode).to.equal(200)
scope.done()
})

it('fails when provided a WHATWG URL instance', () => {
// This test just proves the lack of current support. When this feature is added,
// this test should be removed and the test above un-skipped.
expect(() => nock(new url.URL('http://example.test'))).to.throw()
it('throws on invalid or omitted protocol', async () => {
expect(() => nock('ws://example.test')).to.throw()
expect(() => nock('localhost/foo')).to.throw()
expect(() => nock('foo.com:1234')).to.throw()
})

it('throws on invalid URL format', async () => {
expect(() => nock(['This is not a url'])).to.throw()
// The following contains all valid properties of WHATWG URL, but is not an
// instance of URL. Maybe we should support object literals some day? A
// simple duck-type validator would suffice.
expect(() =>
nock({
href: 'http://google.com/foo',
origin: 'http://google.com',
protocol: 'http:',
username: '',
password: '',
host: 'google.com',
hostname: 'google.com',
port: 80,
pathname: '/foo',
search: '',
searchParams: {},
hash: '',
})
).to.throw()
})
})

Expand Down

0 comments on commit 3375382

Please sign in to comment.