Repository: capricorn86/happy-dom
Pull request #156 (2020-11-16 21:03:36)

#140@minor: Adds support for History API.


Patch:
diff --git a/packages/happy-dom/src/history/History.ts b/packages/happy-dom/src/history/History.ts
new file mode 100644
index 00000000..5432d272
--- /dev/null
+++ b/packages/happy-dom/src/history/History.ts
@@ -0,0 +1,78 @@
+import HistoryScrollRestorationEnum from './HistoryScrollRestorationEnum';
+
+/**
+ * History API.
+ *
+ * Reference:
+ * https://developer.mozilla.org/en-US/docs/Web/API/History
+ */
+export default class History {
+	public readonly length = 0;
+	public readonly state = null;
+	private _scrollRestoration = HistoryScrollRestorationEnum.auto;
+
+	/**
+	 * Returns scroll restoration.
+	 *
+	 * @returns Sroll restoration.
+	 */
+	public get scrollRestoration(): HistoryScrollRestorationEnum {
+		return this._scrollRestoration;
+	}
+
+	/**
+	 * Sets scroll restoration.
+	 *
+	 * @param scrollRestoration Sroll restoration.
+	 */
+	public set scrollRestoration(scrollRestoration: HistoryScrollRestorationEnum) {
+		this._scrollRestoration = HistoryScrollRestorationEnum[scrollRestoration]
+			? scrollRestoration
+			: this._scrollRestoration;
+	}
+
+	/**
+	 * Goes to the previous page in session history.
+	 */
+	public back(): void {
+		// Do nothing.
+	}
+
+	/**
+	 * Goes to the next page in session history.
+	 */
+	public forward(): void {
+		// Do nothing.
+	}
+
+	/**
+	 * Load a specific page from the session history.
+	 *
+	 * @param delta Delta.
+	 */
+	public go(_delta: number): void {
+		// Do nothing.
+	}
+
+	/**
+	 * Pushes the given data onto the session history stack.
+	 *
+	 * @param state State.
+	 * @param title Title.
+	 * @param [url] URL.
+	 */
+	public pushState(_state: object, _title, _url?: string): void {
+		// Do nothing.
+	}
+
+	/**
+	 * This method modifies the current history entry, replacing it with a new state.
+	 *
+	 * @param state State.
+	 * @param title Title.
+	 * @param [url] URL.
+	 */
+	public replaceState(_state: object, _title, _url?: string): void {
+		// Do nothing.
+	}
+}
diff --git a/packages/happy-dom/src/history/HistoryScrollRestorationEnum.ts b/packages/happy-dom/src/history/HistoryScrollRestorationEnum.ts
new file mode 100644
index 00000000..81171375
--- /dev/null
+++ b/packages/happy-dom/src/history/HistoryScrollRestorationEnum.ts
@@ -0,0 +1,5 @@
+enum HistoryScrollRestorationEnum {
+	auto = 'auto',
+	manual = 'manual'
+}
+export default HistoryScrollRestorationEnum;
diff --git a/packages/happy-dom/src/window/Window.ts b/packages/happy-dom/src/window/Window.ts
index 08115a89..60b0de87 100644
--- a/packages/happy-dom/src/window/Window.ts
+++ b/packages/happy-dom/src/window/Window.ts
@@ -26,6 +26,7 @@ import Blob from '../file/Blob';
 import File from '../file/File';
 import DOMException from '../exception/DOMException';
 import FileReader from '../file/FileReader';
+import History from '../history/History';
 
 /**
  * Handles the Window.
@@ -59,11 +60,13 @@ export default class Window extends EventTarget implements NodeJS.Global {
 	public File = File;
 	public FileReader = FileReader;
 	public DOMException = DOMException;
+	public History = History;
 
 	// Public Properties
 	public document: Document;
 	public customElements: CustomElementRegistry = new CustomElementRegistry();
 	public location = new Location();
+	public history = new History();
 	public navigator = { userAgent: 'happy-dom' };
 	public console = global ? global.console : null;
 	public self: Window = this;


Test patch:
diff --git a/packages/happy-dom/test/history/History.test.ts b/packages/happy-dom/test/history/History.test.ts
new file mode 100644
index 00000000..cfdabd44
--- /dev/null
+++ b/packages/happy-dom/test/history/History.test.ts
@@ -0,0 +1,50 @@
+import History from '../../src/history/History';
+import HistoryScrollRestorationEnum from '../../src/history/HistoryScrollRestorationEnum';
+
+describe('History', () => {
+	let history: History;
+
+	beforeEach(() => {
+		history = new History();
+	});
+
+	describe('get length()', () => {
+		test('Returns "0".', () => {
+			expect(history.length).toBe(0);
+		});
+	});
+
+	describe('get state()', () => {
+		test('Returns "null".', () => {
+			expect(history.state).toBe(null);
+		});
+	});
+
+	describe('get scrollRestoration()', () => {
+		test('Returns "auto" by default.', () => {
+			expect(history.scrollRestoration).toBe(HistoryScrollRestorationEnum.auto);
+		});
+	});
+
+	describe('set scrollRestoration()', () => {
+		test('Is not possible to set an invalid value.', () => {
+			// @ts-ignore
+			history.scrollRestoration = 'invalid';
+			expect(history.scrollRestoration).toBe(HistoryScrollRestorationEnum.auto);
+		});
+
+		test('Is possible to set to "manual".', () => {
+			history.scrollRestoration = HistoryScrollRestorationEnum.manual;
+			expect(history.scrollRestoration).toBe(HistoryScrollRestorationEnum.manual);
+		});
+	});
+
+	for (const method of ['back', 'forward', 'go', 'pushState', 'replaceState']) {
+		describe(`${method}()`, () => {
+			test('Method exists and does nothing.', () => {
+				expect(typeof history[method]).toBe('function');
+				history[method]();
+			});
+		});
+	}
+});