From 56a98c32ded38017663ddf35fa48c7a3cbd1607c Mon Sep 17 00:00:00 2001 From: Yuval Saraf Date: Mon, 25 Jul 2016 17:58:59 +0300 Subject: [PATCH] Finished unit tests, 100% --- karma.conf.js | 15 +- package.json | 1 + src/set-events-for-template-helpers.js | 41 +++ src/set-events-for-template.js | 64 +---- test/index.js | 1 + test/mock/emoji-data.js | 27 ++ .../set-events-for-template-helpers-spec.js | 111 ++++++++ test/spec/set-events-for-template-spec.js | 246 ++++++++++++++++++ test/spec/set-spec.js | 12 +- test/spec/template-spec.js | 94 +++++++ webpack.config.js | 4 - 11 files changed, 550 insertions(+), 66 deletions(-) create mode 100644 src/set-events-for-template-helpers.js create mode 100644 test/mock/emoji-data.js create mode 100644 test/spec/set-events-for-template-helpers-spec.js create mode 100644 test/spec/set-events-for-template-spec.js create mode 100644 test/spec/template-spec.js diff --git a/karma.conf.js b/karma.conf.js index 87b0b39..8c6e659 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -48,8 +48,8 @@ module.exports = function karmaConfig(config) { } }, // Override `module` in `webpackConfig` only if coverage is produced - module: Object.assign(webpackConfig.module, isCoverage ? { - preLoaders: webpackConfig.module.preLoaders.concat([ + module: Object.assign(webpackConfig.module, { + preLoaders: webpackConfig.module.preLoaders.concat(isCoverage ? [ { // `isparta` all the code We want to be in the coverage report test: /\.js$/, include: [ @@ -65,10 +65,15 @@ module.exports = function karmaConfig(config) { ], loader: 'babel?presets[]=es2015' } - ]), + ] : []), // Exclude js loaders from `loaders` because they are set in preLoaders - loaders: webpackConfig.module.loaders.filter(loaderObj => (typeof loaderObj.test !== 'function' && loaderObj.test.toString().indexOf('.js') === -1)) - } : {}) + loaders: webpackConfig.module.loaders + .filter(loaderObj => (typeof loaderObj.test !== 'function' && loaderObj.test.toString().indexOf('.js') === -1)) + .concat(isCoverage ? [] : [{ + test: /\.js$/, + loader: 'babel?presets[]=es2015' + }]) + }) }), webpackMiddleware: { noInfo: true diff --git a/package.json b/package.json index 0834b37..6bc3739 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "absurd-loader": "^0.0.3", "babel-eslint": "^6.0.2", "babel-loader": "^6.2.4", + "babel-polyfill": "^6.9.1", "babel-preset-es2015": "^6.6.0", "bower": "^1.7.9", "coveralls": "^2.11.11", diff --git a/src/set-events-for-template-helpers.js b/src/set-events-for-template-helpers.js new file mode 100644 index 0000000..6244dc0 --- /dev/null +++ b/src/set-events-for-template-helpers.js @@ -0,0 +1,41 @@ +import categoryOrder from './category-order'; +import { CATEGORY } from './constant'; + +export const getIsElementScrollable = el => { + return el.clientHeight !== el.scrollHeight; +}; + +export const round = num => ((num * 2).toFixed() / 2); + +export const scrollElementTo = (el, done, newScrollHeight = 0, scrollDuration = 300) => { + const scrollHeight = el.scrollTop; + const scrollDiff = scrollHeight - newScrollHeight; + const scrollStep = Math.PI / (scrollDuration / 15); + const cosParameter = scrollDiff / 2; + let scrollCount = 0; + let scrollMargin; + + const step = () => { + setTimeout(() => { + if (el.scrollTop !== newScrollHeight) { + requestAnimationFrame(step); + scrollCount = scrollCount + 1; + scrollMargin = round(cosParameter - cosParameter * Math.cos(scrollCount * scrollStep)); + el.scrollTop = scrollHeight - scrollMargin; + } else { + done(); + } + }, 15); + }; + + requestAnimationFrame(step); +}; + +const marginParam = 100 / categoryOrder.length; +export const slideToCategory = (panelVariables, slideEl, categoryId) => { + if (panelVariables.selectedCategoryId !== categoryId) { + panelVariables.selectedCategoryId = categoryId; + const selectedCategoryIndex = categoryOrder.indexOf(categoryId); + slideEl.style.marginLeft = selectedCategoryIndex * marginParam + '%'; + } +}; diff --git a/src/set-events-for-template.js b/src/set-events-for-template.js index b5d9f8f..bacb78a 100644 --- a/src/set-events-for-template.js +++ b/src/set-events-for-template.js @@ -1,44 +1,4 @@ -import categoryOrder from './category-order'; -import { CATEGORY } from './constant'; - -const getIsElementScrollable = el => { - return el.clientHeight !== el.scrollHeight; -}; - -const round = num => ((num * 2).toFixed() / 2); - -const scrollElementTo = (el, done, newScrollHeight = 0, scrollDuration = 300) => { - const scrollHeight = el.scrollTop; - const scrollDiff = scrollHeight - newScrollHeight; - const scrollStep = Math.PI / (scrollDuration / 15); - const cosParameter = scrollDiff / 2; - let scrollCount = 0; - let scrollMargin; - - const step = () => { - setTimeout(() => { - if (el.scrollTop !== newScrollHeight) { - requestAnimationFrame(step); - scrollCount = scrollCount + 1; - scrollMargin = round(cosParameter - cosParameter * Math.cos(scrollCount * scrollStep)); - el.scrollTop = scrollHeight - scrollMargin; - } else { - done(); - } - }, 15); - }; - - requestAnimationFrame(step); -}; - -const marginParam = 100 / categoryOrder.length; -const slideToCategory = (panelVariables, slideEl, categoryId) => { - if (panelVariables.selectedCategoryId !== categoryId) { - panelVariables.selectedCategoryId = categoryId; - const selectedCategoryIndex = categoryOrder.indexOf(categoryId); - slideEl.style.marginLeft = selectedCategoryIndex * marginParam + '%'; - } -}; +import { getIsElementScrollable, scrollElementTo, slideToCategory } from './set-events-for-template-helpers'; export default (el, { animationDuration, eventListeners } = {}) => { const panelVariables = { @@ -47,15 +7,15 @@ export default (el, { animationDuration, eventListeners } = {}) => { const categoriesEl = el.querySelector('.ep-categories'); const slideEl = el.querySelector('.ep-slide'); - const emojiesContainer = el.querySelector('.ep-emojies'); + const emojiesContainerEl = el.querySelector('.ep-emojies'); // Set styles slideEl.style.transitionDuration = animationDuration + 'ms'; const scrollListener = () => { - const scrollHeight = emojiesContainer.scrollTop; - const emojiesContainerChildren = Array.from(emojiesContainer.children); - const lastVisibleContainerChild = emojiesContainerChildren + const scrollHeight = emojiesContainerEl.scrollTop; + const emojiesContainerElChildren = Array.from(emojiesContainerEl.children); + const lastVisibleContainerChild = emojiesContainerElChildren .find(node => scrollHeight >= node.offsetTop && (!node.nextElementSibling || scrollHeight < node.nextElementSibling.offsetTop)); const categoryId = Number(lastVisibleContainerChild.dataset.categoryId); @@ -69,19 +29,19 @@ export default (el, { animationDuration, eventListeners } = {}) => { target = target.parentElement; } if (target.classList.contains('ep-c')) { - const isElementScrollable = getIsElementScrollable(emojiesContainer); + const isElementScrollable = getIsElementScrollable(emojiesContainerEl); if (isElementScrollable) { const categoryId = Number(target.dataset.categoryId); - const categoryEl = emojiesContainer.querySelector(`[data-category-id="${categoryId}"]`); + const categoryEl = emojiesContainerEl.querySelector(`[data-category-id="${categoryId}"]`); const categoryHeight = categoryEl.offsetTop; panelVariables.isMidScrollAnimation = true; // Remove scroll event listener for better performance - emojiesContainer.removeEventListener('scroll', scrollListener); - scrollElementTo(emojiesContainer, () => { + emojiesContainerEl.removeEventListener('scroll', scrollListener); + scrollElementTo(emojiesContainerEl, () => { panelVariables.isMidScrollAnimation = false; // Readd scroll event listener after javascript animation has finished - emojiesContainer.addEventListener('scroll', scrollListener); + emojiesContainerEl.addEventListener('scroll', scrollListener); }, categoryHeight, animationDuration); slideToCategory(panelVariables, slideEl, categoryId); @@ -90,10 +50,10 @@ export default (el, { animationDuration, eventListeners } = {}) => { } }); - emojiesContainer.addEventListener('scroll', scrollListener); + emojiesContainerEl.addEventListener('scroll', scrollListener); if (eventListeners.onClick) { - emojiesContainer.addEventListener('click', e => { + emojiesContainerEl.addEventListener('click', e => { const target = e.target; if (target.classList.contains('ep-e')) { const index = Number(target.dataset.index); diff --git a/test/index.js b/test/index.js index ccb1476..157ee3a 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,4 @@ +import 'babel-polyfill'; // Disabling `no-undef` because we are using here require.context which requires files dynamically, instead of regular ES6 imports /* eslint-disable no-undef */ diff --git a/test/mock/emoji-data.js b/test/mock/emoji-data.js new file mode 100644 index 0000000..da7729c --- /dev/null +++ b/test/mock/emoji-data.js @@ -0,0 +1,27 @@ +import { CATEGORY } from 'src/constant'; +export default { + [CATEGORY.ACTIVITY]: [ + { + index: 0, + unified: 'a' + }, { + index: 1, + unified: 'b' + } + ], + [CATEGORY.FLAGS]: [ + { + index: 2, + unified: 'c' + }, { + index: 3, + unified: 'd' + } + ], + [CATEGORY.FOODS]: [], + [CATEGORY.NATURE]: [], + [CATEGORY.OBJECTS]: [], + [CATEGORY.PEOPLE]: [], + [CATEGORY.PLACES]: [], + [CATEGORY.SYMBOLS]: [] +}; diff --git a/test/spec/set-events-for-template-helpers-spec.js b/test/spec/set-events-for-template-helpers-spec.js new file mode 100644 index 0000000..0733029 --- /dev/null +++ b/test/spec/set-events-for-template-helpers-spec.js @@ -0,0 +1,111 @@ +import { getIsElementScrollable, round, scrollElementTo, slideToCategory } from 'src/set-events-for-template-helpers'; +import categoryOrder from 'src/category-order'; +import { CATEGORY } from 'src/constant'; + +describe('`set-events-for-template-helpers`', () => { + describe('has `getIsElementScrollable` function which returns', () => { + it('false if `clientHeight` equals `scrollHeight`', () => { + expect(getIsElementScrollable({ + clientHeight: 12, + scrollHeight: 12 + })).toBeFalsy(); + }); + it('true if `clientHeight` is different than `scrollHeight`', () => { + expect(getIsElementScrollable({ + clientHeight: 200, + scrollHeight: 12 + })).toBeTruthy(); + }); + }); + describe('has `round` function', () => { + it('that rounds up numbers', () => { + expect(round(1.77)).toBe(2); + }); + it('that rounds down numbers', () => { + expect(round(1.12)).toBe(1); + }); + describe('that rounds to half', () => { + it('when is half', () => { + expect(round(1.5)).toBe(1.5); + }); + it('when is bellow half but closer to half than whole number', () => { + expect(round(1.32)).toBe(1.5); + }); + it('when is higher than half but closer to half than whole number', () => { + expect(round(1.72)).toBe(1.5); + }); + }); + }); + describe('has `slideToCategory` function', () => { + let panelVariables; + let slideEl; + beforeEach(() => { + panelVariables = { + selectedCategoryId: categoryOrder[0] + }; + slideEl = { + style: { + marginLeft: '0%' + } + }; + }); + it('that does nothing if categoryId matches the `panelVariables.selectedCategoryId`', () => { + slideToCategory(panelVariables, slideEl, categoryOrder[0]); + expect(panelVariables.selectedCategoryId).toBe(categoryOrder[0]); + expect(slideEl.style.marginLeft).toBe('0%'); + }); + it('that changes `panelVariables.selectedCategoryId` and `slideEl.style.marginLeft` (based on how many categories there are) if given differentCategoryId', () => { + const indexOfNewCatergyId = 3; + const newCategoryId = categoryOrder[indexOfNewCatergyId]; + slideToCategory(panelVariables, slideEl, newCategoryId); + expect(panelVariables.selectedCategoryId).toBe(newCategoryId); + expect(slideEl.style.marginLeft).toBe(`${(indexOfNewCatergyId / categoryOrder.length) * 100}%`); + }); + }); + describe('has `scrollElementTo` function that does scroll animation', () => { + let funcArray; + /** + * Loops through funcArray and runs all of the functions + */ + const runFuncArray = fn => { + let i = 0; + while (i < funcArray.length) { + funcArray[i](); + // Middleware function + if (fn) { + fn(); + } + i++; + } + }; + /** + * beforeEach sets resets array to empty, and each time raf or setTimeout is called, adds them to the array to be run by `runFuncArray` + */ + beforeEach(() => { + funcArray = []; + spyOn(window, 'setTimeout').and.callFake(fn => { + funcArray.push(fn); + }); + spyOn(window, 'requestAnimationFrame').and.callFake(fn => { + funcArray.push(fn); + }); + }); + it('`done` is called once', () => { + const doneSpy = jasmine.createSpy('done'); + scrollElementTo({ + scrollTop: 0 + }, doneSpy, 300); + runFuncArray(); + expect(doneSpy).toHaveBeenCalledTimes(1); + }); + it('step function is called every 15ms', () => { + const animationTime = 310; + const timesStepShouldBeCalled = Math.ceil(animationTime / 15); + scrollElementTo({ + scrollTop: 0 + }, () => {}, animationTime); + runFuncArray(); + expect(window.setTimeout).toHaveBeenCalledTimes(timesStepShouldBeCalled); + }); + }); +}); diff --git a/test/spec/set-events-for-template-spec.js b/test/spec/set-events-for-template-spec.js new file mode 100644 index 0000000..b4a09a6 --- /dev/null +++ b/test/spec/set-events-for-template-spec.js @@ -0,0 +1,246 @@ +import setEventsForTemplateInjector from 'inject!src/set-events-for-template'; +import categoryOrder from 'src/category-order'; +const animationDuration = 300; + +describe('`set-events-for-template`', () => { + let el; + let categoriesEl; + let slideEl; + let emojiesContainerEl; + let setEventsForTemplate; + let slideToCategorySpy; + let scrollElementToSpy; + let getIsElementScrollableSpy; + let fireScrollToDone; + beforeEach(() => { + slideToCategorySpy = jasmine.createSpy('slideToCategorySpy'); + fireScrollToDone = () => { scrollElementToDone(); }; + let scrollElementToDone; + scrollElementToSpy = jasmine.createSpy('scrollElementToSpy').and.callFake((el, done) => { scrollElementToDone = done; }); + getIsElementScrollableSpy = jasmine.createSpy('getIsElementScrollableSpy').and.returnValue(true); + setEventsForTemplate = setEventsForTemplateInjector({ + './set-events-for-template-helpers': { + slideToCategory: slideToCategorySpy, + scrollElementTo: scrollElementToSpy, + getIsElementScrollable: getIsElementScrollableSpy + } + }).default; + categoriesEl = { + addEventListener: () => {} + }; + slideEl = { + style: {} + }; + // Set emojiesContainerEl to have some properties used in scroll event + emojiesContainerEl = { + addEventListener: () => {}, + removeEventListener: () => {}, + querySelector: () => {}, + children: categoryOrder + .map((categoryId, i) => ({ + offsetTop: 200 * i, + dataset: { + categoryId + } + })) + .map((x, i, arr) => { + x.nextElementSibling = arr[i + 1]; + return x; + }) + }; + el = { + querySelector: query => { + switch (query) { + case '.ep-categories': + return categoriesEl; + case '.ep-slide': + return slideEl; + case '.ep-emojies': + return emojiesContainerEl; + } + } + }; + }); + it('should find elements to set events to from given element by className', () => { + const eventListeners = {}; + spyOn(el, 'querySelector').and.callThrough(); + setEventsForTemplate(el, { animationDuration, eventListeners }); + expect(el.querySelector.calls.allArgs()).toEqual([['.ep-categories'], ['.ep-slide'], ['.ep-emojies']]); + }); + describe('should add `click` event to categoriesEl', () => { + it('once', () => { + const eventListeners = {}; + spyOn(categoriesEl, 'addEventListener'); + setEventsForTemplate(el, { animationDuration, eventListeners }); + expect(categoriesEl.addEventListener).toHaveBeenCalledTimes(1); + expect(categoriesEl.addEventListener.calls.argsFor(0)).toEqual(['click', jasmine.any(Function)]); + }); + describe('which fires an event that', () => { + let event; + const fireEvent = e => { + event(e); + }; + const eventListeners = {}; + beforeEach(() => { + spyOn(categoriesEl, 'addEventListener').and.callFake((eventType, eventHanler) => { + event = eventHanler; + }); + setEventsForTemplate(el, { animationDuration, eventListeners }); + }); + it('does nothing if target class is not category\'s icon', () => { + spyOn(emojiesContainerEl, 'querySelector').and.returnValue({}); + fireEvent({ + target: document.createElement('span') + }); + expect(emojiesContainerEl.querySelector).not.toHaveBeenCalled(); + }); + it('continues if icon is clicked', () => { + const catEl = document.createElement('span'); + catEl.setAttribute('class', 'ep-c'); + catEl.setAttribute('data-category-id', '2'); + const catIconEl = document.createElement('span'); + catIconEl.setAttribute('class', 'cat'); + catEl.appendChild(catIconEl); + spyOn(emojiesContainerEl, 'querySelector').and.returnValue({}); + fireEvent({ + target: catIconEl + }); + expect(emojiesContainerEl.querySelector).toHaveBeenCalledTimes(1); + }); + it('continues if category is clicked', () => { + const catEl = document.createElement('span'); + catEl.setAttribute('class', 'ep-c'); + catEl.setAttribute('data-category-id', '2'); + spyOn(emojiesContainerEl, 'querySelector').and.returnValue({}); + fireEvent({ + target: catEl + }); + expect(emojiesContainerEl.querySelector).toHaveBeenCalledTimes(1); + }); + it('removes `scroll` event from emojis and then returns it', () => { + const catEl = document.createElement('span'); + catEl.setAttribute('class', 'ep-c'); + catEl.setAttribute('data-category-id', '2'); + spyOn(emojiesContainerEl, 'querySelector').and.returnValue({}); + spyOn(emojiesContainerEl, 'addEventListener'); + spyOn(emojiesContainerEl, 'removeEventListener'); + fireEvent({ + target: catEl + }); + expect(emojiesContainerEl.removeEventListener).toHaveBeenCalledTimes(1); + fireScrollToDone(); // Animation end + expect(emojiesContainerEl.addEventListener).toHaveBeenCalledTimes(1); + }); + it('will not call `slideToCategory` and `scrollElementTo` twice in a row (if clicks before animation ends)', () => { + const catEl = document.createElement('span'); + catEl.setAttribute('class', 'ep-c'); + catEl.setAttribute('data-category-id', '2'); + spyOn(emojiesContainerEl, 'querySelector').and.returnValue({}); + fireEvent({ + target: catEl + }); + fireEvent({ + target: catEl + }); + expect(scrollElementToSpy).toHaveBeenCalledTimes(1); + expect(slideToCategorySpy).toHaveBeenCalledTimes(1); + }); + it('will not call `slideToCategory` and `scrollElementTo` twice in a row (if clicks before animation ends)', () => { + const catEl = document.createElement('span'); + catEl.setAttribute('class', 'ep-c'); + catEl.setAttribute('data-category-id', '2'); + spyOn(emojiesContainerEl, 'querySelector').and.returnValue({ offsetTop: 200 }); + fireEvent({ + target: catEl + }); + expect(scrollElementToSpy).toHaveBeenCalledWith(emojiesContainerEl, jasmine.any(Function), 200, animationDuration); + expect(slideToCategorySpy).toHaveBeenCalledWith(jasmine.any(Object), slideEl, 2); + }); + }); + }); + describe('should addEvents to emoji - ', () => { + it('once when no `onClick` given', () => { + const eventListeners = {}; + spyOn(emojiesContainerEl, 'addEventListener'); + setEventsForTemplate(el, { animationDuration, eventListeners }); + expect(emojiesContainerEl.addEventListener).toHaveBeenCalledTimes(1); + expect(emojiesContainerEl.addEventListener.calls.argsFor(0)).toEqual(['scroll', jasmine.any(Function)]); + }); + it('twice, once with `scroll`, second time with `click` when `onClick` given', () => { + const eventListeners = { onClick: () => {} }; + spyOn(emojiesContainerEl, 'addEventListener'); + setEventsForTemplate(el, { animationDuration, eventListeners }); + expect(emojiesContainerEl.addEventListener).toHaveBeenCalledTimes(2); + expect(emojiesContainerEl.addEventListener.calls.argsFor(0)).toEqual(['scroll', jasmine.any(Function)]); + expect(emojiesContainerEl.addEventListener.calls.argsFor(1)).toEqual(['click', jasmine.any(Function)]); + }); + describe('scroll event ', () => { + const fireEvent = e => { event(e); }; + let event; + beforeEach(() => { + const eventListeners = {}; + spyOn(emojiesContainerEl, 'addEventListener').and.callFake((eventType, eventHanler) => { + if (eventType === 'scroll') { + event = eventHanler; + } + }); + setEventsForTemplate(el, { animationDuration, eventListeners }); + }); + it('should scroll to first category for 0 scrollTop', () => { + emojiesContainerEl.scrollTop = 0; + fireEvent(); + expect(slideToCategorySpy).toHaveBeenCalledWith(jasmine.any(Object), slideEl, categoryOrder[0]); + }); + it('should scroll to last category for scrollTop higher then any other offsetTop', () => { + emojiesContainerEl.scrollTop = 8000; + fireEvent(); + expect(slideToCategorySpy).toHaveBeenCalledWith(jasmine.any(Object), slideEl, categoryOrder[categoryOrder.length - 1]); + }); + it('should scroll to category for offsetTop equals to the scrollTop', () => { + emojiesContainerEl.scrollTop = 200; + fireEvent(); + expect(slideToCategorySpy).toHaveBeenCalledWith(jasmine.any(Object), slideEl, categoryOrder[1]); + }); + it('should scroll to the lower category that is between to categories based on scrollTop', () => { + emojiesContainerEl.scrollTop = 300; // between 200 and 400, should choose category with 200 + fireEvent(); + expect(slideToCategorySpy).toHaveBeenCalledWith(jasmine.any(Object), slideEl, categoryOrder[1]); + }); + }); + describe('click event ', () => { + const eventListeners = { onClick: () => {} }; + const fireEvent = e => { event(e); }; + let event; + beforeEach(() => { + spyOn(emojiesContainerEl, 'addEventListener').and.callFake((eventType, eventHanler) => { + if (eventType === 'click') { + event = eventHanler; + } + }); + setEventsForTemplate(el, { animationDuration, eventListeners }); + }); + it('should not call `onClick` if target is not an emoji (some other element in emoji container)', () => { + const span = document.createElement('span'); + spyOn(eventListeners, 'onClick'); + fireEvent({ + target: span + }); + expect(eventListeners.onClick).not.toHaveBeenCalled(); + }); + it('should call `onClick` if target is an emoji (class="ep-e") and return "data-index" as number and "data-unified"', () => { + const span = document.createElement('span'); + const index = 123; + const unified = 'yuval'; + span.setAttribute('class', 'ep-e'); + span.setAttribute('data-index', index); + span.setAttribute('data-unified', unified); + spyOn(eventListeners, 'onClick'); + fireEvent({ + target: span + }); + expect(eventListeners.onClick).toHaveBeenCalledTimes(1); + expect(eventListeners.onClick).toHaveBeenCalledWith({ index, unified }); + }); + }); + }); +}); diff --git a/test/spec/set-spec.js b/test/spec/set-spec.js index 6afd55c..19e124e 100644 --- a/test/spec/set-spec.js +++ b/test/spec/set-spec.js @@ -1,9 +1,11 @@ -import set from 'src/set'; - -import emojiData from 'src/emoji-data'; +import setInjector from 'inject!src/set'; +import emojiDataMock from 'test/mock/emoji-data'; import categoryOrder from 'src/category-order'; import { IMAGE_SET, SIZE } from 'src/constant'; import Map from 'src/map'; +const set = setInjector({ + './emoji-data': emojiDataMock +}); describe('set function', () => { it('calls `api.add` once with css object', () => { @@ -11,10 +13,10 @@ describe('set function', () => { spyOn(api, 'add').and.callFake(obj => { // flatten emoji-data object array to single array, filter out categories not in `categoryOrder` - const emojiesToAddToCss = Object.keys(emojiData).reduce((arr, catStr) => { + const emojiesToAddToCss = Object.keys(emojiDataMock).reduce((arr, catStr) => { const cat = Number(catStr); if (categoryOrder.indexOf(cat) !== -1) { - return arr.concat(emojiData[cat]); + return arr.concat(emojiDataMock[cat]); } else { // Filter out categories not in `categoryOrder` return arr; diff --git a/test/spec/template-spec.js b/test/spec/template-spec.js new file mode 100644 index 0000000..120e131 --- /dev/null +++ b/test/spec/template-spec.js @@ -0,0 +1,94 @@ +import templateInjector from 'inject!src/template.ahtml'; +import categoryOrder from 'src/category-order'; +import { CATEGORY } from 'src/constant'; +import emojiDataMock from 'test/mock/emoji-data'; +const template = templateInjector({ + './emoji-data': emojiDataMock +}); + +describe('template.ahtml returns a function that produces html', () => { + it('that calls `api.morph(\'html\').add` once with html object', () => { + let morphCount = 0; + let addCount = 0; + const api = { + morph: morphType => { + expect(morphType).toBe('html'); + morphCount++; + if (morphCount !== 1) { + fail(`'morph' function was called other than once, was called ${morphCount} times.`); + } + return { + add: htmlObject => { + addCount++; + if (addCount !== 1) { + fail(`'add' function was called other than once, was called ${addCount} times.`); + } + expect(htmlObject).toEqual(jasmine.any(Object)); + } + }; + } + }; + template(api); + }); + it('with a slider at the same container as the categories', () => { + const api = { + morph: () => ({ + add: htmlObject => { + const categoriesContainerChildren = htmlObject['div[class="ep"]']['div[class="ep-categories"]']; + const sliderObject = categoriesContainerChildren[0]; // First index is slider + const sliderElementNames = Object.keys(sliderObject); + expect(sliderElementNames.length).toBe(1); // Slider is 1 element only + expect(sliderElementNames[0]).toBe('span[class="ep-slide"]'); + } + }) + }; + template(api); + }); + it('with categories in the order of `category-order`', () => { + const api = { + morph: () => ({ + add: htmlObject => { + const categoriesContainerChildren = htmlObject['div[class="ep"]']['div[class="ep-categories"]']; + const categoriesSpansOnly = categoriesContainerChildren[1]; // First index is slider + const categoriesSpanNamesOnly = Object.keys(categoriesSpansOnly) + const categoriesValuesFromSpans = categoriesSpanNamesOnly.map(spanElement => { + expect(spanElement).toEqual(jasmine.stringMatching(/^span\[class="ep-c" data-category-id="(\d{1,})"\]$/)); + const categoryStringValue = spanElement.match(/^span\[class="ep-c" data-category-id="(\d{1,})"\]$/)[1]; + const categoryValue = Number(categoryStringValue); + return categoryValue; + }); + expect(categoriesValuesFromSpans).toEqual(categoryOrder); + } + }) + }; + template(api); + }); + it('emojies in the order of `emoji-data` each in category order by `category-order`', () => { + const api = { + morph: () => ({ + add: htmlObject => { + const emojisContainer = htmlObject['div[class="ep"]']['div[class="ep-emojies"]']; + const emojiCategoryDivNames = Object.keys(emojisContainer); + + const constructedEmojiDataFromHtml = emojiCategoryDivNames.reduce((obj, categoryDivElement) => { + expect(categoryDivElement).toEqual(jasmine.stringMatching(/^div\[class="ep-emojies-c" data-category-id="(\d{1,})"\]$/)); + const categoryStringValue = categoryDivElement.match(/^div\[class="ep-emojies-c" data-category-id="(\d{1,})"\]$/)[1]; + const categoryValue = Number(categoryStringValue); + return Object.assign(obj, { + [categoryValue]: Object.keys(emojisContainer[categoryDivElement]).map(emojiSpanName => { + expect(emojiSpanName).toEqual(jasmine.stringMatching(/^span\[class="ep-e" data-index="(\d{1,})" data-unified="(\w{1,})"\]$/)); + const emojiMathValues = emojiSpanName.match(/^span\[class="ep-e" data-index="(\d{1,})" data-unified="(\w{1,})"\]$/); + const index = Number(emojiMathValues[1]); + const unified = emojiMathValues[2]; + return { index, unified }; + }) + }); + }, {}); + + expect(constructedEmojiDataFromHtml).toEqual(emojiDataMock); + } + }) + }; + template(api); + }); +}); diff --git a/webpack.config.js b/webpack.config.js index a9a1d39..08bd3a8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -76,10 +76,6 @@ module.exports = { loaders: [ { test: name => name.match(/\.js$/) && !name.match(/(\.ahtml|\.acss).js$/), - include: [ - path.resolve(__dirname, 'src'), - path.resolve(__dirname, 'example') - ], loader: 'babel?presets[]=es2015' }, {