Repository: wheresrhys/fetch-mock (js)

Issue:
clearHistory breaks if there have been unmatched calls
```typescript 
import fetchMock from "fetch-mock";

describe(fetch-mock", () => {
 it("should avoid real fetch when enabled, even if no mocks set", async () => {
    fetchMock.mockGlobal();
    try {
      await expect(fetch("https://example.com")).rejects.toBeInstanceOf(Error);
      fetchMock.clearHistory();  // Error
    } finally {
      fetchMock.unmockGlobal();
    }
  });
});
```

```
__tests__/fetch-mock.test.ts
  fetch-mock
    ✕ should avoid real fetch when enabled, even if no mocks set (2 ms)

  ● fetch-mock › should avoid real fetch when enabled, even if no mocks set

    TypeError: Cannot read properties of undefined (reading 'reset')

      31 |     try {
      32 |       await expect(fetch(testUrl)).rejects.toBeInstanceOf(Error);
    > 33 |       fetchMock.clearHistory();  // Error
         |                 ^
      34 |     } finally {
      35 |       fetchMock.unmockGlobal();
      36 |     }

      at node_modules/fetch-mock/dist/esm/CallHistory.js:19:52
          at Array.forEach (<anonymous>)
      at CallHistory.clear (node_modules/fetch-mock/dist/esm/CallHistory.js:19:23)
      at FetchMock.clearHistory (node_modules/fetch-mock/dist/esm/FetchMock.js:83:26)
      at Object.<anonymous> (__tests__/fetch-mock.test.ts:33:17)
```

Pull request description:
fix: clearHistory() can deal with unmatched calls
Also adds some long overdue tests

Fixes #852 

Patch:
diff --git a/packages/fetch-mock/src/CallHistory.ts b/packages/fetch-mock/src/CallHistory.ts
index 2702365..d796ba1 100644
--- a/packages/fetch-mock/src/CallHistory.ts
+++ b/packages/fetch-mock/src/CallHistory.ts
@@ -55,7 +55,11 @@ class CallHistory {
 	}
 
 	clear() {
-		this.callLogs.forEach(({ route }) => route.reset());
+		this.callLogs.forEach(({ route }) => {
+			if (route) {
+				route.reset();
+			}
+		});
 		this.callLogs = [];
 	}
 


Test patch:
diff --git a/packages/fetch-mock/src/__tests__/CallHistory.test.js b/packages/fetch-mock/src/__tests__/CallHistory.test.js
index adacddf..0727d3b 100644
--- a/packages/fetch-mock/src/__tests__/CallHistory.test.js
+++ b/packages/fetch-mock/src/__tests__/CallHistory.test.js
@@ -11,7 +11,38 @@ describe('CallHistory', () => {
 	const fetchTheseUrls = (...urls) =>
 		Promise.all(urls.map((url) => fm.fetchHandler(url)));
 
-	describe('helper methods', () => {
+	describe('clear', () => {
+		it('should clear all call logs', async () => {
+			fm.route('http://a.com', 200).route('http://b.com', 200);
+			fm.fetchHandler('http://a.com');
+			fm.fetchHandler('http://b.com');
+
+			expect(fm.callHistory.callLogs.length).toEqual(2);
+			fm.callHistory.clear();
+			expect(fm.callHistory.callLogs.length).toEqual(0);
+		});
+		it('should clear counter in routes that are set to only respond a set number of times', async () => {
+			fm.once('http://a.com', 200);
+			fm.fetchHandler('http://a.com');
+			fm.callHistory.clear();
+			await expect(fm.fetchHandler('http://a.com')).resolves.not.toThrow();
+		});
+		it('should not error if there are unmatched calls', async () => {
+			try {
+				await fm.fetchHandler('https://example.com');
+			} catch {
+				expect(() => fm.callHistory.clear()).not.toThrow(); // Error
+			}
+		});
+
+		it('is aliased to fetchMock.clearHistory()', async () => {
+			vi.spyOn(fm.callHistory, 'clear');
+			fm.clearHistory();
+			expect(fm.callHistory.clear).toHaveBeenCalled();
+			fm.callHistory.clear.mockRestore();
+		});
+	});
+	describe('inspecting call history methods', () => {
 		describe('called()', () => {
 			it('returns a suitable boolean', () => {
 				fm.catch();