Fake XHR and server for testing JavaScript applications
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Low-level XMLHttpRequest replacement providing complete control over the request/response lifecycle, response types, and progress events. This gives you fine-grained control over every aspect of HTTP request simulation.
Enable fake XMLHttpRequest globally or for specific scopes.
/**
* @typedef {Object} FakeXHRModule
* @property {XHRUtilities} xhr - XHR utilities
* @property {Function} FakeXMLHttpRequest - FakeXMLHttpRequest constructor
* @property {Function} useFakeXMLHttpRequest - Function that returns FakeXMLHttpRequest constructor
*/
/**
* Replace global XMLHttpRequest with fake implementation
* @returns {Function} FakeXMLHttpRequest constructor
*/
function useFakeXMLHttpRequest() {
// Implementation
}
/**
* Create fake XMLHttpRequest for specific global scope
* @param {Object} globalScope - Global object (window in browser, global in Node.js)
* @returns {FakeXHRModule} Object with xhr utilities and FakeXMLHttpRequest
*/
function fakeXMLHttpRequestFor(globalScope) {
// Implementation
}Usage Example:
const nise = require("nise");
// Replace global XMLHttpRequest
const FakeXHR = nise.fakeXhr.useFakeXMLHttpRequest();
// Now all XMLHttpRequest usage in your code uses the fake
const xhr = new XMLHttpRequest(); // Actually creates FakeXMLHttpRequest
// Restore original later
FakeXHR.restore();Create individual fake XMLHttpRequest instances.
/**
* @typedef {Object} FakeXHRConfig
* @property {function(string): void} [logger] - Custom logging function
* @property {boolean} [useImmediateExceptions] - Throw errors immediately vs on next tick
* @property {function(Function, number): *} [setTimeout] - Custom setTimeout function
*/
/**
* Create a new fake XMLHttpRequest instance
* @param {FakeXHRConfig} [config] - Optional configuration
*/
function FakeXMLHttpRequest(config) {
// Implementation
}Complete XMLHttpRequest API implementation.
/**
* Initialize a request
* @param {string} method - HTTP method
* @param {string} url - Request URL
* @param {boolean} [async] - Async flag (default: true)
* @param {string} [username] - Optional username for authentication
* @param {string} [password] - Optional password for authentication
* @returns {void}
*/
open(method, url, async, username, password) {
// Implementation
}
/**
* Send the request
* @param {string|FormData|ArrayBuffer} [data] - Optional request body data
* @returns {void}
*/
send(data) {
// Implementation
}
/**
* Set a request header
* @param {string} header - Header name
* @param {string} value - Header value (must be string)
* @returns {void}
*/
setRequestHeader(header, value) {
// Implementation
}
/**
* Get a response header value
* @param {string} header - Header name
* @returns {string|null} Header value or null if not found
*/
getResponseHeader(header) {
// Implementation
}
/**
* Get all response headers as a string
* @returns {string} All headers in HTTP format
*/
getAllResponseHeaders() {
// Implementation
}
/**
* Abort the current request
* @returns {void}
*/
abort() {
// Implementation
}
/**
* Override the MIME type of the response
* @param {string} type - MIME type to use
* @returns {void}
*/
overrideMimeType(type) {
// Implementation
}All standard XMLHttpRequest properties are supported.
/**
* @typedef {Object} FakeXMLHttpRequest
* @property {0} UNSENT - State constant
* @property {1} OPENED - State constant
* @property {2} HEADERS_RECEIVED - State constant
* @property {3} LOADING - State constant
* @property {4} DONE - State constant
* @property {number} readyState - Current state
* @property {number} status - HTTP status code
* @property {string} statusText - HTTP status text
* @property {string} responseURL - Response URL
* @property {*} response - Response data
* @property {string} responseText - Response as text
* @property {Document|null} responseXML - Response as XML document
* @property {string} responseType - Expected response type ("", "text", "json", "blob", "arraybuffer", "document")
* @property {number} timeout - Request timeout in milliseconds (if supported by platform)
* @property {boolean} withCredentials - Whether to send credentials (if CORS is supported)
* @property {XMLHttpRequestUpload} upload - Upload event target
* @property {function(Event): void|null} onreadystatechange - Ready state change event handler
* @property {function(ProgressEvent): void|null} onloadstart - Load start event handler
* @property {function(ProgressEvent): void|null} onprogress - Progress event handler
* @property {function(ProgressEvent): void|null} onload - Load event handler
* @property {function(ProgressEvent): void|null} onloadend - Load end event handler
* @property {function(ProgressEvent): void|null} onerror - Error event handler
* @property {function(ProgressEvent): void|null} onabort - Abort event handler
* @property {function(ProgressEvent): void|null} ontimeout - Timeout event handler
*/Additional methods for controlling the fake XMLHttpRequest behavior.
/**
* Set the response status code
* @param {number} status - HTTP status code
* @returns {void}
*/
setStatus(status) {
// Implementation
}
/**
* Set response headers
* @param {Object.<string, string>} headers - Object with header name/value pairs
* @returns {void}
*/
setResponseHeaders(headers) {
// Implementation
}
/**
* Set the response body
* @param {string|ArrayBuffer|Blob} body - Response body data
* @returns {void}
*/
setResponseBody(body) {
// Implementation
}
/**
* Set complete response (status, headers, and body)
* @param {number} status - HTTP status code
* @param {Object.<string, string>} headers - Response headers object
* @param {string|ArrayBuffer} body - Response body
* @returns {void}
*/
respond(status, headers, body) {
// Implementation
}
/**
* Trigger an error state
* @returns {void}
*/
error() {
// Implementation
}
/**
* Trigger a timeout
* @returns {void}
*/
triggerTimeout() {
// Implementation
}Usage Example:
const nise = require("nise");
// Create fake XHR instance
const xhr = new nise.fakeXhr.FakeXMLHttpRequest();
// Set up event handlers
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
console.log(`Response: ${xhr.responseText}`);
}
};
// Initialize and send request
xhr.open("GET", "/api/data");
xhr.send();
// Simulate response
xhr.respond(200, { "Content-Type": "application/json" }, '{"data": "test"}');Simulate upload and download progress events.
/**
* @typedef {Object} ProgressEventInit
* @property {number} loaded - Bytes loaded
* @property {number} total - Total bytes
*/
/**
* Simulate upload progress
* @param {ProgressEventInit} progressEventRaw - Progress event data
* @returns {void}
*/
uploadProgress(progressEventRaw) {
// Implementation
}
/**
* Simulate download progress
* @param {ProgressEventInit} progressEventRaw - Progress event data
* @returns {void}
*/
downloadProgress(progressEventRaw) {
// Implementation
}
/**
* Simulate upload error
* @param {*} error - Error details
* @returns {void}
*/
uploadError(error) {
// Implementation
}Usage Example:
const xhr = new nise.fakeXhr.FakeXMLHttpRequest();
xhr.upload.onprogress = function(event) {
console.log(`Upload: ${event.loaded}/${event.total}`);
};
xhr.onprogress = function(event) {
console.log(`Download: ${event.loaded}/${event.total}`);
};
xhr.open("POST", "/upload");
xhr.send(new FormData());
// Simulate upload progress
xhr.uploadProgress({ loaded: 50, total: 100 });
xhr.uploadProgress({ loaded: 100, total: 100 });
// Simulate response with download progress
xhr.setStatus(200);
xhr.setResponseHeaders({ "Content-Length": "1000" });
xhr.downloadProgress({ loaded: 500, total: 1000 });
xhr.setResponseBody("response data");
xhr.downloadProgress({ loaded: 1000, total: 1000 });Filter requests to allow some to pass through to real XMLHttpRequest.
/**
* Add a filter function to determine which requests should use real XHR
* @param {function(string, string, boolean, string=, string=): boolean} filterFn - Function that returns true to use real XHR
* @returns {void}
*/
static addFilter(filterFn) {
// Implementation
}
/**
* Enable request filtering
* @type {boolean}
*/
static useFilters = false;
/**
* Convert fake XHR to real XHR for this request
* @param {FakeXMLHttpRequest} fakeXhr - The fake XHR instance
* @param {Array} xhrArgs - Arguments passed to open()
* @returns {void}
*/
static defake(fakeXhr, xhrArgs) {
// Implementation
}Usage Example:
const nise = require("nise");
// Enable filtering
nise.fakeXhr.FakeXMLHttpRequest.useFilters = true;
// Add filter to allow real requests to external APIs
nise.fakeXhr.FakeXMLHttpRequest.addFilter(function(method, url) {
// Use real XHR for external URLs
return url.startsWith("https://api.external.com");
});
// Replace global XHR
const FakeXHR = nise.fakeXhr.useFakeXMLHttpRequest();
// Internal requests use fake XHR
const internalXhr = new XMLHttpRequest();
internalXhr.open("GET", "/api/internal"); // Uses fake XHR
// External requests use real XHR
const externalXhr = new XMLHttpRequest();
externalXhr.open("GET", "https://api.external.com/data"); // Uses real XHRSupport for different response types (text, json, blob, arraybuffer, document).
/**
* @typedef {Object} ResponseTypeHandling - Response type conversion and validation
* @property {string} responseType - Set before send() to specify expected response type ("", "text", "json", "blob", "arraybuffer", "document")
* @property {*} response - Response data in the format specified by responseType
* @property {string} responseText - Response as text (for responseType "" or "text")
* @property {Document|null} responseXML - Response as XML document (for responseType "document" or XML content)
*/Usage Example:
const xhr = new nise.fakeXhr.FakeXMLHttpRequest();
// JSON response
xhr.open("GET", "/api/data");
xhr.responseType = "json";
xhr.send();
xhr.respond(200, { "Content-Type": "application/json" }, '{"name": "Alice"}');
console.log(xhr.response); // {name: "Alice"} (parsed object)
// ArrayBuffer response
const xhr2 = new nise.fakeXhr.FakeXMLHttpRequest();
xhr2.open("GET", "/api/binary");
xhr2.responseType = "arraybuffer";
xhr2.send();
xhr2.respond(200, {}, "binary data");
console.log(xhr2.response instanceof ArrayBuffer); // trueUtility methods and properties available on the FakeXMLHttpRequest constructor.
/**
* HTTP status codes mapping
* @type {Object.<number, string>}
*/
static statusCodes = {};
/**
* Parse XML text into Document
* @param {string} text - XML text to parse
* @returns {Document|null} Parsed document or null if parsing failed
*/
static parseXML(text) {
// Implementation
}
/**
* Callback called when new FakeXMLHttpRequest instances are created
* @type {function(FakeXMLHttpRequest): void|null}
*/
static onCreate = null;Usage Example:
const nise = require("nise");
// Set up onCreate callback for all new XHR instances
nise.fakeXhr.FakeXMLHttpRequest.onCreate = function(xhr) {
console.log("New XHR created:", xhr);
// Could set default headers, event handlers, etc.
};
// Check status codes
console.log(nise.fakeXhr.FakeXMLHttpRequest.statusCodes[404]); // "Not Found"
// Parse XML
const doc = nise.fakeXhr.FakeXMLHttpRequest.parseXML("<root><item>test</item></root>");
console.log(doc.querySelector("item").textContent); // "test"