Repository: capricorn86/happy-dom
Pull request #367 (2022-02-17 17:37:00)

#366@minor: Adds support for HTMLInputElement.stepUp() and HTMLInputE…
…lement.stepDown().


Patch:
diff --git a/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts b/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts
index f11f828a..5f7205d8 100644
--- a/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts
+++ b/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts
@@ -10,6 +10,7 @@ import HTMLInputElementSelectionDirectionEnum from './HTMLInputElementSelectionD
 import IHTMLInputElement from './IHTMLInputElement';
 import IHTMLFormElement from '../html-form-element/IHTMLFormElement';
 import IHTMLElement from '../html-element/IHTMLElement';
+import HTMLInputElementValueStepping from './HTMLInputElementValueStepping';
 
 /**
  * HTML Input Element.
@@ -903,6 +904,30 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
 		return true;
 	}
 
+	/**
+	 * Steps up.
+	 *
+	 * @param [increment] Increment.
+	 */
+	public stepUp(increment?: number): void {
+		const newValue = HTMLInputElementValueStepping.step(this.type, this.value, 1, increment);
+		if (newValue !== null) {
+			this.value = newValue;
+		}
+	}
+
+	/**
+	 * Steps down.
+	 *
+	 * @param [increment] Increment.
+	 */
+	public stepDown(increment?: number): void {
+		const newValue = HTMLInputElementValueStepping.step(this.type, this.value, -1, increment);
+		if (newValue !== null) {
+			this.value = newValue;
+		}
+	}
+
 	/**
 	 * Clones a node.
 	 *
diff --git a/packages/happy-dom/src/nodes/html-input-element/HTMLInputElementValueStepping.ts b/packages/happy-dom/src/nodes/html-input-element/HTMLInputElementValueStepping.ts
new file mode 100644
index 00000000..48e6dd14
--- /dev/null
+++ b/packages/happy-dom/src/nodes/html-input-element/HTMLInputElementValueStepping.ts
@@ -0,0 +1,34 @@
+import DOMException from '../../exception/DOMException';
+
+/**
+ * HTML input element value stepping.
+ */
+export default class HTMLInputElementValueStepping {
+	/**
+	 * Steps up or down.
+	 *
+	 * @param type Type.
+	 * @param value Value.
+	 * @param direction Direction.
+	 * @param [increment] Increment.
+	 * @returns New value.
+	 */
+	public static step(type: string, value: string, direction: -1 | 1, increment?: number): string {
+		switch (type) {
+			case 'number':
+				return String(
+					Number(value) + (increment !== undefined ? increment * direction : direction)
+				);
+			case 'date':
+			case 'month':
+			case 'week':
+			case 'time':
+			case 'datetime-local':
+			case 'range':
+				// TODO: Implement support for additional types
+				return null;
+			default:
+				throw new DOMException('This form element is not steppable.');
+		}
+	}
+}
diff --git a/packages/happy-dom/src/nodes/html-input-element/IHTMLInputElement.ts b/packages/happy-dom/src/nodes/html-input-element/IHTMLInputElement.ts
index 836ffcbc..4c689cd4 100644
--- a/packages/happy-dom/src/nodes/html-input-element/IHTMLInputElement.ts
+++ b/packages/happy-dom/src/nodes/html-input-element/IHTMLInputElement.ts
@@ -84,6 +84,20 @@ export default interface IHTMLInputElement extends IHTMLElement {
 	 */
 	checkValidity(): boolean;
 
+	/**
+	 * Steps up.
+	 *
+	 * @param [increment] Increment.
+	 */
+	stepUp(increment?: number): void;
+
+	/**
+	 * Steps up.
+	 *
+	 * @param [increment] Increment.
+	 */
+	stepDown(increment?: number): void;
+
 	/**
 	 * Clones a node.
 	 *


Test patch:
diff --git a/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts b/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts
index 1823eb02..69be8ff6 100644
--- a/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts
@@ -1,6 +1,7 @@
 import Window from '../../../src/window/Window';
 import Document from '../../../src/nodes/document/Document';
 import HTMLInputElement from '../../../src/nodes/html-input-element/HTMLInputElement';
+import DOMException from '../../../src/exception/DOMException';
 import File from '../../../src/file/File';
 import HTMLInputElementSelectionModeEnum from '../../../src/nodes/html-input-element/HTMLInputElementSelectionModeEnum';
 import HTMLInputElementSelectionDirectionEnum from '../../../src/nodes/html-input-element/HTMLInputElementSelectionDirectionEnum';
@@ -432,6 +433,54 @@ describe('HTMLInputElement', () => {
 		});
 	});
 
+	describe('checkValidity()', () => {
+		it('Returns "true".', () => {
+			expect(element.checkValidity()).toBe(true);
+		});
+	});
+
+	describe('stepUp()', () => {
+		it('Steps up with default value.', () => {
+			element.type = 'number';
+			element.stepUp();
+			expect(element.value).toBe('1');
+		});
+
+		it('Steps up with defined increment value.', () => {
+			element.type = 'number';
+			element.value = '1';
+			element.stepUp(3);
+			expect(element.value).toBe('4');
+		});
+
+		it('Throws exception when invalid type.', () => {
+			expect(() => element.stepUp()).toThrowError(
+				new DOMException('This form element is not steppable.')
+			);
+		});
+	});
+
+	describe('stepDown()', () => {
+		it('Steps up with default value.', () => {
+			element.type = 'number';
+			element.stepDown();
+			expect(element.value).toBe('-1');
+		});
+
+		it('Steps up with defined increment value.', () => {
+			element.type = 'number';
+			element.value = '1';
+			element.stepDown(3);
+			expect(element.value).toBe('-2');
+		});
+
+		it('Throws exception when invalid type.', () => {
+			expect(() => element.stepDown()).toThrowError(
+				new DOMException('This form element is not steppable.')
+			);
+		});
+	});
+
 	describe('cloneNode()', () => {
 		it('Clones when type is "checkbox".', () => {
 			element.type = 'checkbox';