Skip to content

Commit

Permalink
Merge branch 'main' into feat/http-response-empty-type
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed May 8, 2024
2 parents 2ea31c3 + 7cf34c1 commit a18f0cb
Show file tree
Hide file tree
Showing 15 changed files with 352 additions and 156 deletions.
13 changes: 11 additions & 2 deletions config/scripts/validate-esm.js
Expand Up @@ -90,7 +90,7 @@ function validatePackageExports() {
console.log('✅ Validated package.json exports')
}

function validateExportConditions(pointer, conditions) {
function validateExportConditions(pointer, conditions, level = 0) {
if (typeof conditions === 'string') {
invariant(
fs.existsSync(conditions),
Expand All @@ -103,7 +103,7 @@ function validateExportConditions(pointer, conditions) {

const keys = Object.keys(conditions)

if (conditions[keys[0]] !== null) {
if (level == 0 && conditions[keys[0]] !== null) {
invariant(keys[0] === 'types', 'FS')
}

Expand All @@ -115,6 +115,15 @@ function validateExportConditions(pointer, conditions) {
return
}

if (typeof relativeExportPath === 'object') {
validateExportConditions(
`${pointer}.${key}`,
relativeExportPath,
level + 1,
)
return
}

const exportPath = fromRoot(relativeExportPath)
invariant(
fs.existsSync(exportPath),
Expand Down
22 changes: 17 additions & 5 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "msw",
"version": "2.2.13",
"version": "2.3.0",
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
"main": "./lib/core/index.js",
"module": "./lib/core/index.mjs",
Expand All @@ -14,22 +14,34 @@
"default": "./lib/core/index.js"
},
"./browser": {
"node": null,
"types": "./lib/browser/index.d.ts",
"browser": {
"require": "./lib/browser/index.js",
"import": "./lib/browser/index.mjs"
},
"node": null,
"require": "./lib/browser/index.js",
"import": "./lib/browser/index.mjs",
"default": "./lib/browser/index.js"
},
"./node": {
"browser": null,
"types": "./lib/node/index.d.ts",
"node": {
"require": "./lib/node/index.js",
"import": "./lib/node/index.mjs"
},
"browser": null,
"require": "./lib/node/index.js",
"import": "./lib/node/index.mjs",
"default": "./lib/node/index.mjs"
},
"./native": {
"browser": null,
"types": "./lib/native/index.d.ts",
"react-native": {
"require": "./lib/native/index.js",
"import": "./lib/native/index.mjs"
},
"browser": null,
"require": "./lib/native/index.js",
"import": "./lib/native/index.mjs",
"default": "./lib/native/index.js"
Expand Down Expand Up @@ -125,7 +137,7 @@
"@bundled-es-modules/statuses": "^1.0.1",
"@inquirer/confirm": "^3.0.0",
"@mswjs/cookies": "^1.1.0",
"@mswjs/interceptors": "^0.26.14",
"@mswjs/interceptors": "^0.29.0",
"@open-draft/until": "^2.1.0",
"@types/cookie": "^0.6.0",
"@types/statuses": "^2.0.4",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions src/core/utils/internal/Disposable.ts
@@ -1,9 +1,12 @@
export type DisposableSubscription = () => Promise<void> | void
export type DisposableSubscription = () => void

export class Disposable {
protected subscriptions: Array<DisposableSubscription> = []

public async dispose() {
await Promise.all(this.subscriptions.map((subscription) => subscription()))
public dispose() {
let subscription: DisposableSubscription | undefined
while ((subscription = this.subscriptions.shift())) {
subscription()
}
}
}
21 changes: 21 additions & 0 deletions src/core/utils/internal/devUtils.test.ts
@@ -0,0 +1,21 @@
import { InternalError } from './devUtils'

describe(InternalError, () => {
it('creates an InternalError instance', () => {
const error = new InternalError('Message')

expect(error.name).toBe('InternalError')
expect(error.message).toBe('Message')
expect(error.toString()).toBe('InternalError: Message')
expect(error.stack).toMatch(/\w+/)
})

it('passes the identity check', () => {
const error = new InternalError('Message')
expect(error instanceof InternalError).toBe(true)
expect(error instanceof Error).toBe(true)

const extraneousError = new Error('Message')
expect(extraneousError).not.toBeInstanceOf(InternalError)
})
})
13 changes: 13 additions & 0 deletions src/core/utils/internal/devUtils.ts
Expand Up @@ -29,3 +29,16 @@ export const devUtils = {
warn,
error,
}

/**
* Internal error instance.
* Used to differentiate the library errors that must be forwarded
* to the user from the unhandled exceptions. Use this if you don't
* wish for the error to be coerced to a 500 fallback response.
*/
export class InternalError extends Error {
constructor(message: string) {
super(message)
this.name = 'InternalError'
}
}
34 changes: 30 additions & 4 deletions src/core/utils/request/onUnhandledRequest.test.ts
Expand Up @@ -7,10 +7,10 @@ import {
} from './onUnhandledRequest'

const fixtures = {
warningWithoutSuggestions: `\
warningWithoutSuggestions: (url = `/api`) => `\
[MSW] Warning: intercepted a request without a matching request handler:
• GET /api
• GET ${url}
If you still wish to intercept this unhandled request, please create a request handler for it.
Read more: https://mswjs.io/docs/getting-started/mocks`,
Expand Down Expand Up @@ -46,7 +46,9 @@ test('supports the "bypass" request strategy', async () => {
test('supports the "warn" request strategy', async () => {
await onUnhandledRequest(new Request(new URL('http://localhost/api')), 'warn')

expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions)
expect(console.warn).toHaveBeenCalledWith(
fixtures.warningWithoutSuggestions(),
)
})

test('supports the "error" request strategy', async () => {
Expand Down Expand Up @@ -103,7 +105,9 @@ test('supports calling default strategies from the custom callback function', as
test('does not print any suggestions given no handlers to suggest', async () => {
await onUnhandledRequest(new Request(new URL('http://localhost/api')), 'warn')

expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions)
expect(console.warn).toHaveBeenCalledWith(
fixtures.warningWithoutSuggestions(),
)
})

test('throws an exception given unknown request strategy', async () => {
Expand All @@ -117,3 +121,25 @@ test('throws an exception given unknown request strategy', async () => {
'[MSW] Failed to react to an unhandled request: unknown strategy "invalid-strategy". Please provide one of the supported strategies ("bypass", "warn", "error") or a custom callback function as the value of the "onUnhandledRequest" option.',
)
})

test('prints with a relative URL and search params', async () => {
await onUnhandledRequest(
new Request(new URL('http://localhost/api?foo=boo')),
'warn',
)

expect(console.warn).toHaveBeenCalledWith(
fixtures.warningWithoutSuggestions(`/api?foo=boo`),
)
})

test('prints with an absolute URL and search params', async () => {
await onUnhandledRequest(
new Request(new URL('https://mswjs.io/api?foo=boo')),
'warn',
)

expect(console.warn).toHaveBeenCalledWith(
fixtures.warningWithoutSuggestions(`https://mswjs.io/api?foo=boo`),
)
})
8 changes: 4 additions & 4 deletions src/core/utils/request/onUnhandledRequest.ts
@@ -1,5 +1,5 @@
import { toPublicUrl } from './toPublicUrl'
import { devUtils } from '../internal/devUtils'
import { InternalError, devUtils } from '../internal/devUtils'

export interface UnhandledRequestPrint {
warning(): void
Expand All @@ -22,7 +22,7 @@ export async function onUnhandledRequest(
strategy: UnhandledRequestStrategy = 'warn',
): Promise<void> {
const url = new URL(request.url)
const publicUrl = toPublicUrl(url)
const publicUrl = toPublicUrl(url) + url.search

const unhandledRequestMessage = `intercepted a request without a matching request handler:\n\n \u2022 ${request.method} ${publicUrl}\n\nIf you still wish to intercept this unhandled request, please create a request handler for it.\nRead more: https://mswjs.io/docs/getting-started/mocks`

Expand All @@ -33,7 +33,7 @@ export async function onUnhandledRequest(
devUtils.error('Error: %s', unhandledRequestMessage)

// Throw an exception to halt request processing and not perform the original request.
throw new Error(
throw new InternalError(
devUtils.formatMessage(
'Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.',
),
Expand All @@ -49,7 +49,7 @@ export async function onUnhandledRequest(
break

default:
throw new Error(
throw new InternalError(
devUtils.formatMessage(
'Failed to react to an unhandled request: unknown strategy "%s". Please provide one of the supported strategies ("bypass", "warn", "error") or a custom callback function as the value of the "onUnhandledRequest" option.',
strategy,
Expand Down
8 changes: 7 additions & 1 deletion src/node/SetupServerCommonApi.ts
Expand Up @@ -15,7 +15,7 @@ import { SetupApi } from '~/core/SetupApi'
import { handleRequest } from '~/core/utils/handleRequest'
import type { RequestHandler } from '~/core/handlers/RequestHandler'
import { mergeRight } from '~/core/utils/internal/mergeRight'
import { devUtils } from '~/core/utils/internal/devUtils'
import { InternalError, devUtils } from '~/core/utils/internal/devUtils'
import type { SetupServerCommon } from './glossary'

export const DEFAULT_LISTEN_OPTIONS: RequiredDeep<SharedOptions> = {
Expand Down Expand Up @@ -68,6 +68,12 @@ export class SetupServerCommonApi
return
})

this.interceptor.on('unhandledException', ({ error }) => {
if (error instanceof InternalError) {
throw error
}
})

this.interceptor.on(
'response',
({ response, isMockedResponse, request, requestId }) => {
Expand Down

0 comments on commit a18f0cb

Please sign in to comment.