From 0cf630aea06621e3098b5567d9aab96471717644 Mon Sep 17 00:00:00 2001 From: rudywaltz Date: Fri, 10 Jun 2022 16:07:13 +0200 Subject: [PATCH] #475@minor: Add crossOrigin and canPlayType to HTMLMediaElement. --- .../html-media-element/HTMLMediaElement.ts | 107 ++++++++++++++---- .../html-media-element/IHTMLMediaElement.ts | 26 +++-- .../HTMLMediaElement.test.ts | 69 ++++++++++- 3 files changed, 168 insertions(+), 34 deletions(-) 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'); + }); + }); });