Repository: capricorn86/happy-dom (TypeScript)

Issue:
MutationObserver callback should have MutationObserver as second argument
**Describe the bug**

MutationObserver callback should have MutationObserver as second argument.

**To Reproduce**

I have a test in my test suite that checks if MutationObserver callback gets called with correct arguments.

```js
    expect(listener).toHaveBeenCalledWith(
      [expect.any(MutationRecord)],
      expect.any(MutationObserver),
    );
```

and results in an error:

```
AssertionError: expected "spy" to be called with arguments: [ [ Any{ …(3) } ], Any{ …(3) } ]

Received: 

  1st spy call:

  Array [
    Array [
     Any<MutationRecord>,
    ],
-   Any<MutationObserver>,
  ]
```

**Expected behavior**

MutationObserver callback to have MutationObserver as second argument.

Source of this claim:

- https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
- TypeScript's lib.dom.d.ts:
    ```ts
    interface MutationCallback {
        (mutations: MutationRecord[], observer: MutationObserver): void;
    }
    ```
- JSDoc behavior

**Screenshots**

Commit with tests failing after replacing JSDom with Happy DOM:
https://github.com/wojtekmaj/react-hooks/commit/cc74f9a6bf1a897c63db9afbe585701b10162b7f



Discussion:

Patch:
diff --git a/packages/happy-dom/src/mutation-observer/MutationListener.ts b/packages/happy-dom/src/mutation-observer/MutationListener.ts
index e9a49fa55..b27678e32 100644
--- a/packages/happy-dom/src/mutation-observer/MutationListener.ts
+++ b/packages/happy-dom/src/mutation-observer/MutationListener.ts
@@ -1,4 +1,5 @@
 import IMutationObserverInit from './IMutationObserverInit.js';
+import MutationObserver from './MutationObserver.js';
 import MutationRecord from './MutationRecord.js';
 
 /**
@@ -6,5 +7,6 @@ import MutationRecord from './MutationRecord.js';
  */
 export default class MutationListener {
 	public options: IMutationObserverInit = null;
-	public callback: (record: MutationRecord[]) => void = null;
+	public observer: MutationObserver = null;
+	public callback: (record: MutationRecord[], observer: MutationObserver) => void = null;
 }
diff --git a/packages/happy-dom/src/mutation-observer/MutationObserver.ts b/packages/happy-dom/src/mutation-observer/MutationObserver.ts
index f657b2528..414158e82 100644
--- a/packages/happy-dom/src/mutation-observer/MutationObserver.ts
+++ b/packages/happy-dom/src/mutation-observer/MutationObserver.ts
@@ -11,7 +11,7 @@ import MutationRecord from './MutationRecord.js';
  * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
  */
 export default class MutationObserver {
-	private callback: (records: MutationRecord[]) => void;
+	private callback: (records: MutationRecord[], observer: MutationObserver) => void;
 	private target: INode = null;
 	private listener: MutationObserverListener = null;
 
@@ -20,7 +20,7 @@ export default class MutationObserver {
 	 *
 	 * @param callback Callback.
 	 */
-	constructor(callback: (records: MutationRecord[]) => void) {
+	constructor(callback: (records: MutationRecord[], observer: MutationObserver) => void) {
 		this.callback = callback;
 	}
 
@@ -47,6 +47,7 @@ export default class MutationObserver {
 		this.listener = new MutationObserverListener();
 		this.listener.options = options;
 		this.listener.callback = this.callback.bind(this);
+		this.listener.observer = this;
 
 		(<Node>target)._observe(this.listener);
 	}
diff --git a/packages/happy-dom/src/nodes/character-data/CharacterData.ts b/packages/happy-dom/src/nodes/character-data/CharacterData.ts
index f48c9c5c9..1045cdf23 100644
--- a/packages/happy-dom/src/nodes/character-data/CharacterData.ts
+++ b/packages/happy-dom/src/nodes/character-data/CharacterData.ts
@@ -68,7 +68,7 @@ export default abstract class CharacterData extends Node implements ICharacterDa
 					record.target = this;
 					record.type = MutationTypeEnum.characterData;
 					record.oldValue = observer.options.characterDataOldValue ? oldValue : null;
-					observer.callback([record]);
+					observer.callback([record], observer.observer);
 				}
 			}
 		}
diff --git a/packages/happy-dom/src/nodes/element/ElementNamedNodeMap.ts b/packages/happy-dom/src/nodes/element/ElementNamedNodeMap.ts
index 21364ea2f..18c3c86b9 100644
--- a/packages/happy-dom/src/nodes/element/ElementNamedNodeMap.ts
+++ b/packages/happy-dom/src/nodes/element/ElementNamedNodeMap.ts
@@ -103,7 +103,7 @@ export default class ElementNamedNodeMap extends NamedNodeMap {
 					record.type = MutationTypeEnum.attributes;
 					record.attributeName = item.name;
 					record.oldValue = observer.options.attributeOldValue ? oldValue : null;
-					observer.callback([record]);
+					observer.callback([record], observer.observer);
 				}
 			}
 		}
@@ -164,7 +164,7 @@ export default class ElementNamedNodeMap extends NamedNodeMap {
 					record.type = MutationTypeEnum.attributes;
 					record.attributeName = removedItem.name;
 					record.oldValue = observer.options.attributeOldValue ? removedItem.value : null;
-					observer.callback([record]);
+					observer.callback([record], observer.observer);
 				}
 			}
 		}
diff --git a/packages/happy-dom/src/nodes/node/NodeUtility.ts b/packages/happy-dom/src/nodes/node/NodeUtility.ts
index 2b38f26c0..a8dddb376 100644
--- a/packages/happy-dom/src/nodes/node/NodeUtility.ts
+++ b/packages/happy-dom/src/nodes/node/NodeUtility.ts
@@ -81,7 +81,7 @@ export default class NodeUtility {
 					(<Node>node)._observe(observer);
 				}
 				if (observer.options.childList) {
-					observer.callback([record]);
+					observer.callback([record], observer.observer);
 				}
 			}
 		}
@@ -121,7 +121,7 @@ export default class NodeUtility {
 			for (const observer of (<Node>ancestorNode)._observers) {
 				(<Node>node)._unobserve(observer);
 				if (observer.options.childList) {
-					observer.callback([record]);
+					observer.callback([record], observer.observer);
 				}
 			}
 		}
@@ -208,7 +208,7 @@ export default class NodeUtility {
 					(<Node>newNode)._observe(observer);
 				}
 				if (observer.options.childList) {
-					observer.callback([record]);
+					observer.callback([record], observer.observer);
 				}
 			}
 		}


Test patch:
diff --git a/packages/happy-dom/test/mutation-observer/MutationObserver.test.ts b/packages/happy-dom/test/mutation-observer/MutationObserver.test.ts
index 11dcfde2d..3366b3004 100644
--- a/packages/happy-dom/test/mutation-observer/MutationObserver.test.ts
+++ b/packages/happy-dom/test/mutation-observer/MutationObserver.test.ts
@@ -215,6 +215,15 @@ describe('MutationObserver', () => {
 				]
 			]);
 		});
+
+		it('Calls callback with the observer as second parameter.', () => {
+			const div = document.createElement('div');
+			const observer = new MutationObserver((mutationRecords, observer) => {
+				expect(observer).toBeInstanceOf(MutationObserver);
+			});
+			observer.observe(div, { attributes: true });
+			div.setAttribute('attr', 'value');
+		});
 	});
 
 	describe('disconnect()', () => {