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 + ) + ); + }); + } + }); });