diff --git a/packages/happy-dom/src/nodes/html-media-element/HTMLMediaElement.ts b/packages/happy-dom/src/nodes/html-media-element/HTMLMediaElement.ts
index 339db3d7..b5cadaea 100644
--- a/packages/happy-dom/src/nodes/html-media-element/HTMLMediaElement.ts
+++ b/packages/happy-dom/src/nodes/html-media-element/HTMLMediaElement.ts
@@ -1,3 +1,5 @@
+import DOMException from '../../exception/DOMException';
+import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum';
import HTMLElement from '../html-element/HTMLElement';
import IHTMLMediaElement from './IHTMLMediaElement';
@@ -8,6 +10,7 @@ import IHTMLMediaElement from './IHTMLMediaElement';
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base.
*/
export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaElement {
+ #volume = 1;
/**
* Returns autoplay.
*
@@ -98,7 +101,7 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
/**
* Returns muted.
*
- * @returns muted.
+ * @returns Muted.
*/
public get muted(): boolean {
return this.getAttributeNS(null, 'muted') !== null;
@@ -107,7 +110,7 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
/**
* Sets muted.
*
- * @param muted muted.
+ * @param muted Muted.
*/
public set muted(muted: boolean) {
if (!muted) {
@@ -117,6 +120,37 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
}
}
+ /**
+ * Returns volume.
+ *
+ * @returns Volume.
+ */
+ public get volume(): number {
+ return this.#volume;
+ }
+
+ /**
+ * Sets volume.
+ *
+ * @param volume Volume.
+ */
+ public set volume(volume: number | string) {
+ const parsedVolume = Number(volume);
+
+ if (isNaN(parsedVolume)) {
+ throw new TypeError(
+ `Failed to set the 'volume' property on 'HTMLMediaElement': The provided double value is non-finite.`
+ );
+ }
+ if (parsedVolume < 0 || parsedVolume > 1) {
+ throw new DOMException(
+ `Failed to set the 'volume' property on 'HTMLMediaElement': The volume provided (${parsedVolume}) is outside the range [0, 1].`,
+ DOMExceptionNameEnum.indexSizeError
+ );
+ }
+ this.#volume = parsedVolume;
+ }
+
/**
*
*/
diff --git a/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts b/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts
index 34ceeba2..65089d3d 100644
--- a/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts
+++ b/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts
@@ -20,15 +20,16 @@ export default interface IHTMLMediaElement extends IHTMLElement {
// DefaultPlaybackRate; // TODO
// DisableRemotePlayback: boolean; // TODO
// Duration: number; // TODO
- // Ended: boolean; // TODO
+ // Ended: boolean; // TODO readonly
// Error; // TODO object
loop: boolean;
// MediaKeys; // TODO
muted: boolean;
// NetworkState; // TODO
- paused: boolean;
- // Played: boolean; // TODO
+ paused: boolean; // TODO readonly?
+ // Played: // TODO timeranges
// PlaybackRate: number; // TODO
+ volume: number | string;
/**
* The HTMLMediaElement.pause() method will pause playback of the media, if the media is already in a paused state this method will have no effect.
@@ -39,7 +40,7 @@ export default interface IHTMLMediaElement extends IHTMLElement {
/**
* The HTMLMediaElement play() method attempts to begin playback of the media. It returns a Promise which is resolved when playback has been successfully started.
*/
- // play(): Promise; // TODO function
+ // Play(): Promise; // TODO function
/**
* Clones a node.
diff --git a/packages/happy-dom/test/nodes/html-media-element/HTMLMediaElement.test.ts b/packages/happy-dom/test/nodes/html-media-element/HTMLMediaElement.test.ts
index 40cc4d71..e6cbc208 100644
--- a/packages/happy-dom/test/nodes/html-media-element/HTMLMediaElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-media-element/HTMLMediaElement.test.ts
@@ -1,5 +1,7 @@
import Window from '../../../src/window/Window';
import IWindow from '../../../src/window/IWindow';
+import DOMException from '../../../src/exception/DOMException';
+import DOMExceptionNameEnum from '../../../src/exception/DOMExceptionNameEnum';
import IDocument from '../../../src/nodes/document/IDocument';
import IHTMLMediaElement from '../../../src/nodes/html-media-element/IHTMLMediaElement';
@@ -41,4 +43,43 @@ describe('HTMLMediaElement', () => {
expect(element.getAttribute('paused')).toBe('');
});
});
+
+ describe('volume()', () => {
+ it('Returns default value', () => {
+ expect(element.volume).toBe(1);
+ });
+
+ it('Set value', () => {
+ element.volume = 0.5;
+ expect(element.volume).toBe(0.5);
+ });
+
+ it('Set parse volmue as a number', () => {
+ element.volume = '0.5';
+ expect(element.volume).toBe(0.5);
+ });
+
+ it('Throw type error if volume is not a number', () => {
+ expect(() => {
+ element.volume = 'zeropointfive';
+ }).toThrowError(
+ new TypeError(
+ `Failed to set the 'volume' property on 'HTMLMediaElement': The provided double value is non-finite.`
+ )
+ );
+ });
+
+ for (const volume of [-0.4, 1.3]) {
+ it(`Throw error if out of range: ${volume}`, () => {
+ expect(() => {
+ element.volume = volume;
+ }).toThrowError(
+ new DOMException(
+ `Failed to set the 'volume' property on 'HTMLMediaElement': The volume provided (${volume}) is outside the range [0, 1].`,
+ DOMExceptionNameEnum.indexSizeError
+ )
+ );
+ });
+ }
+ });
});