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 4832ac2f..428a26e7 100644
--- a/packages/happy-dom/src/nodes/html-media-element/HTMLMediaElement.ts
+++ b/packages/happy-dom/src/nodes/html-media-element/HTMLMediaElement.ts
@@ -1,8 +1,9 @@
+import ErrorEvent from 'src/event/events/ErrorEvent';
import Event from '../../event/Event';
import DOMException from '../../exception/DOMException';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum';
import HTMLElement from '../html-element/HTMLElement';
-import IHTMLMediaElement from './IHTMLMediaElement';
+import IHTMLMediaElement, { IMediaError } from './IHTMLMediaElement';
/**
* HTML Base Element.
@@ -11,12 +12,25 @@ import IHTMLMediaElement from './IHTMLMediaElement';
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base.
*/
export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaElement {
+ // Public Properties
+ public readonly readyState = 0;
+ public readonly networkState = 0;
+ public readonly error: IMediaError = null;
+ public readonly ended = false;
+ public readonly duration = NaN;
+ public readonly textTracks = [];
+
+ // Events
+ public onerror: (event: ErrorEvent) => void = null;
+
#volume = 1;
#paused = true;
- #duration = NaN;
#currentTime = 0;
#playbackRate = 1;
#defaultPlaybackRate = 1;
+ #muted = false;
+ #defaultMuted = false;
+ #preservesPitch = true;
/**
* Returns autoplay.
*
@@ -88,7 +102,7 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
* @returns Muted.
*/
public get muted(): boolean {
- return this.getAttributeNS(null, 'muted') !== null;
+ return this.#muted;
}
/**
@@ -97,7 +111,31 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
* @param muted Muted.
*/
public set muted(muted: boolean) {
- if (!muted) {
+ this.muted = Boolean(muted);
+ if (!muted && !this.#defaultMuted) {
+ this.removeAttributeNS(null, 'muted');
+ } else {
+ this.setAttributeNS(null, 'muted', '');
+ }
+ }
+
+ /**
+ * Returns defaultMuted.
+ *
+ * @returns DefaultMuted.
+ */
+ public get defaultMuted(): boolean {
+ return this.#defaultMuted;
+ }
+
+ /**
+ * Sets defaultMuted.
+ *
+ * @param defaultMuted DefaultMuted.
+ */
+ public set defaultMuted(defaultMuted: boolean) {
+ this.#defaultMuted = Boolean(defaultMuted);
+ if (!this.#defaultMuted && !this.#muted) {
this.removeAttributeNS(null, 'muted');
} else {
this.setAttributeNS(null, 'muted', '');
@@ -201,15 +239,6 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
return this.#paused;
}
- /**
- * Returns duration.
- *
- * @returns Duration.
- */
- public get duration(): number {
- return this.#duration;
- }
-
/**
* Returns currentTime.
*
@@ -237,16 +266,16 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
/**
* Returns playbackRate.
*
- * @returns playbackRate.
+ * @returns PlaybackRate.
*/
- public get playbackRate(): number {
+ public get playbackRate(): number {
return this.#playbackRate;
}
/**
* Sets playbackRate.
*
- * @param playbackRate playbackRate.
+ * @param playbackRate PlaybackRate.
*/
public set playbackRate(playbackRate: number | string) {
const parsedPlaybackRate = Number(playbackRate);
@@ -261,16 +290,16 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
/**
* Returns defaultPlaybackRate.
*
- * @returns defaultPlaybackRate.
+ * @returns DefaultPlaybackRate.
*/
- public get defaultPlaybackRate(): number {
+ public get defaultPlaybackRate(): number {
return this.#defaultPlaybackRate;
}
/**
* Sets defaultPlaybackRate.
*
- * @param defaultPlaybackRate defaultPlaybackRate.
+ * @param defaultPlaybackRate DefaultPlaybackRate.
*/
public set defaultPlaybackRate(defaultPlaybackRate: number | string) {
const parsedDefaultPlaybackRate = Number(defaultPlaybackRate);
@@ -282,6 +311,24 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
this.#defaultPlaybackRate = parsedDefaultPlaybackRate;
}
+ /**
+ * Returns preservesPitch.
+ *
+ * @returns PlaybackRate.
+ */
+ public get preservesPitch(): boolean {
+ return this.#preservesPitch;
+ }
+
+ /**
+ * Sets preservesPitch.
+ *
+ * @param preservesPitch PreservesPitch.
+ */
+ public set preservesPitch(preservesPitch: boolean) {
+ this.#preservesPitch = Boolean(preservesPitch);
+ }
+
/**
*
*/
@@ -305,6 +352,18 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
return '';
}
+ /**
+ *
+ */
+ public load(): void {}
+
+ /**
+ *
+ */
+ public captureStream(): object {
+ return {};
+ }
+
/**
* Clones a node.
*
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 48ae30b4..fa545d3f 100644
--- a/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts
+++ b/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts
@@ -6,51 +6,75 @@ import IHTMLElement from '../html-element/IHTMLElement';
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement.
*/
+
+export interface IMediaError {
+ code: number;
+ message: string;
+}
export default interface IHTMLMediaElement extends IHTMLElement {
readonly currentSrc: string;
readonly duration: number;
+ readonly ended: boolean;
+ readonly error: IMediaError | null;
+ readonly networkState: number;
+ readonly readyState: number;
+ readonly textTracks: object[];
autoplay: boolean;
controls: boolean;
+ crossOrigin: string; // Only anonymus and 'use-credentials' is valid
loop: boolean;
muted: boolean;
paused: boolean;
volume: number | string;
src: string;
- crossOrigin: string; // Only anonymus and 'use-credentials' is valid
currentTime: number | string;
playbackRate: number | string;
defaultPlaybackRate: number | string;
+ defaultMuted: boolean;
+ preservesPitch: boolean;
-
- // AddTextTrack;
// Buffered; // TODO tameranges
- // CaptureStream; // TODO
- // ControlsList: string; // TODO
- // DefaultMuted: boolean; // TODO
- // DisableRemotePlayback: boolean; // TODO
- // Ended: boolean; // TODO readonly
- // Error; // TODO object
- // NetworkState; // TODO
// Played: // TODO timeranges
+ // Seekable: // TODO timeranges
+
+ // CaptureStream; // TODO https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream
/**
- * 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.
+ * 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.
*/
pause(): void;
/**
- * The HTMLMediaElement play() method attempts to begin playback of the media. It returns a Promise which is resolved when playback has been successfully started.
+ * 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;
/**
- * The HTMLMediaElement method canPlayType() reports how likely it is that the current browser will be able to play media of a given MIME type.
+ * The HTMLMediaElement method canPlayType() reports how likely it is that the current browser will be able to play
+ * media of a given MIME type.
* Https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType
* possible return value: "" | "probably" | "maybe".
*/
canPlayType(_type: string): string;
+ /**
+ * The HTMLMediaElement method load() resets the media element to its initial state and begins the process of
+ * selecting a media source and loading the media in preparation for playback to begin at the beginning.
+ * Https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/load.
+ */
+ load(): void;
+
+ /**
+ * A MediaStream object which can be used as a source for audio and/or video data by other media processing code,
+ * or as a source for WebRTC.
+ * Https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream.
+ */
+
+ captureStream(): object;
+
/**
* 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 557f1e90..ac5178f9 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
@@ -60,6 +60,27 @@ describe('HTMLMediaElement', () => {
});
}
+ describe(`get defaultMuted()`, () => {
+ it('Returns value.', () => {
+ expect(element.defaultMuted).toBe(false);
+ element.defaultMuted = true;
+ expect(element.defaultMuted).toBe(true);
+ });
+ });
+
+ describe(`set defaultMuted()`, () => {
+ it('Sets attribute value.', () => {
+ element.defaultMuted = true;
+ expect(element.getAttribute('muted')).toBe('');
+ });
+
+ it('Remove attribute value.', () => {
+ element.defaultMuted = true;
+ element.defaultMuted = false;
+ expect(element.getAttribute('muted')).toBeNull();
+ });
+ });
+
describe('canplay event', () => {
it('Should dispatch after src set', () => {
let dispatchedEvent: Event = null;
@@ -78,11 +99,11 @@ describe('HTMLMediaElement', () => {
describe('currentSrc', () => {
it('Returns the current src', () => {
- const src = 'https://src'
+ const src = 'https://src';
element.src = src;
expect(element.currentSrc).toBe(src);
- })
- })
+ });
+ });
describe('paused', () => {
it('Default is true', () => {
@@ -146,6 +167,12 @@ describe('HTMLMediaElement', () => {
});
});
+ describe('enden', () => {
+ it('Returns false', () => {
+ expect(element.ended).toBeFalsy();
+ });
+ });
+
describe('CrossOrigin', () => {
for (const crossOrigin of ['', null, 'use-credentials', 'anonymous']) {
it(`Set ${crossOrigin} as a valid crossOrigin`, () => {
@@ -163,7 +190,7 @@ describe('HTMLMediaElement', () => {
});
describe('duration', () => {
- it('Return 0 by default', () => {
+ it('Return NaN by default', () => {
expect(element.duration).toBe(NaN);
});
});
@@ -239,4 +266,35 @@ describe('HTMLMediaElement', () => {
);
});
});
+
+ describe('error', () => {
+ it('Return null by default', () => {
+ expect(element.error).toBeNull();
+ });
+ });
+
+ describe('networkState', () => {
+ it('Return 0 by default', () => {
+ expect(element.networkState).toBe(0);
+ });
+ });
+
+ describe('preservesPitch', () => {
+ it('Return true by default', () => {
+ expect(element.preservesPitch).toBe(true);
+ });
+
+ for (const property of [null, undefined, false]) {
+ it(`Set false with ${property}`, () => {
+ element.preservesPitch = property;
+ expect(element.preservesPitch).toBe(false);
+ });
+ }
+ });
+
+ describe('readyState', () => {
+ it('Return 0 by default', () => {
+ expect(element.readyState).toBe(0);
+ });
+ });
});