Repository: capricorn86/happy-dom (ts)

Issue:
timeStamp property missing from Event & CustomEvent
**Is your feature request related to a problem? Please describe.**

[`Event`](https://github.com/capricorn86/happy-dom/blob/master/packages/happy-dom/src/event/Event.ts#L11-L20) (and hence [`CustomEvent`](https://github.com/capricorn86/happy-dom/blob/master/packages/happy-dom/src/event/events/CustomEvent.ts)) are missing the `timeStamp` property. This means that for example unit tests running with `happy-dom` will fail if they try to access the undefined `timeStamp` property.

Example code:
```ts
console.debug("TYPEOF timeStamp", typeof event.timeStamp);
```
```sh
TYPEOF timeStamp undefined
```

**Describe the solution you'd like**
Happy DOM should assign the `timeStamp` property. 

~~(I will be providing a pull request.)~~ I've provided a **Pull Request for this at https://github.com/capricorn86/happy-dom/pull/723**


**Describe alternatives you've considered**
- Hack my own tests to check if `timeStamp` is undefined – _basically making the tests useless_
- Override the global `CustomEvent` – _feels dirty_


**Additional context**

Looking at [`jsdom` implementation of `timeStamp`](https://github.com/jsdom/jsdom/blob/82c4d54aa7062e6e7402a27502639e01836d694d/lib/jsdom/living/events/Event-impl.js#L39) is just a `Date.now()` – which does not match the specification (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp) and [Spec](https://dom.spec.whatwg.org/#ref-for-dom-event-timestamp%E2%91%A0)):
> _This value is the number of milliseconds elapsed from the beginning of the time origin until the event was created. If the global object is [Window](https://developer.mozilla.org/en-US/docs/Web/API/Window), the time origin is the moment the user clicked on the link, or the script that initiated the loading of the document. In a worker, the time origin is the moment of creation of the worker._

I think we could do better: My proposed solution is to use `performance.timeOrigin` as the time origin to resolve the relative Event `timeStamp` value.

Pull request:
#722@minor: Add timeStamp property to Event.
Fix #722 by implementing `timeStamp` property on `Event`.



According to specification (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp) and [Spec](https://dom.spec.whatwg.org/#ref-for-dom-event-timestamp%E2%91%A0)) the `timeStamp` property is described as follows:
> _This value is the number of milliseconds elapsed from the beginning of the time origin until the event was created. If the global object is [Window](https://developer.mozilla.org/en-US/docs/Web/API/Window), the time origin is the moment the user clicked on the link, or the script that initiated the loading of the document. In a worker, the time origin is the moment of creation of the worker._

My proposed solution is to use `performance.timeOrigin` as the time origin to resolve the relative Event `timeStamp` value.


Patch:
diff --git a/packages/happy-dom/src/event/Event.ts b/packages/happy-dom/src/event/Event.ts
index 451df20c..541d5413 100644
--- a/packages/happy-dom/src/event/Event.ts
+++ b/packages/happy-dom/src/event/Event.ts
@@ -4,6 +4,7 @@ import IWindow from '../window/IWindow';
 import IShadowRoot from '../nodes/shadow-root/IShadowRoot';
 import IEventTarget from './IEventTarget';
 import NodeTypeEnum from '../nodes/node/NodeTypeEnum';
+import { performance } from 'perf_hooks';
 
 /**
  * Event.
@@ -17,6 +18,7 @@ export default class Event {
 	public _propagationStopped = false;
 	public _target: IEventTarget = null;
 	public _currentTarget: IEventTarget = null;
+	public timeStamp: number = performance.now();
 	public type: string = null;
 
 	/**
diff --git a/turbo.json b/turbo.json
index a5afee76..b53db218 100644
--- a/turbo.json
+++ b/turbo.json
@@ -11,8 +11,20 @@
 		"lint": {
 			"outputs": []
 		},
+		"lint:fix": {
+			"outputs": []
+		},
 		"test": {
 			"outputs": []
+		},
+		"test:watch": {
+			"outputs": []
+		},
+		"version": {
+			"outputs": []
+		},
+		"publish": {
+			"outputs": []
 		}
 	}
 }


Test patch:
diff --git a/packages/happy-dom/test/event/Event.test.ts b/packages/happy-dom/test/event/Event.test.ts
index fb1b5d95..91bfe362 100644
--- a/packages/happy-dom/test/event/Event.test.ts
+++ b/packages/happy-dom/test/event/Event.test.ts
@@ -3,6 +3,7 @@ import Window from '../../src/window/Window';
 import IDocument from '../../src/nodes/document/IDocument';
 import Event from '../../src/event/Event';
 import CustomElement from '../CustomElement';
+import { performance } from 'perf_hooks';
 
 describe('Event', () => {
 	let window: IWindow;
@@ -15,6 +16,25 @@ describe('Event', () => {
 		window.customElements.define('custom-element', CustomElement);
 	});
 
+	afterEach(() => {
+		jest.restoreAllMocks();
+	});
+
+	describe('get timeStamp()', () => {
+		it('Returns the value returned by performance.now() at the time it was created.', () => {
+			Object.defineProperty(performance, 'now', {
+				value: jest.fn(),
+				configurable: true,
+				writable: true
+			});
+
+			const performanceNow = 12345;
+			jest.spyOn(performance, 'now').mockImplementation(() => performanceNow);
+			const event = new Event('click');
+			expect(event.timeStamp).toBe(performanceNow);
+		});
+	});
+
 	describe('composedPath()', () => {
 		it('Returns a composed path.', () => {
 			const div = document.createElement('div');
diff --git a/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts b/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts
index b8cb6985..4e420249 100644
--- a/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-element/HTMLElement.test.ts
@@ -354,22 +354,18 @@ describe('HTMLElement', () => {
 
 	describe('click()', () => {
 		it('Dispatches "click" event.', () => {
-			let triggeredEvent = null;
+			let triggeredEvent: PointerEvent = null;
 
-			element.addEventListener('click', (event) => {
-				triggeredEvent = event;
-			});
+			element.addEventListener('click', (event: PointerEvent) => (triggeredEvent = event));
 
 			element.click();
 
-			const event = new PointerEvent('click', {
-				bubbles: true,
-				composed: true
-			});
-			event._target = element;
-			event._currentTarget = element;
-
-			expect(triggeredEvent).toEqual(event);
+			expect(triggeredEvent instanceof PointerEvent).toBe(true);
+			expect(triggeredEvent.type).toBe('click');
+			expect(triggeredEvent.bubbles).toBe(true);
+			expect(triggeredEvent.composed).toBe(true);
+			expect(triggeredEvent.target === element).toBe(true);
+			expect(triggeredEvent.currentTarget === element).toBe(true);
 		});
 	});
 
@@ -401,7 +397,7 @@ describe('HTMLElement', () => {
 			expect(triggeredFocusOutEvent.composed).toBe(true);
 			expect(triggeredFocusOutEvent.target === element).toBe(true);
 
-			expect(document.activeElement).toEqual(document.body);
+			expect(document.activeElement === document.body).toBe(true);
 		});
 
 		it('Does not dispatch "blur" event if not connected to the DOM.', () => {
@@ -459,7 +455,7 @@ describe('HTMLElement', () => {
 			expect(triggeredFocusInEvent.composed).toBe(true);
 			expect(triggeredFocusInEvent.target === element).toBe(true);
 
-			expect(document.activeElement).toEqual(element);
+			expect(document.activeElement === element).toBe(true);
 		});
 
 		it('Does not dispatch "focus" event if not connected to the DOM.', () => {