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 b5cadaea..e4568d98 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,4 @@
+import Event from '../../event/Event';
import DOMException from '../../exception/DOMException';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum';
import HTMLElement from '../html-element/HTMLElement';
@@ -11,6 +12,7 @@ import IHTMLMediaElement from './IHTMLMediaElement';
*/
export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaElement {
#volume = 1;
+ #paused = true;
/**
* Returns autoplay.
*
@@ -55,28 +57,6 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
}
}
- /**
- * Returns paused.
- *
- * @returns Paused.
- */
- public get paused(): boolean {
- return this.getAttributeNS(null, 'paused') !== null;
- }
-
- /**
- * Sets paused.
- *
- * @param paused Paused.
- */
- public set paused(paused: boolean) {
- if (!paused) {
- this.removeAttributeNS(null, 'paused');
- } else {
- this.setAttributeNS(null, 'paused', '');
- }
- }
-
/**
* Returns loop.
*
@@ -120,6 +100,36 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
}
}
+ /**
+ * Returns src.
+ *
+ * @returns Src.
+ */
+ public get src(): string {
+ return this.getAttributeNS(null, 'src') || '';
+ }
+
+ /**
+ * Sets src.
+ *
+ * @param src Src.
+ */
+ public set src(src: string) {
+ this.setAttributeNS(null, 'src', src);
+ if (Boolean(src)) {
+ this.dispatchEvent(new Event('canplay', { bubbles: false, cancelable: false }));
+ }
+ }
+
+ /**
+ * Returns currentSrc.
+ *
+ * @returns CurrentrSrc.
+ */
+ public get currentSrc(): string {
+ return this.src;
+ }
+
/**
* Returns volume.
*
@@ -151,11 +161,62 @@ export default class HTMLMediaElement extends HTMLElement implements IHTMLMediaE
this.#volume = parsedVolume;
}
+ /**
+ * Returns crossOrigin.
+ *
+ * @returns CrossOrigin.
+ */
+ public get crossOrigin(): string {
+ return this.getAttributeNS(null, 'crossorigin');
+ }
+
+ /**
+ * Sets crossOrigin.
+ *
+ * @param crossOrigin CrossOrigin.
+ */
+ public set crossOrigin(crossOrigin: string | null) {
+ if (crossOrigin === null) {
+ return
+ }
+
+ if (['', 'use-credentials', 'anonymous'].includes(crossOrigin)) {
+ this.setAttributeNS(null, 'crossorigin', crossOrigin);
+ } else {
+ this.setAttributeNS(null, 'crossorigin', 'anonymous');
+ }
+ }
+
+ /**
+ * Returns paused.
+ *
+ * @returns Paused.
+ */
+ public get paused(): boolean {
+ return this.#paused;
+ }
+
/**
*
*/
public pause(): void {
- this.paused = true;
+ this.#paused = true;
+ }
+
+ /**
+ *
+ */
+ public async play(): Promise {
+ this.#paused = false;
+ return Promise.resolve();
+ }
+
+ /**
+ *
+ * @param _type
+ */
+ public canPlayType(_type: string): string {
+ return '';
}
/**
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 65089d3d..ee62b71d 100644
--- a/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts
+++ b/packages/happy-dom/src/nodes/html-media-element/IHTMLMediaElement.ts
@@ -7,13 +7,20 @@ import IHTMLElement from '../html-element/IHTMLElement';
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement.
*/
export default interface IHTMLMediaElement extends IHTMLElement {
- // AddTextTrack;
+ readonly currentSrc: string;
autoplay: boolean;
+ controls: boolean;
+ loop: boolean;
+ muted: boolean;
+ paused: boolean; // TODO readonly?
+ volume: number | string;
+ src: string;
+ crossOrigin: string; // Only anonymus and 'use-credentials' is valid
+
+ // AddTextTrack;
// Buffered; // TODO
// CaptureStream; // TODO
- controls: boolean;
// ControlsList: string; // TODO
- // CrossOrigin: string; // TODO: enum?
// CurrentSrc: string; // TODO
// CurrentTime; // TODO
// DefaultMuted: boolean; // TODO
@@ -22,14 +29,10 @@ export default interface IHTMLMediaElement extends IHTMLElement {
// Duration: number; // TODO
// Ended: boolean; // TODO readonly
// Error; // TODO object
- loop: boolean;
// MediaKeys; // TODO
- muted: boolean;
// NetworkState; // 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.
@@ -40,7 +43,14 @@ 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;
+
+ /**
+ * 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;
/**
* 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 e6cbc208..e73665b9 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
@@ -4,6 +4,7 @@ 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';
+import Event from '../../../src/event/Event';
describe('HTMLMediaElement', () => {
let window: IWindow;
@@ -20,7 +21,7 @@ describe('HTMLMediaElement', () => {
jest.restoreAllMocks();
});
- for (const property of ['autoplay', 'controls', 'paused', 'loop', 'muted']) {
+ for (const property of ['autoplay', 'controls', 'loop', 'muted']) {
describe(`get ${property}()`, () => {
it('Returns attribute value.', () => {
expect(element[property]).toBe(false);
@@ -37,10 +38,52 @@ describe('HTMLMediaElement', () => {
});
}
+ for (const property of ['src']) {
+ describe(`get ${property}()`, () => {
+ it(`Returns the "${property}" attribute.`, () => {
+ element.setAttribute(property, 'test');
+ expect(element[property]).toBe('test');
+ });
+ });
+
+ describe(`set ${property}()`, () => {
+ it(`Sets the attribute "${property}".`, () => {
+ element[property] = 'test';
+ expect(element.getAttribute(property)).toBe('test');
+ });
+ });
+ }
+
+ describe('canplay event', () => {
+ it('Should dispatch after src set', () => {
+ let dispatchedEvent: Event = null;
+ element.addEventListener('canplay', (event: Event) => (dispatchedEvent = event));
+ element.src = 'https://songURL';
+ expect(dispatchedEvent.cancelable).toBe(false);
+ expect(dispatchedEvent.bubbles).toBe(false);
+ });
+ it('Should not dispatch if src is empty', () => {
+ let dispatchedEvent: Event = null;
+ element.addEventListener('canplay', (event: Event) => (dispatchedEvent = event));
+ element.src = '';
+ expect(dispatchedEvent).toBeNull();
+ });
+ });
+
describe('paused', () => {
- it('Set paused attribute as well', () => {
+ it('Default is true', () => {
+ expect(element.paused).toBeTruthy();
+ });
+
+ it('Set false with play', () => {
+ element.play();
+ expect(element.paused).toBeFalsy();
+ });
+
+ it('Set true with pause', () => {
+ element.play();
element.pause();
- expect(element.getAttribute('paused')).toBe('');
+ expect(element.paused).toBeTruthy();
});
});
@@ -82,4 +125,24 @@ describe('HTMLMediaElement', () => {
});
}
});
+
+ describe('canPlayType', () => {
+ it('Returns empty string', () => {
+ expect(element.canPlayType('notValidMIMEtype')).toBe('');
+ });
+ });
+
+ describe('CrossOrigin', () => {
+ for (const crossOrigin of ['', null, 'use-credentials', 'anonymous']) {
+ it(`Set ${crossOrigin} as a valid crossOrigin`, () => {
+ element.crossOrigin = crossOrigin;
+ expect(element.getAttribute('crossorigin')).toBe(crossOrigin);
+ });
+ }
+
+ it(`Return 'anonymous' if crossOrigin is not valid`, () => {
+ element.crossOrigin = 'randomString';
+ expect(element.getAttribute('crossorigin')).toBe('anonymous');
+ });
+ });
});