Skip to content

Commit

Permalink
fix(xhr): support environments with missing "location" (#482)
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito authored Nov 9, 2023
1 parent e868e46 commit da1cc71
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,17 @@ export class XMLHttpRequestController {
}

function toAbsoluteUrl(url: string | URL): URL {
/**
* @note XMLHttpRequest interceptor may run in environments
* that implement XMLHttpRequest but don't implement "location"
* (for example, React Native). If that's the case, return the
* input URL as-is (nothing to be relative to).
* @see https://github.com/mswjs/msw/issues/1777
*/
if (typeof location === 'undefined') {
return new URL(url)
}

return new URL(url.toString(), location.href)
}

Expand Down
20 changes: 20 additions & 0 deletions test/envs/react-native-like.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* React Native-like environment for Vitest.
*/
import type { Environment } from 'vitest'
import { builtinEnvironments } from 'vitest/environments'

export default <Environment>{
name: 'react-native-like',
async setup(global, options) {
const { teardown } = await builtinEnvironments.jsdom.setup(global, options)

// React Native does not have the global "location" property.
Reflect.deleteProperty(globalThis, 'window')
Reflect.deleteProperty(globalThis, 'location')

return {
teardown,
}
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// @vitest-environment react-native-like
import { it, expect, beforeAll, afterAll } from 'vitest'
import { XMLHttpRequestInterceptor } from '../../../../src/interceptors/XMLHttpRequest'
import { createXMLHttpRequest } from '../../../helpers'

const interceptor = new XMLHttpRequestInterceptor()

beforeAll(() => {
interceptor.apply()
})

afterAll(() => {
interceptor.dispose()
})

it('responds to a request with an absolute URL', async () => {
interceptor.once('request', ({ request }) => {
request.respondWith(new Response('Hello world'))
})

const request = await createXMLHttpRequest((request) => {
request.open('GET', 'https://example.com/resource')
request.send()
})

expect(request.status).toBe(200)
expect(request.response).toBe('Hello world')
})

it('throws on a request with a relative URL', async () => {
const createRequest = () => {
return createXMLHttpRequest((request) => {
/**
* @note Since the "location" is not present in React Native,
* relative requests will throw (nothing to be relative to).
* This is the correct behavior in React Native, where relative
* requests are a no-op.
*/
request.open('GET', '/relative/url')
request.send()
})
}

expect(createRequest).toThrow('Invalid URL: /relative/url')
})
3 changes: 3 additions & 0 deletions test/vitest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ export default defineConfig({
root: __dirname,
include: ['**/*.test.ts'],
exclude: ['**/*.browser.test.ts'],
alias: {
'vitest-environment-react-native-like': './envs/react-native-like',
},
},
})

0 comments on commit da1cc71

Please sign in to comment.