Repository: wheresrhys/fetch-mock
Pull request #875 (2024-11-15 18:22:18)

Rhys/hard reset
Added a hardReset method


Patch:
diff --git a/docs/docs/API/resetting.md b/docs/docs/API/resetting.md
index bef4696..289749e 100644
--- a/docs/docs/API/resetting.md
+++ b/docs/docs/API/resetting.md
@@ -6,6 +6,10 @@ sidebar_position: 5
 
 The methods below can be used to restore some or all of fetchMock's behaviour toits default state.
 
+## .hardReset(options)
+
+Removes all the behaviour and state that's been added so far. It runs `.removeRoutes()`, `.clearHistory()` and `.unmockGlobal()`. Pass `{includeSticky: true}` to force even sticky routes to be removed.
+
 ## .removeRoutes(options)
 
 This method removes some or all of the routes that have been added to fetch mock. It accepts the following options.
diff --git a/docs/docs/Usage/upgrade-guide.md b/docs/docs/Usage/upgrade-guide.md
index e5e74e7..e1096e6 100644
--- a/docs/docs/Usage/upgrade-guide.md
+++ b/docs/docs/Usage/upgrade-guide.md
@@ -49,4 +49,5 @@ The same is true for `postOnce()`, `deleteOnce()` etc.
 This was principally used when mocking node-fetch referenced as a local variable. Given that `fetch` is now available as a native global it's less useful and has been removed. If necessary to mock a local instance of node-fetch use [`.fetchHandler`](/fetch-mock/docs/API/mocking-and-spying#fetchhandler)
 
 ### Types completely written
+
 There are many changes, both in the naming and structuring of types. Refer to the errors generated by your toolchain to update these.
diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js
index 4c78d88..d9c85a2 100644
--- a/docs/src/pages/index.js
+++ b/docs/src/pages/index.js
@@ -33,8 +33,8 @@ export default function Home() {
 	const { siteConfig } = useDocusaurusContext();
 	return (
 		<Layout
-			title={`Hello from ${siteConfig.title}`}
-			description="Description will go into a meta tag in <head />"
+			description={`${siteConfig.tagline} Runs in a wide range of environments, from node.js to service workers,
+				and compatible with most testing frameworks.`}
 		>
 			<HomepageHeader />
 			<main>
diff --git a/packages/codemods/src/codemods/methods.js b/packages/codemods/src/codemods/methods.js
index 5ed3cf2..ec5c6a4 100644
--- a/packages/codemods/src/codemods/methods.js
+++ b/packages/codemods/src/codemods/methods.js
@@ -74,11 +74,16 @@ module.exports.simpleMethods = function (fetchMockVariableName, root) {
 				const sticky = path?.value?.arguments[0]?.properties?.find(
 					(prop) => prop.key.name === 'sticky',
 				)?.value?.value;
-				const newExpressions = j(`
-${methodName !== 'resetBehavior' ? 'fetchMock.clearHistory()' : ''};
+				let expressionsString;
+				if (methodName === 'resetBehavior') {
+					expressionsString = `
 fetchMock.removeRoutes(${sticky ? '{includeSticky: true}' : ''});
-fetchMock.unmockGlobal();
-`)
+fetchMock.unmockGlobal();`;
+				} else {
+					expressionsString = `
+fetchMock.hardReset(${sticky ? '{includeSticky: true}' : ''});`;
+				}
+				const newExpressions = j(expressionsString)
 					.find(j.ExpressionStatement)
 					.paths();
 				const insertLocation = j(path)
diff --git a/packages/fetch-mock/src/FetchMock.ts b/packages/fetch-mock/src/FetchMock.ts
index b5f8f47..aad608c 100644
--- a/packages/fetch-mock/src/FetchMock.ts
+++ b/packages/fetch-mock/src/FetchMock.ts
@@ -4,6 +4,10 @@ import { MatcherDefinition, RouteMatcher } from './Matchers.js';
 import CallHistory from './CallHistory.js';
 import * as requestUtils from './RequestUtils.js';
 
+export type HardResetOptions = {
+	includeSticky?: boolean;
+};
+
 export type FetchMockGlobalConfig = {
 	includeContentLength?: boolean;
 	matchPartialBody?: boolean;
@@ -146,6 +150,13 @@ export class FetchMock {
 		return this;
 	}
 
+	hardReset(options?: HardResetOptions): FetchMock {
+		this.clearHistory();
+		this.removeRoutes(options as RemoveRouteOptions);
+		this.unmockGlobal();
+		return this;
+	}
+
 	spy(
 		this: FetchMock,
 		matcher?: RouteMatcher | UserRouteConfig,
diff --git a/packages/jest/src/index.ts b/packages/jest/src/index.ts
index ddda7c9..81b1e25 100644
--- a/packages/jest/src/index.ts
+++ b/packages/jest/src/index.ts
@@ -32,7 +32,6 @@ export function manageFetchMockGlobally(jest: Jest) {
 	const { clearAllMocks, resetAllMocks, restoreAllMocks } = jest;
 
 	jest.clearAllMocks = () => {
-		console.log('yeah');
 		clearAllMocks.apply(jest);
 		fetchMockJest.mockClear();
 		return jest;


Test patch:
diff --git a/packages/codemods/src/__tests__/method-codemods.test.js b/packages/codemods/src/__tests__/method-codemods.test.js
index 9d099c7..54e25de 100644
--- a/packages/codemods/src/__tests__/method-codemods.test.js
+++ b/packages/codemods/src/__tests__/method-codemods.test.js
@@ -66,35 +66,21 @@ describe('codemods operating on methods', () => {
 
 	describe('converting resetting methods', () => {
 		it('rewrites restore()', () => {
-			expectCodemodResult(
-				'fetchMock.restore()',
-				`fetchMock.clearHistory();
-fetchMock.removeRoutes();
-fetchMock.unmockGlobal();`,
-			);
+			expectCodemodResult('fetchMock.restore()', `fetchMock.hardReset();`);
 		});
 		it('rewrites restore() with {sticky: true}', () => {
 			expectCodemodResult(
 				'fetchMock.restore({sticky: true})',
-				`fetchMock.clearHistory();
-fetchMock.removeRoutes({includeSticky: true});
-fetchMock.unmockGlobal();`,
+				`fetchMock.hardReset({includeSticky: true});`,
 			);
 		});
 		it('rewrites reset()', () => {
-			expectCodemodResult(
-				'fetchMock.reset()',
-				`fetchMock.clearHistory();
-fetchMock.removeRoutes();
-fetchMock.unmockGlobal();`,
-			);
+			expectCodemodResult('fetchMock.reset()', `fetchMock.hardReset();`);
 		});
 		it('rewrites reset() with {sticky: true}', () => {
 			expectCodemodResult(
 				'fetchMock.reset({sticky: true})',
-				`fetchMock.clearHistory();
-fetchMock.removeRoutes({includeSticky: true});
-fetchMock.unmockGlobal();`,
+				`fetchMock.hardReset({includeSticky: true});`,
 			);
 		});
 
diff --git a/packages/fetch-mock/src/__tests__/FetchMock/instance-management.test.js b/packages/fetch-mock/src/__tests__/FetchMock/instance-management.test.js
index fe39a07..9154f19 100644
--- a/packages/fetch-mock/src/__tests__/FetchMock/instance-management.test.js
+++ b/packages/fetch-mock/src/__tests__/FetchMock/instance-management.test.js
@@ -154,4 +154,27 @@ describe('instance management', () => {
 			expect(fm.router.routes.length).toEqual(0);
 		});
 	});
+
+	describe('hardReset', () => {
+		it('can hardReset and leave just sticky routes', () => {
+			const fm = fetchMock.createInstance();
+			fm.route('*', 200, { sticky: true, name: 'sticky' }).route('*', 200);
+			fm.mockGlobal();
+			fm.fetchHandler('http://a.com');
+			fm.hardReset();
+			expect(fm.callHistory.callLogs.length).toBe(0);
+			expect(globalThis.fetch).not.toEqual(fm.fetchHandler);
+			expect(fm.router.routes[0].config.name).toBe('sticky');
+		});
+		it('can hardReset including sticky routes', () => {
+			const fm = fetchMock.createInstance();
+			fm.route('*', 200, { sticky: true, name: 'sticky' }).route('*', 200);
+			fm.mockGlobal();
+			fm.fetchHandler('http://a.com');
+			fm.hardReset({ includeSticky: true });
+			expect(fm.callHistory.callLogs.length).toBe(0);
+			expect(globalThis.fetch).not.toEqual(fm.fetchHandler);
+			expect(fm.router.routes.length).toBe(0);
+		});
+	});
 });
diff --git a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js
index 03af666..36524f0 100644
--- a/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js
+++ b/packages/fetch-mock/src/__tests__/FetchMock/mock-and-spy.test.js
@@ -51,6 +51,10 @@ describe('mock and spy', () => {
 			fm.mockGlobal().unmockGlobal();
 			expect(globalThis.fetch).toEqual(nativeFetch);
 		});
+
+		it('does not error when called on unmocked fetch', () => {
+			expect(() => fm.unmockGlobal()).not.toThrow();
+		});
 	});
 	describe('.spy()', () => {
 		testChainableMethod('spy');