feat: migrate to ESM internally, remain CJS-first (#2490)

Co-authored-by: Mateusz Burzyński <[email protected]>
This commit is contained in:
Artem Zakharchenko
2025-05-08 19:05:47 +02:00
committed by GitHub
co-authored by Mateusz Burzyński
parent b678b44033
commit 37d96f55ab
165 changed files with 1465 additions and 1130 deletions
+3 -3
View File
@@ -33,8 +33,8 @@ jobs:
- name: Build
run: pnpm build
- name: Validate package.json exports
run: pnpm check:exports
- name: Lint package
run: pnpm publint
- name: Test modules (Node.js)
run: pnpm test:modules:node
@@ -54,7 +54,7 @@ jobs:
matrix:
# As a general rule, when adding a new version, remove the oldest version
# Check if the oldest version is EOL or not first.
ts: ['4.8', '4.9', '5.0', '5.1', '5.2', '5.3', '5.4', '5.5']
ts: ['5.0', '5.1', '5.2', '5.3', '5.4', '5.5']
steps:
- name: Checkout
uses: actions/checkout@v4
+1 -1
View File
@@ -19,4 +19,4 @@ msw-*.tgz
/package.json.copy
/examples
/test/modules/node/node-esm-tests
tsconfig.vitest-temp.json
*.vitest-temp.json
+1 -1
View File
@@ -175,7 +175,7 @@ import { test, expect } from './playwright.extend'
test('returns a mocked response', async ({ loadExample, fetch }) => {
// Compile the given usage example on runtime.
await loadExample(require.resolve('./example.mocks.ts'))
await loadExample(new URL('./example.mocks.ts', import.meta.url))
// Perform the "GET /books" request in the browser.
const res = await fetch('/books')
+28 -3
View File
@@ -1,5 +1,30 @@
{
"main": "../lib/browser/index.js",
"module": "../lib/browser/index.mjs",
"types": "../lib/browser/index.d.ts"
"type": "module",
"main": "../lib/browser/index.mjs",
"types": "../lib/browser/index.d.mts",
"exports": {
".": {
"module-sync": {
"types": "./../lib/browser/index.d.mts",
"default": "./../lib/browser/index.mjs"
},
"module": {
"types": "./../lib/browser/index.d.mts",
"default": "./../lib/browser/index.mjs"
},
"browser": {
"types": "./../lib/browser/index.d.mts",
"default": "./../lib/browser/index.mjs"
},
"import": {
"types": "./../lib/browser/index.d.mts",
"default": "./../lib/browser/index.mjs"
},
"default": {
"types": "./../lib/browser/index.d.ts",
"default": "./../lib/browser/index.js"
}
},
"./package.json": "./package.json"
}
}
+5 -3
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env node
const yargs = require('yargs')
import yargs from 'yargs'
import { init } from './init.js'
yargs
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
yargs(process.argv.slice(2))
.usage('$0 <cmd> [args]')
.command(
'init',
@@ -27,7 +29,7 @@ yargs
.example('msw init ./public')
.example('msw init ./static --save')
},
require('./init'),
init,
)
.demandCommand()
.help().argv
+12 -7
View File
@@ -1,11 +1,11 @@
const fs = require('fs')
const path = require('path')
const colors = require('picocolors')
const confirm = require('@inquirer/confirm').default
const invariant = require('./invariant')
const { SERVICE_WORKER_BUILD_PATH } = require('../config/constants')
import fs from 'node:fs'
import path from 'node:path'
import colors from 'picocolors'
import confirm from '@inquirer/confirm'
import { invariant } from './invariant.js'
import { SERVICE_WORKER_BUILD_PATH } from '../config/constants.js'
module.exports = async function init(args) {
export async function init(args) {
const CWD = args.cwd || process.cwd()
const publicDir = args._[1] ? normalizePath(args._[1]) : undefined
@@ -33,6 +33,7 @@ module.exports = async function init(args) {
// You can also provide the "--no-save" option, and then "args.save"
// will equal to false.
else if (args.save == null) {
// eslint-disable-next-line no-console
console.log(`\
${colors.cyan(
'INFO',
@@ -126,6 +127,7 @@ async function copyWorkerScript(destination, cwd) {
})
}
// eslint-disable-next-line no-console
console.log('Copying the worker script at "%s"...', absolutePublicDir)
const workerFilename = path.basename(SERVICE_WORKER_BUILD_PATH)
@@ -140,6 +142,7 @@ async function copyWorkerScript(destination, cwd) {
* @param {Array<string>} paths
*/
function printSuccessMessage(paths) {
// eslint-disable-next-line no-console
console.log(`
${colors.green('Worker script successfully copied!')}
${paths.map((path) => colors.gray(` - ${path}\n`))}
@@ -151,6 +154,7 @@ ${colors.red(colors.bold('https://mswjs.io/docs/getting-started'))}
}
function printFailureMessage(pathsWithErrors) {
// eslint-disable-next-line no-console
console.error(`\
${colors.red('Copying the worker script failed at following paths:')}
${pathsWithErrors
@@ -166,6 +170,7 @@ ${pathsWithErrors
function saveWorkerDirectory(packageJsonPath, publicDir) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
// eslint-disable-next-line no-console
console.log(
colors.gray('Updating "msw.workerDirectory" at "%s"...'),
packageJsonPath,
+3 -2
View File
@@ -1,7 +1,8 @@
const colors = require('picocolors')
import colors from 'picocolors'
module.exports = function invariant(predicate, message, ...args) {
export function invariant(predicate, message, ...args) {
if (!predicate) {
// eslint-disable-next-line no-console
console.error(colors.red(message), ...args)
process.exit(1)
}
+3
View File
@@ -0,0 +1,3 @@
{
"type": "module"
}
+9 -13
View File
@@ -1,17 +1,13 @@
const path = require('path')
import url from 'node:url'
import path from 'node:path'
const SERVICE_WORKER_SOURCE_PATH = path.resolve(
__dirname,
'../src/mockServiceWorker.js',
export const SERVICE_WORKER_SOURCE_PATH = url.fileURLToPath(
new URL('../src/mockServiceWorker.js', import.meta.url),
)
const SERVICE_WORKER_BUILD_PATH = path.resolve(
__dirname,
'../lib',
path.basename(SERVICE_WORKER_SOURCE_PATH),
export const SERVICE_WORKER_BUILD_PATH = url.fileURLToPath(
new URL(
path.join('../lib', path.basename(SERVICE_WORKER_SOURCE_PATH)),
import.meta.url,
),
)
module.exports = {
SERVICE_WORKER_SOURCE_PATH,
SERVICE_WORKER_BUILD_PATH,
}
+3 -3
View File
@@ -1,5 +1,5 @@
import * as fs from 'fs'
import * as path from 'path'
import fs from 'node:fs'
import path from 'node:path'
import { until } from '@open-draft/until'
/**
@@ -30,7 +30,7 @@ export default async function copyServiceWorker(
}
const packageJson = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf8'),
fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
)
const nextFileContent = readFileResult.data
+3
View File
@@ -0,0 +1,3 @@
{
"type": "module"
}
+3 -3
View File
@@ -1,10 +1,10 @@
import fs from 'fs'
import path from 'path'
import fs from 'node:fs'
import path from 'node:path'
import crypto from 'crypto'
import minify from 'babel-minify'
import { invariant } from 'outvariant'
import type { Plugin } from 'esbuild'
import copyServiceWorker from '../../copyServiceWorker'
import copyServiceWorker from '../../copyServiceWorker.js'
const SERVICE_WORKER_ENTRY_PATH = path.resolve(
process.cwd(),
@@ -1,4 +1,7 @@
import { Plugin } from 'esbuild'
import { type Plugin } from 'esbuild'
export const ESM_EXTENSION = '.mjs'
export const CJS_EXTENSION = '.js'
export function forceEsmExtensionsPlugin(): Plugin {
return {
@@ -16,7 +19,7 @@ export function forceEsmExtensionsPlugin(): Plugin {
// This ignores additional files emitted, like sourcemaps ("*.js.map").
if (
!(
outputFile.path.endsWith('.js') ||
outputFile.path.endsWith(ESM_EXTENSION) ||
outputFile.path.endsWith('.mjs')
)
) {
@@ -37,7 +40,7 @@ const CJS_RELATIVE_IMPORT_EXP = /require\(["'](\..+)["']\)(;)?/gm
const ESM_RELATIVE_IMPORT_EXP = /from ["'](\..+)["'](;)?/gm
function modifyRelativeImports(contents: string, isEsm: boolean): string {
const extension = isEsm ? '.mjs' : '.js'
const extension = isEsm ? ESM_EXTENSION : CJS_EXTENSION
const importExpression = isEsm
? ESM_RELATIVE_IMPORT_EXP
: CJS_RELATIVE_IMPORT_EXP
@@ -1,4 +1,4 @@
import fs from 'fs/promises'
import fs from 'node:fs'
import type { Plugin } from 'esbuild'
/**
@@ -16,7 +16,7 @@ export function graphqlImportPlugin(): Plugin {
}
build.onLoad({ filter: /\.ts$/ }, async (args) => {
const contents = await fs.readFile(args.path, 'utf-8')
const contents = await fs.promises.readFile(args.path, 'utf-8')
const match = /require\(['"]graphql['"]\)/g.exec(contents)
if (match) {
@@ -1,6 +1,6 @@
import { Plugin } from 'esbuild'
const { replaceCoreImports } = require('../../replaceCoreImports')
import { replaceCoreImports } from '../../replaceCoreImports.js'
import { ESM_EXTENSION } from './forceEsmExtensionsPlugin.js'
export function resolveCoreImportsPlugin(): Plugin {
return {
@@ -12,7 +12,7 @@ export function resolveCoreImportsPlugin(): Plugin {
}
for (const outputFile of result.outputFiles || []) {
const isEsm = outputFile.path.endsWith('.mjs')
const isEsm = outputFile.path.endsWith(ESM_EXTENSION)
const fileContents = outputFile.text
const nextFileContents = replaceCoreImports(fileContents, isEsm)
+3 -8
View File
@@ -5,11 +5,11 @@ function getCoreImportPattern(isEsm) {
return isEsm ? CORE_ESM_IMPORT_PATTERN : CORE_CJS_IMPORT_PATTERN
}
function hasCoreImports(fileContents, isEsm) {
export function hasCoreImports(fileContents, isEsm) {
return getCoreImportPattern(isEsm).test(fileContents)
}
function replaceCoreImports(fileContents, isEsm) {
export function replaceCoreImports(fileContents, isEsm) {
return fileContents.replace(
getCoreImportPattern(isEsm),
(_, __, maybeSubmodulePath, maybeSemicolon) => {
@@ -17,13 +17,8 @@ function replaceCoreImports(fileContents, isEsm) {
const semicolon = maybeSemicolon || ''
return isEsm
? `from "../core${submodulePath}"${semicolon}`
? `from "../core${submodulePath.endsWith('.mjs') ? submodulePath : submodulePath + '.mjs'}"${semicolon}`
: `require("../core${submodulePath}")${semicolon}`
},
)
}
module.exports = {
hasCoreImports,
replaceCoreImports,
}
+8 -8
View File
@@ -1,14 +1,14 @@
const fs = require('node:fs')
const { exec } = require('node:child_process')
const path = require('node:path')
const { promisify } = require('node:util')
const { invariant } = require('outvariant')
const glob = require('glob')
const { hasCoreImports, replaceCoreImports } = require('../replaceCoreImports')
import fs from 'node:fs'
import url from 'node:url'
import { exec } from 'node:child_process'
import { promisify } from 'node:util'
import { invariant } from 'outvariant'
import * as glob from 'glob'
import { hasCoreImports, replaceCoreImports } from '../replaceCoreImports.js'
const execAsync = promisify(exec)
const BUILD_DIR = path.resolve(__dirname, '../../lib')
const BUILD_DIR = url.fileURLToPath(new URL('../../lib', import.meta.url))
async function patchTypeDefs() {
const typeDefsPaths = glob.sync('**/*.d.{ts,mts}', {
+3 -3
View File
@@ -1,6 +1,6 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
import fs from 'node:fs'
import path from 'node:path'
import { execSync } from 'node:child_process'
// When executing the "postinstall" script, the "process.cwd" equals
// the package directory, not the parent project where the package is installed.
-256
View File
@@ -1,256 +0,0 @@
const fs = require('fs')
const path = require('path')
const { invariant } = require('outvariant')
const ROOT_PATH = path.resolve(__dirname, '../..')
function fromRoot(...paths) {
return path.resolve(ROOT_PATH, ...paths)
}
const PKG_JSON_PATH = fromRoot('package.json')
const PKG_JSON = require(PKG_JSON_PATH)
function validatePackageExports() {
const { exports } = PKG_JSON
// Validate the "main", "browser", and "types" root fields.
invariant('main' in PKG_JSON, 'Missing "main" field in package.json')
invariant('module' in PKG_JSON, 'Missing "module" field in package.json')
invariant('types' in PKG_JSON, 'Missing "types" field in package.json')
invariant(
fs.existsSync(fromRoot(PKG_JSON.main)),
'The "main" field points at a non-existing path at "%s"',
PKG_JSON.main,
)
// The "exports" key must be present.
invariant(exports, 'package.json must have an "exports" field')
// The "exports" must list expected paths.
const expectedExportPaths = [
'.',
'./browser',
'./node',
'./package.json',
'./native',
]
expectedExportPaths.forEach((exportPath) => {
invariant(exportPath in exports, 'Missing exports path "%s"', exportPath)
})
// Must describe the root export properly.
const rootExport = exports['.']
validateExportConditions(`exports['.']`, rootExport)
validateBundle(rootExport.require, false)
validateBundle(rootExport.import, true)
validateTypeDefs(rootExport.types)
// Validate "./browser" exports.
const browserExports = exports['./browser']
validateExportConditions(`exports['./browser']`, browserExports)
invariant(
browserExports.node === null,
'The "browser" export must set the "node" field to null',
)
validateBundle(browserExports.require, false)
validateBundle(browserExports.import, true)
validateTypeDefs(browserExports.types)
// Validate "./node" exports.
const nodeExports = exports['./node']
validateExportConditions(`exports['./node']`, nodeExports)
invariant(
nodeExports.browser === null,
'The "node" export must set the "browser" field to null',
)
validateBundle(nodeExports.require, false)
validateBundle(nodeExports.import, true)
validateTypeDefs(nodeExports.types)
// Validate "./native" exports.
const nativeExports = exports['./native']
validateExportConditions(`exports['./native']`, nativeExports)
invariant(
nativeExports.browser === null,
'The "native" export must set the "browser" field to null',
)
validateBundle(nativeExports.require, false)
validateBundle(nativeExports.import, true)
validateTypeDefs(nativeExports.types)
// Validate "./package.json" exports.
validateExportConditions(
`exports['./package.json]`,
exports['./package.json'],
)
console.log('✅ Validated package.json exports')
}
function validateExportConditions(pointer, conditions, level = 0) {
if (typeof conditions === 'string') {
invariant(
fs.existsSync(conditions),
'Expected a valid path at "%s" but got %s',
pointer,
conditions,
)
return
}
const keys = Object.keys(conditions)
if (level == 0 && conditions[keys[0]] !== null) {
invariant(keys[0] === 'types', 'FS')
}
// Ensure that paths point to existing files.
keys.forEach((key) => {
const relativeExportPath = conditions[key]
if (relativeExportPath === null) {
return
}
if (typeof relativeExportPath === 'object') {
validateExportConditions(
`${pointer}.${key}`,
relativeExportPath,
level + 1,
)
return
}
const exportPath = fromRoot(relativeExportPath)
invariant(
fs.existsSync(exportPath),
'Expected the path at "%s" ("%s") to point at existing file but got %s',
pointer,
key,
exportPath,
)
})
}
const ESM_CORE_IMPORT_EXP = /from ["'](..\/)+core(.*)["'];?$/gm
const CJS_CORE_IMPORT_EXP = /require\(["'](..\/)+core(.*)["']\);?$/gm
function getCodeSnippetAt(contents, index) {
return contents.slice(index - 100, index + 50)
}
function validateBundle(bundlePath, isEsm = false) {
const expectedExtension = isEsm ? '.mjs' : '.js'
invariant(
bundlePath.endsWith(expectedExtension),
'Failed to validate bundle: provided bundle path does not point at an ".mjs" file: %s',
bundlePath,
)
const absoluteBundlePath = fromRoot(bundlePath)
const contents = fs.readFileSync(absoluteBundlePath, 'utf8')
// The "~/core" imports must be overwritten on the bundler level.
invariant(
!contents.includes('~/core'),
'Bundle at "%s" includes unresolved "~/core" imports:\n\n%s',
bundlePath,
getCodeSnippetAt(contents, contents.indexOf('~/core')),
)
// The "core" imports must end with the explicit ".mjs" extension.
const coreImportsMatches =
contents.matchAll(isEsm ? ESM_CORE_IMPORT_EXP : CJS_CORE_IMPORT_EXP) || []
for (const match of coreImportsMatches) {
const [, backslashes, relativeImportPath] = match
invariant(
backslashes === '../',
'Found a "core" import with incorrect nesting level',
)
invariant(
relativeImportPath !== '',
'Found a "core" import without an explicit path at "%s":\n\n%s',
absoluteBundlePath,
getCodeSnippetAt(contents, match.index),
)
if (isEsm) {
// Ensure that all relative imports in the ESM bundle end with ".mjs".
// This way bundlers can distinguish between the referenced modules
// since the "core" directory contains both ".js" and ".mjs" modules on the same level.
invariant(
relativeImportPath.endsWith('.mjs'),
`Found a "core" import without "${expectedExtension}" extension at "%s":\n\n%s`,
absoluteBundlePath,
getCodeSnippetAt(contents, match.index),
)
}
}
console.log('✅ Validated bundle at "%s"', bundlePath)
}
function validateTypeDefs(typeDefsPath) {
const absoluteTypeDefsPath = fromRoot(typeDefsPath)
invariant(
fs.existsSync(absoluteTypeDefsPath),
'Failed to validate type definitions at "%s": file does not exist',
absoluteTypeDefsPath,
)
const contents = fs.readFileSync(absoluteTypeDefsPath, 'utf8')
// The "~/core" imports must also be replaced with relative paths on build.
invariant(
!contents.includes('~/core'),
'Found unresolved "~/core" imports at "%s":\n\n%s',
absoluteTypeDefsPath,
getCodeSnippetAt(contents, contents.indexOf('~/core')),
)
console.log('✅ Validated type definitions at "%s"', typeDefsPath)
}
function validatePackageFiles() {
const { files } = PKG_JSON
const expectedFiles = [
'config/constants.js',
'config/scripts/postinstall.js',
'cli',
'lib',
'browser',
'node',
'native',
]
// Must list all the expcted files.
expectedFiles.forEach((expectedFile) => {
invariant(
files.includes(expectedFile),
'"%s" is not listed in "files" in package.json',
expectedFile,
)
})
// All the listed files must exist.
expectedFiles.every((expectedFile) => {
invariant(
fs.existsSync(fromRoot(expectedFile)),
'The file "%s" in "files" points at non-existing file',
expectedFile,
)
})
console.log('✅ Validated package.json "files" field')
}
validatePackageExports()
validatePackageFiles()
+25 -1
View File
@@ -1,6 +1,30 @@
{
"type": "commonjs",
"browser": null,
"main": "../lib/native/index.js",
"module": "../lib/native/index.mjs",
"types": "../lib/native/index.d.ts"
"types": "../lib/native/index.d.ts",
"exports": {
".": {
"react-native": {
"import": {
"types": "./../lib/native/index.d.mts",
"default": "./../lib/native/index.mjs"
},
"default": {
"types": "./../lib/native/index.d.ts",
"default": "./../lib/native/index.js"
}
},
"browser": null,
"import": {
"types": "./../lib/native/index.d.mts",
"default": "./../lib/native/index.mjs"
},
"default": {
"types": "./../lib/native/index.d.ts",
"default": "./../lib/native/index.js"
}
}
}
}
+28 -1
View File
@@ -1,6 +1,33 @@
{
"type": "commonjs",
"browser": null,
"main": "../lib/node/index.js",
"module": "../lib/node/index.mjs",
"types": "../lib/node/index.d.ts"
"types": "../lib/node/index.d.ts",
"exports": {
".": {
"module-sync": {
"types": "./../lib/node/index.d.mts",
"default": "./../lib/node/index.mjs"
},
"module": {
"types": "./../lib/node/index.d.mts",
"default": "./../lib/node/index.mjs"
},
"node": {
"require": "./../lib/node/index.js",
"import": "./../lib/node/index.mjs"
},
"import": {
"types": "./../lib/node/index.d.mts",
"default": "./../lib/node/index.mjs"
},
"browser": null,
"react-native": null,
"default": {
"types": "./../lib/node/index.d.ts",
"default": "./../lib/node/index.js"
}
}
}
}
+144 -46
View File
@@ -2,67 +2,162 @@
"name": "msw",
"version": "2.7.6",
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
"type": "commonjs",
"main": "./lib/core/index.js",
"module": "./lib/core/index.mjs",
"types": "./lib/core/index.d.ts",
"packageManager": "[email protected]",
"exports": {
".": {
"types": "./lib/core/index.d.ts",
"require": "./lib/core/index.js",
"import": "./lib/core/index.mjs",
"default": "./lib/core/index.js"
"module-sync": {
"types": "./lib/core/index.d.mts",
"default": "./lib/core/index.mjs"
},
"module": {
"types": "./lib/core/index.d.mts",
"default": "./lib/core/index.mjs"
},
"react-native": {
"import": {
"types": "./lib/core/index.d.mts",
"default": "./lib/core/index.mjs"
},
"default": {
"types": "./lib/core/index.d.ts",
"default": "./lib/core/index.js"
}
},
"import": {
"types": "./lib/core/index.d.mts",
"default": "./lib/core/index.mjs"
},
"default": {
"types": "./lib/core/index.d.ts",
"default": "./lib/core/index.js"
}
},
"./browser": {
"types": "./lib/browser/index.d.ts",
"module-sync": {
"types": "./lib/browser/index.d.mts",
"default": "./lib/browser/index.mjs"
},
"module": {
"types": "./lib/browser/index.d.mts",
"default": "./lib/browser/index.mjs"
},
"browser": {
"require": "./lib/browser/index.js",
"import": "./lib/browser/index.mjs"
"types": "./lib/browser/index.d.mts",
"default": "./lib/browser/index.mjs"
},
"import": {
"types": "./lib/browser/index.d.mts",
"default": "./lib/browser/index.mjs"
},
"node": null,
"require": "./lib/browser/index.js",
"import": "./lib/browser/index.mjs",
"default": "./lib/browser/index.js"
"react-native": null,
"default": {
"types": "./lib/browser/index.d.ts",
"default": "./lib/browser/index.js"
}
},
"./node": {
"types": "./lib/node/index.d.ts",
"browser": null,
"react-native": null,
"module-sync": {
"types": "./lib/node/index.d.mts",
"default": "./lib/node/index.mjs"
},
"module": {
"types": "./lib/node/index.d.mts",
"default": "./lib/node/index.mjs"
},
"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"
"import": {
"types": "./lib/node/index.d.mts",
"default": "./lib/node/index.mjs"
},
"default": {
"types": "./lib/node/index.d.ts",
"default": "./lib/node/index.js"
}
},
"./native": {
"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"
"react-native": {
"import": {
"types": "./lib/native/index.d.mts",
"default": "./lib/native/index.mjs"
},
"default": {
"types": "./lib/native/index.d.ts",
"default": "./lib/native/index.js"
}
},
"import": {
"types": "./lib/native/index.d.mts",
"default": "./lib/native/index.mjs"
},
"default": {
"types": "./lib/native/index.d.ts",
"default": "./lib/native/index.js"
}
},
"./core/http": {
"types": "./lib/core/http.d.ts",
"require": "./lib/core/http.js",
"import": "./lib/core/http.mjs",
"default": "./lib/core/http.js"
"module-sync": {
"types": "./lib/core/http.d.mts",
"default": "./lib/core/http.mjs"
},
"module": {
"types": "./lib/core/http.d.mts",
"default": "./lib/core/http.mjs"
},
"import": {
"types": "./lib/core/http.d.mts",
"default": "./lib/core/http.mjs"
},
"default": {
"types": "./lib/core/http.d.ts",
"default": "./lib/core/http.js"
}
},
"./core/graphql": {
"types": "./lib/core/graphql.d.ts",
"require": "./lib/core/graphql.js",
"import": "./lib/core/graphql.mjs",
"default": "./lib/core/graphql.js"
"module-sync": {
"types": "./lib/core/graphql.d.mts",
"default": "./lib/core/graphql.mjs"
},
"module": {
"types": "./lib/core/graphql.d.mts",
"default": "./lib/core/graphql.mjs"
},
"import": {
"types": "./lib/core/graphql.d.mts",
"default": "./lib/core/graphql.mjs"
},
"default": {
"types": "./lib/core/graphql.d.ts",
"default": "./lib/core/graphql.js"
}
},
"./core/ws": {
"types": "./lib/core/ws.d.ts",
"require": "./lib/core/ws.js",
"import": "./lib/core/ws.mjs",
"default": "./lib/core/ws.js"
"module-sync": {
"types": "./lib/core/ws.d.mts",
"default": "./lib/core/ws.mjs"
},
"module": {
"types": "./lib/core/ws.d.mts",
"default": "./lib/core/ws.mjs"
},
"import": {
"types": "./lib/core/ws.d.mts",
"default": "./lib/core/ws.mjs"
},
"default": {
"types": "./lib/core/ws.d.ts",
"default": "./lib/core/ws.js"
}
},
"./mockServiceWorker.js": "./lib/mockServiceWorker.js",
"./package.json": "./package.json"
@@ -79,20 +174,20 @@
"lint": "eslint \"{cli,src}/**/*.ts\"",
"build": "pnpm clean && cross-env NODE_ENV=production tsup && pnpm patch:dts",
"patch:dts": "node \"./config/scripts/patch-ts.js\"",
"check:exports": "node \"./config/scripts/validate-esm.js\"",
"publint": "publint",
"test": "pnpm test:unit && pnpm test:node && pnpm test:browser && pnpm test:native",
"test:unit": "vitest",
"test:node": "vitest --config=./test/node/vitest.config.mts",
"test:native": "vitest --config=./test/native/vitest.config.mts",
"test:node": "vitest --config=./test/node/vitest.config.ts",
"test:native": "vitest --config=./test/native/vitest.config.ts",
"test:browser": "playwright test -c ./test/browser/playwright.config.ts",
"test:modules:node": "vitest --config=./test/modules/node/vitest.config.mts",
"test:modules:node": "vitest run --config=./test/modules/node/vitest.config.ts",
"test:modules:browser": "playwright test -c ./test/modules/browser/playwright.config.ts",
"test:e2e": "vitest run --config=./test/e2e/vitest.config.mts",
"test:ts": "vitest --typecheck --config=./test/typings/vitest.config.mts",
"test:e2e": "vitest run --config=./test/e2e/vitest.config.ts",
"test:ts": "vitest --config=./test/typings/vitest.config.ts",
"prepare": "pnpm simple-git-hooks init",
"prepack": "pnpm build",
"release": "release publish",
"postinstall": "node -e \"try{require('./config/scripts/postinstall')}catch(e){}\"",
"postinstall": "node -e \"try{import('./config/scripts/postinstall.js')}catch(e){}\"",
"knip": "knip"
},
"lint-staged": {
@@ -106,7 +201,7 @@
"homepage": "https://mswjs.io",
"repository": {
"type": "git",
"url": "https://github.com/mswjs/msw"
"url": "git+https://github.com/mswjs/msw.git"
},
"author": {
"name": "Artem Zakharchenko",
@@ -115,6 +210,7 @@
"license": "MIT",
"funding": "https://github.com/sponsors/mswjs",
"files": [
"config/package.json",
"config/constants.js",
"config/scripts/postinstall.js",
"cli",
@@ -179,7 +275,7 @@
"cross-env": "^7.0.3",
"cross-fetch": "^4.0.0",
"cz-conventional-changelog": "3.3.0",
"esbuild": "^0.24.0",
"esbuild": "^0.25.3",
"esbuild-loader": "^4.2.2",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
@@ -192,16 +288,18 @@
"json-bigint": "^1.0.0",
"knip": "^5.51.1",
"lint-staged": "^15.2.10",
"msw": "workspace:*",
"page-with": "^0.6.1",
"prettier": "^3.4.2",
"publint": "^0.3.12",
"regenerator-runtime": "^0.14.1",
"rimraf": "^6.0.1",
"simple-git-hooks": "^2.9.0",
"tsup": "^8.3.0",
"tsup": "^8.4.0",
"typescript": "^5.5.2",
"undici": "^6.20.0",
"url-loader": "^4.1.1",
"vitest": "^2.1.8",
"vitest": "^3.1.3",
"vitest-environment-miniflare": "^2.14.4",
"webpack": "^5.95.0",
"webpack-http-server": "^0.5.0"
+530 -265
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -111,7 +111,7 @@ const standardGraphQLHandlers = {
* return HttpResponse.json({ data: { name: 'John' } })
* })
*
* @see {@link https://mswjs.io/docs/api/graphql#graphloperationresolver `graphql.operation()` API reference}
* @see {@link https://mswjs.io/docs/api/graphql#graphqloperationresolver `graphql.operation()` API reference}
*/
operation: createGraphQLOperationHandler('*'),
}
+3
View File
@@ -0,0 +1,3 @@
{
"type": "module"
}
@@ -1,4 +1,4 @@
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
import { waitFor } from '../../support/waitFor'
@@ -36,7 +36,7 @@ test('does not warn on anonymous GraphQL operation when no GraphQL handlers are
query,
spyOnConsole,
}) => {
await loadExample(require.resolve('./anonymous-operation.mocks.ts'))
await loadExample(new URL('./anonymous-operation.mocks.ts', import.meta.url))
const consoleSpy = spyOnConsole()
const endpointUrl = httpServer.http.url('/graphql')
@@ -85,7 +85,7 @@ test('warns on handled anonymous GraphQL operation', async ({
spyOnConsole,
page,
}) => {
await loadExample(require.resolve('./anonymous-operation.mocks.ts'))
await loadExample(new URL('./anonymous-operation.mocks.ts', import.meta.url))
const consoleSpy = spyOnConsole()
await page.evaluate(() => {
@@ -148,7 +148,7 @@ test('does not print a warning on anonymous GraphQL operation handled by "graphq
page,
query,
}) => {
await loadExample(require.resolve('./anonymous-operation.mocks.ts'))
await loadExample(new URL('./anonymous-operation.mocks.ts', import.meta.url))
const consoleSpy = spyOnConsole()
await page.evaluate(() => {
+1 -1
View File
@@ -7,7 +7,7 @@ test('sets cookie on the mocked GraphQL response', async ({
query,
page,
}) => {
await loadExample(require.resolve('./cookies.mocks.ts'))
await loadExample(new URL('./cookies.mocks.ts', import.meta.url))
const res = await query('/graphql', {
query: gql`
+1 -1
View File
@@ -2,7 +2,7 @@ import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
test('mocks a GraphQL error response', async ({ loadExample, query }) => {
await loadExample(require.resolve('./errors.mocks.ts'))
await loadExample(new URL('./errors.mocks.ts', import.meta.url))
const res = await query('/graphql', {
query: gql`
+1 -1
View File
@@ -5,7 +5,7 @@ test('mocks a GraphQL response with both data and extensions', async ({
loadExample,
query,
}) => {
await loadExample(require.resolve('./extensions.mocks.ts'))
await loadExample(new URL('./extensions.mocks.ts', import.meta.url))
const res = await query('/graphql', {
query: gql`
+2 -2
View File
@@ -1,8 +1,8 @@
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
const LINK_EXAMPLE = require.resolve('./link.mocks.ts')
const LINK_EXAMPLE = new URL('./link.mocks.ts', import.meta.url)
const server = new HttpServer((app) => {
app.post('/graphql', (req, res) => {
+1 -1
View File
@@ -3,7 +3,7 @@ import { waitFor } from '../../support/waitFor'
import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
const LOGGING_EXAMPLE = require.resolve('./logging.mocks.ts')
const LOGGING_EXAMPLE = new URL('./logging.mocks.ts', import.meta.url)
test('prints a log for a GraphQL query', async ({
loadExample,
@@ -4,7 +4,7 @@ test('accepts a file from a GraphQL mutation', async ({
loadExample,
query,
}) => {
await loadExample(require.resolve('./multipart-data.mocks.ts'))
await loadExample(new URL('./multipart-data.mocks.ts', import.meta.url))
const UPLOAD_MUTATION = `
mutation UploadFile(
+2 -2
View File
@@ -1,8 +1,8 @@
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
const MUTATION_EXAMPLE = require.resolve('./mutation.mocks.ts')
const MUTATION_EXAMPLE = new URL('./mutation.mocks.ts', import.meta.url)
const server = new HttpServer((app) => {
app.use('*', (req, res) => res.status(405).end())
@@ -1,7 +1,8 @@
import { test, expect } from '../playwright.extend'
const OPERATION_REFERENCE_EXAMPLE = require.resolve(
const OPERATION_REFERENCE_EXAMPLE = new URL(
'./operation-reference.mocks.ts',
import.meta.url,
)
test('allows referencing the request body in the GraphQL query handler', async ({
+2 -2
View File
@@ -1,8 +1,8 @@
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
const OPERATION_EXAMPLE = require.resolve('./operation.mocks.ts')
const OPERATION_EXAMPLE = new URL('./operation.mocks.ts', import.meta.url)
const server = new HttpServer((app) => {
app.post('/search', (req, res) => {
+2 -2
View File
@@ -1,8 +1,8 @@
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
const EXAMPLE_PATH = require.resolve('./query.mocks.ts')
const EXAMPLE_PATH = new URL('./query.mocks.ts', import.meta.url)
const server = new HttpServer((app) => {
app.use('*', (_, res) => res.status(405).end())
@@ -1,7 +1,7 @@
import type { ExecutionResult } from 'graphql'
import { buildSchema, graphql } from 'graphql'
import { SetupWorkerApi } from 'msw/browser'
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
import { gql } from '../../support/graphql'
@@ -50,7 +50,7 @@ test.afterEach(async () => {
})
test('patches a GraphQL response', async ({ loadExample, page }) => {
await loadExample(require.resolve('./response-patching.mocks.ts'))
await loadExample(new URL('./response-patching.mocks.ts', import.meta.url))
const endpointUrl = httpServer.http.url('/graphql')
await page.evaluate(() => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { gql } from '../../support/graphql'
import { test, expect } from '../playwright.extend'
const EXAMPLE_PATH = require.resolve('./variables.mocks.ts')
const EXAMPLE_PATH = new URL('./variables.mocks.ts', import.meta.url)
test('can access variables from a GraphQL query', async ({
loadExample,
+1 -1
View File
@@ -1,6 +1,6 @@
import { test, expect } from '../../playwright.extend'
const DELAY_EXAMPLE = require.resolve('./delay.mocks.ts')
const DELAY_EXAMPLE = new URL('./delay.mocks.ts', import.meta.url)
declare global {
namespace PlaywrightTest {
@@ -1,4 +1,4 @@
import * as fs from 'fs'
import fs from 'node:fs'
import { test, expect } from '../../playwright.extend'
test('supports the usage of the iife bundle in a <script> tag', async ({
@@ -7,13 +7,13 @@ test('supports the usage of the iife bundle in a <script> tag', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./iife.mocks.js'), {
await loadExample(new URL('./iife.mocks.js', import.meta.url), {
markup: `<script src="./iife/index.js"></script>`,
beforeNavigation(compilation) {
compilation.use((router) => {
router.get('/iife/index.js', (_, res) => {
fs.createReadStream(
require.resolve('../../../../lib/iife/index.js'),
new URL('../../../../lib/iife/index.js', import.meta.url),
).pipe(res)
return res
@@ -5,7 +5,7 @@ test('activates the worker without errors', async ({
spyOnConsole,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./exception-handling.mocks.ts'))
await loadExample(new URL('./exception-handling.mocks.ts', import.meta.url))
expect(consoleSpy.get('error')).toBeUndefined()
})
@@ -15,7 +15,7 @@ test('transforms uncaught exceptions into a 500 response', async ({
fetch,
spyOnConsole,
}) => {
await loadExample(require.resolve('./exception-handling.mocks.ts'))
await loadExample(new URL('./exception-handling.mocks.ts', import.meta.url))
const consoleSpy = spyOnConsole()
const res = await fetch('https://api.github.com/users/octocat')
+1 -1
View File
@@ -6,7 +6,7 @@ test('keeps the mocking enabled after hard-reload of the page', async ({
fetch,
waitForMswActivation,
}) => {
await loadExample(require.resolve('./hard-reload.mocks.ts'))
await loadExample(new URL('./hard-reload.mocks.ts', import.meta.url))
page.evaluate(() => {
/**
+30 -26
View File
@@ -1,10 +1,9 @@
import * as fs from 'fs'
import * as path from 'path'
import fs from 'node:fs'
import { test, expect } from '../playwright.extend'
import copyServiceWorker from '../../../config/copyServiceWorker'
import { version } from '../../../package.json'
const { SERVICE_WORKER_SOURCE_PATH } = require('../../../config/constants')
import packageJson from '../../../package.json' assert { type: 'json' }
// @ts-expect-error Importing a Javascript module.
import { SERVICE_WORKER_SOURCE_PATH } from '../../../config/constants.js'
test('activates the worker without errors given the latest integrity', async ({
loadExample,
@@ -12,7 +11,9 @@ test('activates the worker without errors given the latest integrity', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./integrity-check-valid.mocks.ts'))
await loadExample(
new URL('./integrity-check-valid.mocks.ts', import.meta.url),
)
expect(consoleSpy.get('error')).toBeUndefined()
@@ -31,10 +32,10 @@ test('errors when activating the worker with an outdated integrity', async ({
fetch,
waitFor,
}) => {
const TEMP_SERVICE_WORKER_PATH = path.resolve(
__dirname,
const TEMP_SERVICE_WORKER_PATH = new URL(
'../../tmp/mockServiceWorker-outdated.js',
)
import.meta.url,
).pathname
// Manually create a Service Worker file with invalid integrity
await copyServiceWorker(
@@ -44,31 +45,34 @@ test('errors when activating the worker with an outdated integrity', async ({
)
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./integrity-check-invalid.mocks.ts'), {
skipActivation: true,
beforeNavigation(compilation) {
compilation.use((router) => {
router.use('/', (_, res, next) => {
// Appended router are relative to the compilation path.
// Allow the nested worker script to control the root scope.
res.setHeader('Service-Worker-Allowed', '/')
next()
})
await loadExample(
new URL('./integrity-check-invalid.mocks.ts', import.meta.url),
{
skipActivation: true,
beforeNavigation(compilation) {
compilation.use((router) => {
router.use('/', (_, res, next) => {
// Appended router are relative to the compilation path.
// Allow the nested worker script to control the root scope.
res.setHeader('Service-Worker-Allowed', '/')
next()
})
router.get('/mockServiceWorker-outdated.js', (_, res) => {
return res
.set('content-type', 'application/javascript')
.send(fs.readFileSync(TEMP_SERVICE_WORKER_PATH, 'utf8'))
router.get('/mockServiceWorker-outdated.js', (_, res) => {
return res
.set('content-type', 'application/javascript')
.send(fs.readFileSync(TEMP_SERVICE_WORKER_PATH, 'utf8'))
})
})
})
},
},
})
)
await waitFor(() => {
// Produces a meaningful error in the browser's console.
expect(consoleSpy.get('warning')).toEqual(
expect.arrayContaining([
`[MSW] The currently registered Service Worker has been generated by a different version of MSW (${version}) and may not be fully compatible with the installed version.
`[MSW] The currently registered Service Worker has been generated by a different version of MSW (${packageJson.version}) and may not be fully compatible with the installed version.
It's recommended you update your worker script by running this command:
@@ -7,7 +7,7 @@ test('handles a stream response without throwing a timeout error', async ({
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./2129-worker-use.mocks.ts'))
await loadExample(new URL('./2129-worker-use.mocks.ts', import.meta.url))
const getResponse = await fetch('/v1/issues')
expect(await getResponse.text()).toBe('get-body')
@@ -20,7 +20,7 @@ test('handles a stream response without throwing a timeout error', async ({
})
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./handle-stream.mocks.ts'))
await loadExample(new URL('./handle-stream.mocks.ts', import.meta.url))
const getStreamResponse = () => {
return page.evaluate(
@@ -6,7 +6,7 @@ for (const code of [204, 205, 304]) {
fetch,
page,
}) => {
await loadExample(require.resolve('./null-body.mocks.ts'))
await loadExample(new URL('./null-body.mocks.ts', import.meta.url))
const errors: Array<Error> = []
page.on('pageerror', (pageError) => {
+1 -1
View File
@@ -2,7 +2,7 @@ import { HttpResponse, http, passthrough } from 'msw'
import { SetupWorkerApi } from 'msw/browser'
import { expect, test } from '../../playwright.extend'
const PASSTHROUGH_EXAMPLE = require.resolve('./passthrough.mocks.ts')
const PASSTHROUGH_EXAMPLE = new URL('./passthrough.mocks.ts', import.meta.url)
declare namespace window {
export const msw: {
@@ -2,7 +2,7 @@ import { test, expect } from '../../playwright.extend'
test('throws a network error', async ({ spyOnConsole, loadExample, page }) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./network-error.mocks.ts'))
await loadExample(new URL('./network-error.mocks.ts', import.meta.url))
// Do not use `runtime.request()`, because it always awaits a response.
// In this case we await a network error, performing a request manually.
@@ -1,7 +1,7 @@
import { SetupWorkerApi } from 'msw/browser'
import { createTeardown } from 'fs-teardown'
import { Page } from '@playwright/test'
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { fromTemp } from '../../../../support/utils'
import { test, expect } from '../../../playwright.extend'
@@ -63,7 +63,7 @@ test.beforeAll(async () => {
test.beforeEach(async ({ webpackServer }, testInfo) => {
const compilation = await webpackServer.compile([
require.resolve('./fallback-mode.mocks.ts'),
new URL('./fallback-mode.mocks.ts', import.meta.url).pathname,
])
const bundleUrl = new URL('./main.js', compilation.previewUrl)
await fsMock.create({
@@ -5,7 +5,7 @@ test('throws an error given an Array of request handlers to "setupWorker"', asyn
page,
waitFor,
}) => {
await loadExample(require.resolve('./input-validation.mocks.ts'), {
await loadExample(new URL('./input-validation.mocks.ts', import.meta.url), {
skipActivation: true,
})
@@ -1,5 +1,5 @@
import type { SetupWorker } from 'msw/browser'
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import type { ConsoleMessages } from 'page-with'
import { test, expect } from '../../../playwright.extend'
@@ -9,7 +9,7 @@ declare namespace window {
}
}
const ON_EXAMPLE = require.resolve('./on.mocks.ts')
const ON_EXAMPLE = new URL('./on.mocks.ts', import.meta.url)
const server = new HttpServer((app) => {
app.post('/no-response', (_req, res) => {
@@ -7,7 +7,7 @@ declare namespace window {
}
}
const ON_EXAMPLE = require.resolve('./on.mocks.ts')
const ON_EXAMPLE = new URL('./on.mocks.ts', import.meta.url)
test('removes all listeners attached to the worker instance', async ({
loadExample,
@@ -17,7 +17,7 @@ test('removes a listener by the event name', async ({
makeUrl,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./on.mocks.ts'))
await loadExample(new URL('./on.mocks.ts', import.meta.url))
await page.evaluate(() => {
const { msw } = window
@@ -10,7 +10,7 @@ declare namespace window {
}
}
const LIST_HANDLER_EXAMPLE = require.resolve('./listHandlers.mocks.ts')
const LIST_HANDLER_EXAMPLE = new URL('./listHandlers.mocks.ts', import.meta.url)
test('lists all current request handlers', async ({ loadExample, page }) => {
await loadExample(LIST_HANDLER_EXAMPLE)
@@ -11,7 +11,7 @@ declare namespace window {
}
}
const USE_EXAMPLE = require.resolve('./use.mocks.ts')
const USE_EXAMPLE = new URL('./use.mocks.ts', import.meta.url)
test('removes all runtime request handlers when resetting without explicit next handlers', async ({
loadExample,
@@ -12,7 +12,7 @@ test('prints the response info to the console', async ({
fetch,
waitFor,
}) => {
await loadExample(require.resolve('../../rest-api/basic.mocks.ts'))
await loadExample(new URL('../../rest-api/basic.mocks.ts', import.meta.url))
const consoleSpy = spyOnConsole()
const waitForResponseLog = async (exp: RegExp) => {
@@ -15,7 +15,7 @@ test('returns a mocked response from the used one-time request handler when rest
page,
fetch,
}) => {
await loadExample(require.resolve('./use.mocks.ts'))
await loadExample(new URL('./use.mocks.ts', import.meta.url))
await page.evaluate(() => {
const { worker, http, HttpResponse } = window.msw
@@ -5,7 +5,7 @@ test('uses a custom transformer to parse BigInt in response body', async ({
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./custom-transformers.mocks.ts'))
await loadExample(new URL('./custom-transformers.mocks.ts', import.meta.url))
const res = await fetch('/user')
const body = await res.text()
@@ -8,7 +8,7 @@ test('propagates the exception originating from a handled request', async ({
makeUrl,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./internal-error.mocks.ts'))
await loadExample(new URL('./internal-error.mocks.ts', import.meta.url))
const endpointUrl = makeUrl('/user')
const res = await fetch(endpointUrl)
@@ -10,7 +10,7 @@ test('propagates a mocked network error', async ({
}) => {
const consoleSpy = spyOnConsole()
const { workerConsole } = await loadExample(
require.resolve('./network-error.mocks.ts'),
new URL('./network-error.mocks.ts', import.meta.url),
)
const endpointUrl = makeUrl('/user')
@@ -34,7 +34,7 @@ test('propagates a CORS violation error from a non-matching request', async ({
waitFor,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./network-error.mocks.ts'))
await loadExample(new URL('./network-error.mocks.ts', import.meta.url))
await until(() => page.evaluate(() => fetch('/user')))
@@ -6,7 +6,7 @@ test('falls through all relevant request handlers until response is returned', a
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./fall-through.mocks.ts'))
await loadExample(new URL('./fall-through.mocks.ts', import.meta.url))
const res = await fetch('/user')
const body = await res.json()
@@ -29,7 +29,7 @@ test('falls through all relevant handler even if none returns response', async (
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./fall-through.mocks.ts'))
await loadExample(new URL('./fall-through.mocks.ts', import.meta.url))
const res = await fetch('/blog/article', {
method: 'POST',
@@ -1,10 +1,9 @@
import * as path from 'path'
import * as express from 'express'
import type { Frame, Page } from '@playwright/test'
import { test, expect } from '../../../../playwright.extend'
const staticMiddleware = (router: express.Router) => {
router.use(express.static(__dirname))
router.use(express.static(new URL('./', import.meta.url).pathname))
}
export function getFrameById(id: string, page: Page): Frame {
@@ -24,12 +23,15 @@ test('responds with different responses for the same request based on request re
loadExample,
page,
}) => {
await loadExample(require.resolve('./iframe-isolated-response.mocks.ts'), {
markup: path.resolve(__dirname, 'app.html'),
beforeNavigation(compilation) {
compilation.use(staticMiddleware)
await loadExample(
new URL('./iframe-isolated-response.mocks.ts', import.meta.url),
{
markup: new URL('app.html', import.meta.url).pathname,
beforeNavigation(compilation) {
compilation.use(staticMiddleware)
},
},
})
)
const frameOne = getFrameById('frame-one', page)!
const frameTwo = getFrameById('frame-two', page)!
@@ -1,4 +1,3 @@
import * as path from 'path'
import { Frame } from '@playwright/test'
import * as express from 'express'
import { test, expect } from '../../../../playwright.extend'
@@ -14,15 +13,15 @@ function findFrame(frame: Frame) {
}
const staticMiddleware = (router: express.Router) => {
router.use(express.static(__dirname))
router.use(express.static(new URL('./', import.meta.url).pathname))
}
test('intercepts a request from an iframe (nested client)', async ({
loadExample,
page,
}) => {
await loadExample(require.resolve('./iframe.mocks.ts'), {
markup: path.resolve(__dirname, 'page-in-iframe.html'),
await loadExample(new URL('./iframe.mocks.ts', import.meta.url), {
markup: new URL('page-in-iframe.html', import.meta.url).pathname,
beforeNavigation(compilation) {
compilation.use(staticMiddleware)
},
@@ -41,8 +40,8 @@ test('intercepts a request from a deeply nested iframe', async ({
loadExample,
page,
}) => {
await loadExample(require.resolve('./iframe.mocks.ts'), {
markup: path.resolve(__dirname, 'page-in-nested-iframe.html'),
await loadExample(new URL('./iframe.mocks.ts', import.meta.url), {
markup: new URL('page-in-nested-iframe.html', import.meta.url).pathname,
beforeNavigation(compilation) {
compilation.use(staticMiddleware)
},
@@ -67,8 +66,8 @@ test('intercepts a request from a deeply nested iframe given MSW is registered i
loadExample,
page,
}) => {
await loadExample(require.resolve('./iframe.mocks.ts'), {
markup: path.resolve(__dirname, 'page-in-iframe.html'),
await loadExample(new URL('./iframe.mocks.ts', import.meta.url), {
markup: new URL('page-in-iframe.html', import.meta.url).pathname,
beforeNavigation(compilation) {
compilation.use(staticMiddleware)
},
@@ -95,12 +94,12 @@ test('intercepts a request from an iframe given MSW is registered in a sibling i
context,
}) => {
// A frame that registers MSW, but does no requests.
await loadExample(require.resolve('./iframe.mocks.ts'))
await loadExample(new URL('./iframe.mocks.ts', import.meta.url))
// A request-issuing frame. Here lives the `window.fetch` call.
const requestPage = await context.newPage()
const requestCompilation = await webpackServer.compile([], {
markup: path.resolve(__dirname, 'page-in-iframe.html'),
markup: new URL('page-in-iframe.html', import.meta.url).pathname,
})
requestCompilation.use(staticMiddleware)
await requestPage.goto(requestCompilation.previewUrl)
@@ -5,10 +5,10 @@ test('intercepts a request issued by child frame when both child and parent have
page,
}) => {
const parentCompilation = await webpackServer.compile([
require.resolve('./parent.mocks.ts'),
new URL('./parent.mocks.ts', import.meta.url).pathname,
])
const childCompilation = await webpackServer.compile([
require.resolve('./child.mocks.ts'),
new URL('./child.mocks.ts', import.meta.url).pathname,
])
await page.goto(parentCompilation.previewUrl, { waitUntil: 'networkidle' })
@@ -1,8 +1,6 @@
import { test, expect } from '../../../../playwright.extend'
const {
SERVICE_WORKER_BUILD_PATH,
} = require('../../../../../../config/constants')
// @ts-expect-error Importing a JavaScript module.
import { SERVICE_WORKER_BUILD_PATH } from '../../../../../../config/constants.js'
test('warns when visiting the page outside of the worker scope', async ({
loadExample,
@@ -10,7 +8,7 @@ test('warns when visiting the page outside of the worker scope', async ({
}) => {
const consoleSpy = spyOnConsole()
const { compilation } = await loadExample(
require.resolve('./scope-nested.mocks.ts'),
new URL('./scope-nested.mocks.ts', import.meta.url),
{
beforeNavigation(compilation) {
// Create a proxy to the worker script located under a nested ("/public") path.
@@ -40,7 +38,7 @@ test('does not print the scope warning when the page is within the worker scope'
spyOnConsole,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./scope-root.mocks.ts'))
await loadExample(new URL('./scope-root.mocks.ts', import.meta.url))
expect(consoleSpy.get('warning')).toEqual(undefined)
expect(consoleSpy.get('error')).toEqual(undefined)
@@ -51,7 +49,7 @@ test('does not print the scope warning when the "quiet" option is enabled', asyn
spyOnConsole,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./scope-nested-quiet.mocks.ts'), {
await loadExample(new URL('./scope-nested-quiet.mocks.ts', import.meta.url), {
skipActivation: true,
beforeNavigation(compilation) {
// Create a proxy to the worker script located under a nested ("/public") path.
@@ -11,10 +11,10 @@ test('does not interfere with a shared worker', async ({
page,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./shared-worker.mocks.ts'), {
await loadExample(new URL('./shared-worker.mocks.ts', import.meta.url), {
beforeNavigation(compilation) {
compilation.use((router) => {
router.use(express.static(__dirname))
router.use(express.static(new URL('./', import.meta.url).pathname))
})
},
})
@@ -26,7 +26,7 @@ test('bypasses the unhandled request with the "Accept" header containing "text/e
})
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./text-event-stream.mocks.ts'))
await loadExample(new URL('./text-event-stream.mocks.ts', import.meta.url))
await page.evaluate((endpointUrl) => {
const source = new EventSource(endpointUrl)
@@ -11,7 +11,7 @@ test('rejects with a custom error message when given a non-existing worker scrip
loadExample,
page,
}) => {
await loadExample(require.resolve('./error.mocks.ts'), {
await loadExample(new URL('./error.mocks.ts', import.meta.url), {
skipActivation: true,
})
@@ -13,7 +13,7 @@ test('resolves the "start" Promise and returns a ServiceWorkerRegistration when
page,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./find-worker.mocks.ts'))
await loadExample(new URL('./find-worker.mocks.ts', import.meta.url))
await page.waitForFunction(() => {
return typeof window.msw !== 'undefined'
@@ -46,7 +46,7 @@ test('fails to return a ServiceWorkerRegistration when using a findWorker that r
page,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./find-worker.error.mocks.ts'), {
await loadExample(new URL('./find-worker.error.mocks.ts', import.meta.url), {
skipActivation: true,
})
@@ -6,7 +6,7 @@ test('bypasses an unhandled request', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./bypass.mocks.ts'))
await loadExample(new URL('./bypass.mocks.ts', import.meta.url))
const res = await fetch('https://mswjs.io/non-existing-page')
const status = res.status()
@@ -6,7 +6,7 @@ test('executes a default "warn" strategy in a custom callback', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./callback-print.mocks.ts'))
await loadExample(new URL('./callback-print.mocks.ts', import.meta.url))
const res = await fetch('https://mswjs.io/use-warn')
const status = res.status()
@@ -40,7 +40,7 @@ test('executes a default "error" strategy in a custom callback', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./callback-print.mocks.ts'))
await loadExample(new URL('./callback-print.mocks.ts', import.meta.url))
const res = await fetch('https://mswjs.io/use-error')
const status = res.status()
@@ -6,7 +6,7 @@ test('executes a given callback on an unhandled request', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./callback.mocks.ts'))
await loadExample(new URL('./callback.mocks.ts', import.meta.url))
const res = await fetch('https://mswjs.io/non-existing-page')
const status = res.status()
@@ -6,7 +6,7 @@ test('warns on unhandled requests by default', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./default.mocks.ts'))
await loadExample(new URL('./default.mocks.ts', import.meta.url))
const res = await fetch('https://mswjs.io/non-existing-page')
const status = res.status()
@@ -6,7 +6,7 @@ test('warns on an unhandled REST API request with an absolute URL', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./warn.mocks.ts'))
await loadExample(new URL('./warn.mocks.ts', import.meta.url))
const res = await fetch('https://mswjs.io/non-existing-page')
const status = res.status()
@@ -31,7 +31,7 @@ test('warns on an unhandled REST API request with a relative URL', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./warn.mocks.ts'))
await loadExample(new URL('./warn.mocks.ts', import.meta.url))
const res = await fetch('/user-details')
const status = res.status()
@@ -56,7 +56,7 @@ test('does not warn on request which handler explicitly returns no mocked respon
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./warn.mocks.ts'))
await loadExample(new URL('./warn.mocks.ts', import.meta.url))
const res = await fetch('/explicit-return', { method: 'POST' })
const status = res.status()
@@ -77,7 +77,7 @@ test('does not warn on request which handler implicitly returns no mocked respon
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./warn.mocks.ts'))
await loadExample(new URL('./warn.mocks.ts', import.meta.url))
const res = await fetch('/implicit-return', { method: 'POST' })
const status = res.status()
@@ -98,7 +98,7 @@ test('ignores common static assets when using the "warn" strategy', async ({
page,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./warn.mocks.ts'))
await loadExample(new URL('./warn.mocks.ts', import.meta.url))
// This request will error so perform it accordingly.
await page.evaluate(() => {
@@ -1,4 +1,3 @@
import * as path from 'path'
import { test, expect } from '../../../playwright.extend'
test('respects a custom "scope" Service Worker option', async ({
@@ -7,7 +6,7 @@ test('respects a custom "scope" Service Worker option', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(path.resolve(__dirname, 'options-sw-scope.mocks.ts'))
await loadExample(new URL('options-sw-scope.mocks.ts', import.meta.url))
expect(consoleSpy.get('startGroupCollapsed')).toEqual(
expect.arrayContaining([expect.stringContaining('[MSW] Mocking enabled.')]),
@@ -14,7 +14,7 @@ test('does not log the intercepted request when the "quiet" option is set to "tr
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./quiet.mocks.ts'), {
await loadExample(new URL('./quiet.mocks.ts', import.meta.url), {
// Using the "quiet" option to suppress the activation message.
skipActivation: true,
})
@@ -1,4 +1,3 @@
import * as path from 'path'
import { SetupWorkerApi } from 'msw/browser'
import { TestFixtures, test, expect } from '../../../playwright.extend'
@@ -9,13 +8,13 @@ declare namespace window {
}
const exampleOptions: Parameters<TestFixtures['loadExample']> = [
require.resolve('./start.mocks.ts'),
new URL('./start.mocks.ts', import.meta.url),
{
skipActivation: true,
beforeNavigation(compilation) {
compilation.use((router) => {
router.get('/worker.js', (_, res) => {
res.sendFile(path.resolve(__dirname, 'worker.delayed.js'))
res.sendFile(new URL('worker.delayed.js', import.meta.url).pathname)
})
})
},
@@ -6,7 +6,9 @@ test('warns on the "waitUntilReady" option in "worker.start()"', async ({
spyOnConsole,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./warn-on-wait-until-ready.mocks.ts'))
await loadExample(
new URL('./warn-on-wait-until-ready.mocks.ts', import.meta.url),
)
await waitFor(() => {
expect(consoleSpy.get('warning')).toEqual([
@@ -39,7 +39,7 @@ test('disables the mocking when the worker is stopped', async ({
fetch,
page,
}) => {
await loadExample(require.resolve('./stop.mocks.ts'))
await loadExample(new URL('./stop.mocks.ts', import.meta.url))
await stopWorkerOn(page)
const res = await fetch('https://api.github.com')
@@ -56,7 +56,9 @@ test('keeps the mocking enabled in one tab when stopping the worker in another t
context,
fetch,
}) => {
const { compilation } = await loadExample(require.resolve('./stop.mocks.ts'))
const { compilation } = await loadExample(
new URL('./stop.mocks.ts', import.meta.url),
)
const firstPage = await context.newPage()
await firstPage.goto(compilation.previewUrl, {
@@ -90,7 +92,7 @@ test('prints a warning on multiple "worker.stop()" calls', async ({
page,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./stop.mocks.ts'))
await loadExample(new URL('./stop.mocks.ts', import.meta.url))
function byStopMessage(text: string): boolean {
return text === '[MSW] Mocking disabled.'
@@ -7,7 +7,7 @@ declare namespace window {
}
}
const QUIET_EXAMPLE = require.resolve('./quiet.mocks.ts')
const QUIET_EXAMPLE = new URL('./quiet.mocks.ts', import.meta.url)
test('prints out the console stop message', async ({
loadExample,
@@ -15,9 +15,12 @@ test('removes all listeners when the worker is stopped', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./removes-all-listeners.mocks.ts'), {
skipActivation: true,
})
await loadExample(
new URL('./removes-all-listeners.mocks.ts', import.meta.url),
{
skipActivation: true,
},
)
await page.waitForFunction(() => {
return typeof window.msw !== 'undefined'
@@ -15,7 +15,7 @@ test('returns a mocked response from a runtime request handler upon match', asyn
fetch,
page,
}) => {
await loadExample(require.resolve('./use.mocks.ts'))
await loadExample(new URL('./use.mocks.ts', import.meta.url))
await page.evaluate(() => {
const { worker, http, HttpResponse } = window.msw
@@ -48,7 +48,7 @@ test('returns a mocked response from a persistent request handler override', asy
fetch,
page,
}) => {
await loadExample(require.resolve('./use.mocks.ts'))
await loadExample(new URL('./use.mocks.ts', import.meta.url))
await page.evaluate(() => {
const { worker, http, HttpResponse } = window.msw
@@ -78,7 +78,7 @@ test('returns a mocked response from a one-time request handler override only up
fetch,
page,
}) => {
await loadExample(require.resolve('./use.mocks.ts'))
await loadExample(new URL('./use.mocks.ts', import.meta.url))
await page.evaluate(() => {
const { worker, http, HttpResponse } = window.msw
@@ -112,7 +112,7 @@ test('returns a mocked response from a one-time request handler override only up
fetch,
page,
}) => {
await loadExample(require.resolve('./use.mocks.ts'))
await loadExample(new URL('./use.mocks.ts', import.meta.url))
await page.evaluate(() => {
const { worker, http, HttpResponse } = window.msw
@@ -1,4 +1,4 @@
import { HttpServer } from '@open-draft/test-server/lib/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../../playwright.extend'
const httpServer = new HttpServer((app) => {
@@ -20,7 +20,9 @@ test('removes the internal passthrough request header', async ({
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./worker-passthrough-header.mocks.ts'))
await loadExample(
new URL('./worker-passthrough-header.mocks.ts', import.meta.url),
)
const response = await fetch(httpServer.http.url('/resource'), {
headers: { 'x-custom-header': 'yes' },
@@ -39,7 +41,9 @@ test('preserves existing "accept" header values when removing the internal passt
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./worker-passthrough-header.mocks.ts'))
await loadExample(
new URL('./worker-passthrough-header.mocks.ts', import.meta.url),
)
const response = await fetch(httpServer.http.url('/resource'), {
headers: {
+1 -1
View File
@@ -14,7 +14,7 @@ test('unregisters itself when not prompted to be activated again', async ({
waitForMswActivation,
}) => {
const { compilation } = await loadExample(
require.resolve('./unregister.mocks.ts'),
new URL('./unregister.mocks.ts', import.meta.url),
{
skipActivation: true,
beforeNavigation(compilation) {
+3 -6
View File
@@ -1,7 +1,6 @@
import { PlaywrightTestConfig, devices } from '@playwright/test'
import { type PlaywrightTestConfig, devices } from '@playwright/test'
const config: PlaywrightTestConfig = {
testDir: __dirname,
export default {
projects: [
{
name: 'chromium',
@@ -26,6 +25,4 @@ const config: PlaywrightTestConfig = {
// and there's no shared state leaking from the shared compilation
// server or the preview server.
fullyParallel: true,
}
export default config
} satisfies PlaywrightTestConfig
+11 -4
View File
@@ -1,4 +1,5 @@
import * as crypto from 'crypto'
import url from 'node:url'
import crypto from 'node:crypto'
import { test as base, expect, type Response, Page } from '@playwright/test'
import {
Headers,
@@ -7,7 +8,10 @@ import {
type FlatHeadersObject,
} from 'headers-polyfill'
import { spyOnConsole, ConsoleMessages } from 'page-with'
import { HttpServer, HttpServerMiddleware } from '@open-draft/test-server/http'
import {
HttpServer,
HttpServerMiddleware,
} from '@open-draft/test-server/lib/http.js'
import {
Compilation,
CompilationOptions,
@@ -24,7 +28,7 @@ export interface TestFixtures {
createServer(...middleware: Array<HttpServerMiddleware>): Promise<HttpServer>
webpackServer: WebpackHttpServer
loadExample(
entry: string | Array<string>,
entry: string | Array<string> | URL,
options?: CompilationOptions & {
/**
* Do not await the "Mocking enabled" message in the console.
@@ -88,8 +92,11 @@ export const test = base.extend<TestFixtures>({
let compilation: Compilation | undefined
await use(async (entry, options = {}) => {
const resolvedEntry =
entry instanceof URL ? url.fileURLToPath(entry) : entry
compilation = await webpackServer.compile(
Array.prototype.concat([], entry),
Array.prototype.concat([], resolvedEntry),
options,
)
+2 -2
View File
@@ -1,4 +1,4 @@
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
const httpServer = new HttpServer((app) => {
@@ -24,7 +24,7 @@ test('handles a 204 status response without Response instance exceptions', async
fetch,
page,
}) => {
await loadExample(require.resolve('./basic.mocks.ts'))
await loadExample(new URL('./basic.mocks.ts', import.meta.url))
let pageError: Error
+3 -3
View File
@@ -1,7 +1,7 @@
/**
* @see https://github.com/mswjs/msw/issues/1972
*/
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { expect, test } from '../playwright.extend'
const encoder = new TextEncoder()
@@ -46,7 +46,7 @@ test('forwards the 206 response to a bypassed "Range" request', async ({
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./206-response.mocks.ts'))
await loadExample(new URL('./206-response.mocks.ts', import.meta.url))
const response = await fetch(server.http.url('/range'), {
headers: {
@@ -68,7 +68,7 @@ test('responds with a 206 response to a mocked "Range" request', async ({
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./206-response.mocks.ts'))
await loadExample(new URL('./206-response.mocks.ts', import.meta.url))
const response = await fetch('/mocked-range', {
headers: {
+1 -1
View File
@@ -1,7 +1,7 @@
import { test, expect } from '../playwright.extend'
test('mocks response to a GET request', async ({ loadExample, fetch }) => {
await loadExample(require.resolve('./basic.mocks.ts'))
await loadExample(new URL('./basic.mocks.ts', import.meta.url))
const response = await fetch('https://example.com/users/octocat')
const status = response.status()
+1 -1
View File
@@ -1,6 +1,6 @@
import { test, expect } from '../playwright.extend'
const EXAMPLE_PATH = require.resolve('./body.mocks.ts')
const EXAMPLE_PATH = new URL('./body.mocks.ts', import.meta.url)
test('handles a GET request without a body', async ({ loadExample, fetch }) => {
await loadExample(EXAMPLE_PATH)
+1 -1
View File
@@ -4,7 +4,7 @@ test('composes various context utilities into a valid mocked response', async ({
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./context.mocks.ts'))
await loadExample(new URL('./context.mocks.ts', import.meta.url))
const res = await fetch('https://test.mswjs.io/')
const headers = await res.allHeaders()
+2 -2
View File
@@ -1,4 +1,4 @@
import { HttpServer } from '@open-draft/test-server/http'
import { HttpServer } from '@open-draft/test-server/lib/http.js'
import { test, expect } from '../playwright.extend'
const server = new HttpServer((app) => {
@@ -27,7 +27,7 @@ test('handles a CORS request with an "opaque" response', async ({
fetch,
page,
}) => {
await loadExample(require.resolve('./cors.mocks.ts'))
await loadExample(new URL('./cors.mocks.ts', import.meta.url))
const errors = []
page.on('pageerror', (error) => errors.push(error))
+2 -2
View File
@@ -13,7 +13,7 @@ test('supports a generator function as the response resolver', async ({
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./generator.mocks.ts'))
await loadExample(new URL('./generator.mocks.ts', import.meta.url))
const assertRequest = async (expectedBody: ExpectedResponseBody) => {
const res = await fetch('/polling/3')
@@ -35,7 +35,7 @@ test('supports one-time handlers with the generator as the response resolver', a
loadExample,
fetch,
}) => {
await loadExample(require.resolve('./generator.mocks.ts'))
await loadExample(new URL('./generator.mocks.ts', import.meta.url))
const assertRequest = async (expectedBody: ExpectedResponseBody) => {
const res = await fetch('/polling/once/3')
@@ -1,6 +1,6 @@
import { test, expect } from '../playwright.extend'
const EXAMPLE_PATH = require.resolve('./headers-multiple.mocks.ts')
const EXAMPLE_PATH = new URL('./headers-multiple.mocks.ts', import.meta.url)
test('receives all headers from the request header with multiple values', async ({
loadExample,
+1 -1
View File
@@ -8,7 +8,7 @@ test('prints the intercepted request info into browser console', async ({
fetch,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./basic.mocks.ts'))
await loadExample(new URL('./basic.mocks.ts', import.meta.url))
await fetch('https://example.com/users/octocat')
+1 -1
View File
@@ -1,7 +1,7 @@
import { test, expect } from '../playwright.extend'
test('parses request URL parameters', async ({ loadExample, fetch }) => {
await loadExample(require.resolve('./params.mocks.ts'))
await loadExample(new URL('./params.mocks.ts', import.meta.url))
const res = await fetch(
'https://api.github.com/users/octocat/messages/abc-123',
+1 -1
View File
@@ -5,7 +5,7 @@ test('returns a plain Response as a mocked response', async ({
fetch,
spyOnConsole,
}) => {
await loadExample(require.resolve('./plain-response.mocks.ts'))
await loadExample(new URL('./plain-response.mocks.ts', import.meta.url))
const consoleSpy = spyOnConsole()
const response = await fetch('/greeting')
@@ -6,7 +6,7 @@ test('warns when a request handler URL contains query parameters', async ({
spyOnConsole,
}) => {
const consoleSpy = spyOnConsole()
await loadExample(require.resolve('./query-params-warning.mocks.ts'))
await loadExample(new URL('./query-params-warning.mocks.ts', import.meta.url))
expect(consoleSpy.get('warning')).toEqual([
`[MSW] Found a redundant usage of query parameters in the request handler URL for "GET /user?name=admin". Please match against a path instead and access query parameters using \"new URL(request.url).searchParams\" instead. Learn more: https://mswjs.io/docs/recipes/query-parameters`,
+1 -1
View File
@@ -1,6 +1,6 @@
import { test, expect } from '../playwright.extend'
const EXAMPLE_PATH = require.resolve('./query.mocks.ts')
const EXAMPLE_PATH = new URL('./query.mocks.ts', import.meta.url)
test('retrieves a single request URL query parameter', async ({
loadExample,
+1 -1
View File
@@ -6,7 +6,7 @@ test('supports redirect in a mocked response', async ({
makeUrl,
page,
}) => {
await loadExample(require.resolve('./redirect.mocks.ts'))
await loadExample(new URL('./redirect.mocks.ts', import.meta.url))
const [res, redirectRes] = await Promise.all([
await fetch('/login'),

Some files were not shown because too many files have changed in this diff Show More