Skip to content

Commit

Permalink
Merge branch 'v6.X' into PWA-1618-Provide-simple-custom-page-route-he…
Browse files Browse the repository at this point in the history
…lper
  • Loading branch information
devbucket committed Apr 11, 2019
2 parents 232600e + 211a86b commit f3f5c0c
Show file tree
Hide file tree
Showing 24 changed files with 598 additions and 947 deletions.
69 changes: 69 additions & 0 deletions libraries/common/collections/media-providers/MediaProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { logger } from '@shopgate/pwa-core/helpers';
import styles from './style';

/**
* The MediaProvider base class.
*/
class MediaProvider {
/**
* Constructor.
*/
constructor() {
this.containers = new Map();
}

/**
* Optimizes video container to make it responsive.
* @param {NodeList} container A DOM container.
* @private
* @returns {MediaProvider}
*/
responsify(container) {
// Remove fixed dimensions from the container.
container.removeAttribute('height');
container.removeAttribute('width');

// Create the wrapper and apply styling.
const wrapper = document.createElement('div');

wrapper.className = styles;

// Add the wrapper right before the container into the DOM.
container.parentNode.insertBefore(wrapper, container);
// Move the container into the wrapper.
wrapper.appendChild(container);

return this;
}

/**
* Add a DOM container with embedded videos.
* @param {NodeList} container A DOM container.
* @returns {MediaProvider}
*/
add() {
logger.error('MediaProvider.add() needs to be implemented within an inheriting class');
return this;
}

/**
* Remove a DOM container.
* @param {NodeList} container A DOM container.
* @returns {MediaProvider}
*/
remove(container) {
this.containers.delete(container);
return this;
}

/**
* Stops all playing videos within the DOM containers.
* @returns {MediaProvider}
*/
stop() {
logger.error('MediaProvider.stop() needs to be implemented within an inheriting class');
return this;
}
}

export default MediaProvider;
87 changes: 87 additions & 0 deletions libraries/common/collections/media-providers/MediaProvider.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { JSDOM } from 'jsdom';
import { logger } from '@shopgate/pwa-core/helpers';
import MediaProvider from './MediaProvider';
import styles from './style';

jest.mock('@shopgate/pwa-core/helpers', () => ({
logger: {
error: jest.fn(),
},
}));

/**
* Creates a DOM container with iframes.
* @param {Array} srcs A list of video URLs.
* @return {Object}
*/
const createContainer = (srcs) => {
const html = srcs.map(src => `<iframe src="${src}"></iframe>`).join('');
return new JSDOM(html).window.document;
};

describe('MediaProvider', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('.constructor()', () => {
it('should construct as expected', () => {
const instance = new MediaProvider();
expect(instance.containers).toBeInstanceOf(Map);
expect(Array.from(instance.containers)).toHaveLength(0);
});
});

describe('.add()', () => {
it('should log an error when it is not overwritten', () => {
const instance = new MediaProvider();
instance.add();
expect(logger.error).toHaveBeenCalledTimes(1);
});
});

describe('.stop()', () => {
it('should log an error when it is not overwritten', () => {
const instance = new MediaProvider();
instance.stop();
expect(logger.error).toHaveBeenCalledTimes(1);
});
});

describe('.remove()', () => {
it('should remove containers as expected', () => {
const instance = new MediaProvider();
const containerOne = createContainer(['http://www.provider-one.com/video']);
const containerTwo = createContainer(['http://www.provider-two.com/video']);

instance.containers.set(containerOne, containerOne);
instance.containers.set(containerTwo, containerTwo);

instance.remove(containerTwo);

expect(instance.containers.size).toBe(1);
expect(instance.containers.get(containerTwo)).toBeUndefined();
expect(instance.containers.get(containerOne)).toEqual(containerOne);

instance.remove(containerOne);

expect(instance.containers.size).toBe(0);
});
});

describe('.responsify()', () => {
it('should responsify a dom container', () => {
const dom = document.createElement('body');
dom.innerHTML = '<iframe width="560" height="315" />';
const container = dom.querySelectorAll('iframe')[0];

const instance = new MediaProvider();
instance.responsify(container);

expect(dom).toMatchSnapshot();
expect(container.getAttribute('height')).toBeNull();
expect(container.getAttribute('width')).toBeNull();
expect(container.closest('div').className).toBe(styles);
});
});
});
34 changes: 17 additions & 17 deletions libraries/common/collections/media-providers/Vimeo.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
/* eslint-disable extra-rules/potential-point-free */
import MediaProvider from './MediaProvider';

/**
* The Vimeo media provider class.
*/
class VimeoMediaProvider {
class VimeoMediaProvider extends MediaProvider {
/**
* Constructor.
*/
constructor() {
this.containers = new Map();
super();
this.playerReady = false;
this.deferred = [];

this.checkPlayer();
this.initPlayer();
}

/**
* Checks if the Video player script is already available.
* If not, it injects it into the DOM and adds defferred containers.
* If not, it injects it into the DOM and adds deferred containers.
* @private
*/
checkPlayer() {
initPlayer() {
if (typeof window.Vimeo !== 'undefined') {
this.playerReady = true;
return;
Expand All @@ -42,49 +43,48 @@ class VimeoMediaProvider {

/**
* Add a DOM container with embedded videos.
* @override
* @param {NodeList} container A DOM container.
* @returns {VimeoMediaProvider}
*/
add(container) {
if (!this.playerReady) {
this.deferred.push(container);
return;
return this;
}

const iframes = container.querySelectorAll('iframe[src*="vimeo.com"]');

if (!iframes.length) {
return;
return this;
}

const players = [];

iframes.forEach((iframe) => {
this.responsify(iframe);
players.push(new window.Vimeo.Player(iframe));
});

this.containers.set(container, players);
}

/**
* Remove a DOM container.
* @param {NodeList} container A DOM container.
*/
remove(container) {
this.containers.delete(container);
return this;
}

/**
* Stops all playing videos within the DOM containers.
* @override
* @returns {VimeoMediaProvider}
*/
stop() {
this.containers.forEach((players) => {
players.forEach((player) => {
player.pause();
});
});

return this;
}
}

/* eslint-enable extra-rules/potential-point-free */

export default VimeoMediaProvider;
8 changes: 7 additions & 1 deletion libraries/common/collections/media-providers/Vimeo.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ describe('Vimeo media provider', () => {

document.getElementsByTagName('html')[0].innerHTML = '';
instance = new Vimeo();
instance.responsify = jest.fn();

playerScript = document.querySelector('script[src*="vimeo.com"]');
};

Expand All @@ -66,7 +68,7 @@ describe('Vimeo media provider', () => {
});
});

describe('.checkPlayer()', () => {
describe('.initPlayer()', () => {
it('should not inject the player script when it is already loaded', () => {
expect(instance.playerReady).toBe(true);
expect(playerScript).toBeNull();
Expand All @@ -91,6 +93,8 @@ describe('Vimeo media provider', () => {
it('should add multiple containers as expected', () => {
const containerOne = createContainer([videos[0]]);
const containerTwo = createContainer([videos[1]]);
const iframesOne = containerOne.querySelectorAll('iframe');
const iframesTwo = containerTwo.querySelectorAll('iframe');

instance.add(containerOne);
instance.add(containerTwo);
Expand All @@ -99,6 +103,8 @@ describe('Vimeo media provider', () => {
expect(instance.containers.size).toBe(2);
expect(instance.containers.get(containerOne)).toEqual([expect.any(window.Vimeo.Player)]);
expect(instance.containers.get(containerTwo)).toEqual([expect.any(window.Vimeo.Player)]);
expect(instance.responsify).toHaveBeenCalledWith(iframesOne[0]);
expect(instance.responsify).toHaveBeenCalledWith(iframesTwo[0]);
});

it('should defer addition of a container if the player is not ready', () => {
Expand Down
31 changes: 12 additions & 19 deletions libraries/common/collections/media-providers/YouTube.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
/* eslint-disable extra-rules/potential-point-free */
import URLSearchParams from 'url-search-params';
import MediaProvider from './MediaProvider';

/**
* The YouTube media provider class.
*/
class YouTubeMediaProvider {
/**
* Constructor.
*/
constructor() {
this.containers = new Map();
}

class YouTubeMediaProvider extends MediaProvider {
/**
* Add a DOM container with embedded videos.
* @override
* @param {NodeList} container A DOM container.
* @returns {YouTubeMediaProvider}
*/
add(container) {
const iframes = container
.querySelectorAll('iframe[src*="youtube.com"], iframe[src*="youtube-nocookie.com"]');

if (!iframes.length) {
return;
return this;
}

// Update the video urls to enable stopping videos via the event API.
iframes.forEach((iframe, index) => {
this.responsify(iframe);

const { src } = iframe;

const [url, query] = src.split('?');
Expand All @@ -40,18 +37,14 @@ class YouTubeMediaProvider {
});

this.containers.set(container, iframes);
}

/**
* Remove a DOM container.
* @param {NodeList} container A DOM container.
*/
remove(container) {
this.containers.delete(container);
return this;
}

/**
* Stops all playing videos within the DOM containers.
* @override
* @returns {YouTubeMediaProvider}
*/
stop() {
this.containers.forEach((iframes) => {
Expand All @@ -61,9 +54,9 @@ class YouTubeMediaProvider {
}
});
});

return this;
}
}

/* eslint-enable extra-rules/potential-point-free */

export default YouTubeMediaProvider;
8 changes: 6 additions & 2 deletions libraries/common/collections/media-providers/YouTube.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const videos = [
* @return {Object}
*/
const createContainer = (srcs) => {
const html = srcs.map(src => `<iframe src="${src}"></iframe>`).join('');
const iframes = srcs.map(src => `<iframe src="${src}"></iframe>`).join('');
const html = `<div>${iframes}</div>`;
return new JSDOM(html).window.document;
};

Expand All @@ -24,6 +25,7 @@ describe('YouTube media provider', () => {

beforeEach(() => {
instance = new YouTube();
instance.responsify = jest.fn();
});

describe('.constructor()', () => {
Expand All @@ -47,6 +49,8 @@ describe('YouTube media provider', () => {
expect(instance.containers.size).toBe(2);
expect(instance.containers.get(containerOne)).toEqual(iframesOne);
expect(instance.containers.get(containerTwo)).toEqual(iframesTwo);
expect(instance.responsify).toHaveBeenCalledWith(iframesOne[0]);
expect(instance.responsify).toHaveBeenCalledWith(iframesTwo[0]);
});

it('should add a container with different types of YouTube videos', () => {
Expand Down Expand Up @@ -97,7 +101,7 @@ describe('YouTube media provider', () => {
});

describe('.stop()', () => {
it('should stop the videos within mutiple containers', () => {
it('should stop the videos within multiple containers', () => {
const postMessageMock = jest.fn();
const containerOne = createContainer([videos[0]]);
const containerTwo = createContainer([videos[1], videos[2]]);
Expand Down

0 comments on commit f3f5c0c

Please sign in to comment.