diff --git a/apis/test-utils/src/__tests__/index.inspect.js b/apis/test-utils/src/__tests__/index.inspect.js new file mode 100644 index 000000000..531096580 --- /dev/null +++ b/apis/test-utils/src/__tests__/index.inspect.js @@ -0,0 +1,98 @@ +/* eslint no-underscore-dangle:0 */ +import * as stardustUtils from '@nebula.js/stardust'; +import { create } from '../index'; + +jest.mock('@nebula.js/stardust'); + +describe('test-utils', () => { + let hookMock; + let hooked; + + let fnMock; + let initiateMock; + let runMock; + let runRestMock; + let teardownMock; + let runSnapsMock; + let observeActionsMock; + let getImperativeHandleMock; + let updateRectOnNextRunMock; + + beforeEach(() => { + fnMock = jest.fn(); + initiateMock = jest.fn(); + runMock = jest.fn(); + runRestMock = jest.fn(); + runMock.reset = runRestMock; + teardownMock = jest.fn(); + runSnapsMock = jest.fn(); + observeActionsMock = jest.fn(); + getImperativeHandleMock = jest.fn(); + updateRectOnNextRunMock = jest.fn(); + + hooked = { + __hooked: true, + fn: fnMock, + initiate: initiateMock, + run: runMock, + teardown: teardownMock, + runSnaps: runSnapsMock, + observeActions: observeActionsMock, + getImperativeHandle: getImperativeHandleMock, + updateRectOnNextRun: updateRectOnNextRunMock, + fromTest: true, + }; + + hookMock = jest.fn().mockReturnValue(hooked); + jest.spyOn(stardustUtils.__DO_NOT_USE__, 'hook').mockImplementation(hookMock); + }); + + afterEach(() => { + jest.restoreAllMocks(); + jest.resetAllMocks(); + }); + + test('should return api', () => { + const c = create(); + expect(c.update instanceof Function).toBe(true); + expect(c.unmount instanceof Function).toBe(true); + expect(c.takeSnapshot instanceof Function).toBe(true); + expect(c.actions instanceof Function).toBe(true); + }); + + test('should update', () => { + const c = create(); + c.update(); + expect(runMock).toHaveBeenCalledTimes(1); + + runMock.reset(); + expect(runRestMock).toHaveBeenCalledTimes(1); + + const context = { translator: {} }; + c.update(context); + expect(runMock).toHaveBeenCalledWith( + expect.objectContaining({ + context, + }) + ); + }); + + test('should update', () => { + const c = create(); + c.unmount(); + expect(teardownMock).toHaveBeenCalledTimes(1); + }); + + test('should take snapshot', () => { + const c = create(); + c.takeSnapshot(); + expect(runSnapsMock).toHaveBeenCalledTimes(1); + }); + + test.skip('should do actions', () => { + // hooked.observeActions.callsArgWith(1, ['action']); + const c = create(); + // hooked.observeActions.reset(); + expect(c.actions()).toEqual(['action']); + }); +}); diff --git a/apis/test-utils/src/__tests__/index.spec.js b/apis/test-utils/src/__tests__/index.spec.js deleted file mode 100644 index 87f45c348..000000000 --- a/apis/test-utils/src/__tests__/index.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -describe('test-utils', () => { - let sandbox; - let create; - let hook; - let hooked; - - before(() => { - sandbox = sinon.createSandbox(); - - hooked = { - __hooked: true, - fn: sandbox.stub(), - initiate: sandbox.stub(), - run: sandbox.stub(), - teardown: sandbox.stub(), - runSnaps: sandbox.stub(), - observeActions: sandbox.stub(), - getImperativeHandle: sandbox.stub(), - updateRectOnNextRun: sandbox.stub(), - }; - hook = sandbox.stub().returns(hooked); - [{ create }] = aw.mock([['@nebula.js/stardust', () => ({ __DO_NOT_USE__: { hook } })]], ['../index']); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it('should return api', () => { - const c = create(); - expect(c.update).to.be.a('function'); - expect(c.unmount).to.be.a('function'); - expect(c.takeSnapshot).to.be.a('function'); - expect(c.actions).to.be.a('function'); - }); - - it('should update', () => { - const c = create(); - c.update(); - expect(hooked.run.callCount).to.equal(1); - - hooked.run.reset(); - const translator = {}; - const context = { translator }; - c.update(context); - expect(hooked.run).to.have.been.calledWithExactly( - sinon.match({ - context, - }) - ); - }); - - it('should update', () => { - const c = create(); - c.unmount(); - expect(hooked.teardown.callCount).to.equal(1); - }); - - it('should take snapshot', () => { - const c = create(); - c.takeSnapshot(); - expect(hooked.runSnaps.callCount).to.equal(1); - }); - - it('should do actions', () => { - hooked.observeActions.callsArgWith(1, ['action']); - const c = create(); - hooked.observeActions.reset(); - expect(c.actions()).to.deep.equals(['action']); - }); -}); diff --git a/apis/theme/src/__tests__/palette-resolver.spec.js b/apis/theme/src/__tests__/palette-resolver.inspect.js similarity index 57% rename from apis/theme/src/__tests__/palette-resolver.spec.js rename to apis/theme/src/__tests__/palette-resolver.inspect.js index a9d017e68..ceb5639b3 100644 --- a/apis/theme/src/__tests__/palette-resolver.spec.js +++ b/apis/theme/src/__tests__/palette-resolver.inspect.js @@ -1,7 +1,7 @@ import paletteResolverFn from '../palette-resolver'; describe('palette-resolver', () => { - it('dataScales()', () => { + test('dataScales()', () => { expect( paletteResolverFn({ scales: [ @@ -14,7 +14,7 @@ describe('palette-resolver', () => { }, ], }).dataScales() - ).to.eql([ + ).toEqual([ { key: 'p', name: 'name', @@ -26,7 +26,7 @@ describe('palette-resolver', () => { ]); }); - it('dataPalettes()', () => { + test('dataPalettes()', () => { expect( paletteResolverFn({ palettes: { @@ -41,7 +41,7 @@ describe('palette-resolver', () => { ], }, }).dataPalettes() - ).to.eql([ + ).toEqual([ { key: 'p', name: 'name', @@ -52,7 +52,7 @@ describe('palette-resolver', () => { ]); }); - it('uiPalettes()', () => { + test('uiPalettes()', () => { expect( paletteResolverFn({ palettes: { @@ -65,7 +65,7 @@ describe('palette-resolver', () => { ], }, }).uiPalettes() - ).to.eql([ + ).toEqual([ { key: 'ui', name: 'name', @@ -76,7 +76,7 @@ describe('palette-resolver', () => { ]); }); - it('dataColors()', () => { + test('dataColors()', () => { expect( paletteResolverFn({ dataColors: { @@ -85,7 +85,7 @@ describe('palette-resolver', () => { othersColor: 'others', }, }).dataColors() - ).to.eql({ + ).toEqual({ primary: 'primary', nil: 'null', others: 'others', @@ -94,40 +94,43 @@ describe('palette-resolver', () => { describe('uiColor', () => { let p; - let uiPalettes; + let uiPalettesMock; + beforeEach(() => { + uiPalettesMock = jest.fn(); p = paletteResolverFn(); - uiPalettes = sinon.stub(p, 'uiPalettes'); + jest.spyOn(p, 'uiPalettes').mockImplementation(uiPalettesMock); }); afterEach(() => { - uiPalettes.restore(); + jest.resetAllMocks(); + jest.restoreAllMocks(); }); - it('should return color when index < 0 or undefined', () => { - expect(p.uiColor({ color: 'red' })).to.equal('red'); - expect(p.uiColor({ color: 'red', index: -1 })).to.equal('red'); - expect(uiPalettes.callCount).to.equal(0); + test('should return color when index < 0 or undefined', () => { + expect(p.uiColor({ color: 'red' })).toBe('red'); + expect(p.uiColor({ color: 'red', index: -1 })).toBe('red'); + expect(uiPalettesMock).toHaveBeenCalledTimes(0); }); - it('should return color when ui palette is falsy', () => { - uiPalettes.returns([]); - expect(p.uiColor({ color: 'red', index: 0 })).to.equal('red'); - expect(uiPalettes.callCount).to.equal(1); + test('should return color when ui palette is falsy', () => { + uiPalettesMock.mockReturnValue([]); + expect(p.uiColor({ color: 'red', index: 0 })).toEqual('red'); + expect(uiPalettesMock).toHaveBeenCalledTimes(1); }); - it('should return color when index is out of bounds', () => { - uiPalettes.returns([{ colors: ['a', 'b', 'c'] }]); - expect(p.uiColor({ color: 'red', index: 3 })).to.equal('red'); + test('should return color when index is out of bounds', () => { + uiPalettesMock.mockReturnValue([{ colors: ['a', 'b', 'c'] }]); + expect(p.uiColor({ color: 'red', index: 3 })).toEqual('red'); - expect(uiPalettes.callCount).to.equal(1); + expect(uiPalettesMock).toHaveBeenCalledTimes(1); // should keep cached palette p.uiColor({ color: 'red', index: 3 }); - expect(uiPalettes.callCount).to.equal(1); + expect(uiPalettesMock).toHaveBeenCalledTimes(1); }); - it('should return index from palette when index is within bounds', () => { - uiPalettes.returns([{ colors: ['a', 'b', 'c'] }]); - expect(p.uiColor({ color: 'red', index: 1 })).to.equal('b'); + test('should return index from palette when index is within bounds', () => { + uiPalettesMock.mockReturnValue([{ colors: ['a', 'b', 'c'] }]); + expect(p.uiColor({ color: 'red', index: 1 })).toBe('b'); }); }); }); diff --git a/apis/theme/src/__tests__/set-theme.inspect.js b/apis/theme/src/__tests__/set-theme.inspect.js new file mode 100644 index 000000000..75ea6060a --- /dev/null +++ b/apis/theme/src/__tests__/set-theme.inspect.js @@ -0,0 +1,96 @@ +import * as extendModule from 'extend'; +import create from '../set-theme'; +import base from '../themes/base.json'; +import light from '../themes/light.json'; +import dark from '../themes/dark.json'; + +jest.mock('extend'); +jest.mock('../themes/base.json', () => ({ + font: 'Arial', +})); +jest.mock('../themes/light.json', () => ({ + background: 'white', +})); +jest.mock('../themes/dark.json', () => ({ + background: 'black', +})); + +describe('set theme', () => { + let extendMock; + let resolveMock; + + beforeEach(() => { + extendMock = jest.fn(); + resolveMock = jest.fn(); + + jest.spyOn(extendModule, 'default').mockImplementation(extendMock); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + test('should extend from light theme by default', () => { + extendMock.mockReturnValue({ + palettes: {}, + }); + + create({}, resolveMock); + expect(extendMock.mock.calls[0]).toEqual([true, {}, base, light]); + }); + + test('should extend from dark theme when type is dark', () => { + extendMock.mockReturnValue({ + palettes: {}, + }); + + create({ type: 'dark' }, resolveMock); + expect(extendMock.mock.calls[0]).toEqual([true, {}, base, dark]); + }); + + test('should not extend scales and palette arrays', () => { + const root = { color: 'pink', palettes: {} }; + const merged = { palettes: { data: [], ui: [] }, scales: [] }; + extendMock.mockReturnValueOnce(root); + extendMock.mockReturnValueOnce(merged); + const t = { color: 'red' }; + const prevent = { scales: null, palettes: { data: null, ui: null } }; + create(t, resolveMock); + expect(extendMock.mock.calls[1]).toEqual([true, {}, root, prevent, t]); + expect(resolveMock).toHaveBeenCalledWith(merged); + }); + + test('should add defaults if custom scales and palettes are not provided', () => { + const root = { color: 'pink', palettes: { data: 'data', ui: 'ui' }, scales: 'scales' }; + const merged = { palettes: {} }; + extendMock.mockReturnValueOnce(root); + extendMock.mockReturnValueOnce(merged); + const custom = { color: 'red' }; + create(custom, resolveMock); + expect(resolveMock).toHaveBeenCalledWith({ + palettes: { data: 'data', ui: 'ui' }, + scales: 'scales', + }); + }); + + test('should add defaults if custom scales and palettes are empty', () => { + const root = { color: 'pink', palettes: { data: 'data', ui: 'ui' }, scales: 'scales' }; + const merged = { palettes: { data: [], ui: [] }, scales: [] }; + extendMock.mockReturnValueOnce(root); + extendMock.mockReturnValueOnce(merged); + const custom = { color: 'red' }; + create(custom, resolveMock); + expect(resolveMock).toHaveBeenCalledWith({ + palettes: { data: 'data', ui: 'ui' }, + scales: 'scales', + }); + }); + + test('should return resolved theme', () => { + extendMock.mockReturnValueOnce({ palettes: { data: [], ui: [] }, scales: [] }); + extendMock.mockReturnValueOnce({ palettes: { data: [], ui: [] }, scales: [] }); + resolveMock.mockReturnValue('resolved'); + expect(create({}, resolveMock)).toBe('resolved'); + }); +}); diff --git a/apis/theme/src/__tests__/set-theme.spec.js b/apis/theme/src/__tests__/set-theme.spec.js deleted file mode 100644 index 9f022b8d7..000000000 --- a/apis/theme/src/__tests__/set-theme.spec.js +++ /dev/null @@ -1,96 +0,0 @@ -describe('set theme', () => { - let sandbox; - let create; - let extend; - let base; - let light; - let dark; - let resolve; - before(() => { - sandbox = sinon.createSandbox(); - extend = sandbox.stub(); - resolve = sandbox.stub(); - base = { font: 'Arial' }; - light = { background: 'white' }; - dark = { background: 'black' }; - [{ default: create }] = aw.mock( - [ - [require.resolve('extend'), () => extend], - ['**/base.json', () => base], - ['**/light.json', () => light], - ['**/dark.json', () => dark], - ], - ['../set-theme.js'] - ); - }); - - afterEach(() => { - sandbox.reset(); - }); - - it('should extend from light theme by default', () => { - extend.returns({ - palettes: {}, - }); - create({}, resolve); - expect(extend.firstCall).to.have.been.calledWithExactly(true, {}, base, light); - }); - - it('should extend from dark theme when type is dark', () => { - extend.returns({ - palettes: {}, - }); - create( - { - type: 'dark', - }, - resolve - ); - expect(extend.firstCall).to.have.been.calledWithExactly(true, {}, base, dark); - }); - - it('should not extend scales and palette arrays', () => { - const root = { color: 'pink', palettes: {} }; - const merged = { palettes: { data: [], ui: [] }, scales: [] }; - extend.onFirstCall().returns(root); - extend.onSecondCall().returns(merged); - const t = { color: 'red' }; - const prevent = { scales: null, palettes: { data: null, ui: null } }; - create(t, resolve); - expect(extend.secondCall).to.have.been.calledWithExactly(true, {}, root, prevent, t); - expect(resolve).to.have.been.calledWithExactly(merged); - }); - - it('should add defaults if custom scales and palettes are not provided', () => { - const root = { color: 'pink', palettes: { data: 'data', ui: 'ui' }, scales: 'scales' }; - const merged = { palettes: {} }; - extend.onFirstCall().returns(root); - extend.onSecondCall().returns(merged); - const custom = { color: 'red' }; - create(custom, resolve); - expect(resolve).to.have.been.calledWithExactly({ - palettes: { data: 'data', ui: 'ui' }, - scales: 'scales', - }); - }); - - it('should add defaults if custom scales and palettes are empty', () => { - const root = { color: 'pink', palettes: { data: 'data', ui: 'ui' }, scales: 'scales' }; - const merged = { palettes: { data: [], ui: [] }, scales: [] }; - extend.onFirstCall().returns(root); - extend.onSecondCall().returns(merged); - const custom = { color: 'red' }; - create(custom, resolve); - expect(resolve).to.have.been.calledWithExactly({ - palettes: { data: 'data', ui: 'ui' }, - scales: 'scales', - }); - }); - - it('should return resolved theme', () => { - extend.onFirstCall().returns({ palettes: { data: [], ui: [] }, scales: [] }); - extend.onSecondCall().returns({ palettes: { data: [], ui: [] }, scales: [] }); - resolve.returns('resolved'); - expect(create({}, resolve)).to.equal('resolved'); - }); -}); diff --git a/apis/theme/src/__tests__/style-resolver.inspect.js b/apis/theme/src/__tests__/style-resolver.inspect.js new file mode 100644 index 000000000..a420ddcda --- /dev/null +++ b/apis/theme/src/__tests__/style-resolver.inspect.js @@ -0,0 +1,81 @@ +import * as extendModule from 'extend'; +import create from '../style-resolver'; + +describe('style-resolver', () => { + let extendMock; + + beforeEach(() => { + extendMock = jest.fn(); + jest.spyOn(extendModule, 'default').mockImplementation(extendMock); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + test('getStyle from root', () => { + const t = { + fontSize: '16px', + }; + const s = create('base.path', t); + + expect(s.getStyle('', 'fontSize')).toBe('16px'); + }); + + test('getStyle from object', () => { + const t = { + object: { + bar: { + legend: { + title: { + fontSize: '13px', + }, + }, + }, + }, + fontSize: '16px', + }; + const s = create('object.bar', t); + + expect(s.getStyle('', 'fontSize')).toBe('16px'); + expect(s.getStyle('legend', 'fontSize')).toBe('16px'); + expect(s.getStyle('legend.content', 'fontSize')).toBe('16px'); + expect(s.getStyle('title', 'fontSize')).toBe('16px'); + + expect(s.getStyle('legend.title', 'fontSize')).toBe('13px'); + expect(s.getStyle('legend.content', 'color')).toBe(undefined); + + expect(s.getStyle('.', 'legend.title.fontSize')).toBe('13px'); + expect(s.getStyle('legend.', 'title.fontSize')).toBe('13px'); + expect(s.getStyle('legend.', 'content.fontSize')).toBe(undefined); + expect(s.getStyle('legend.', 'content.color')).toBe(undefined); + + expect(s.getStyle('table', 'fontSize')).toBe('16px'); + expect(s.getStyle('table', 'content.fontSize')).toBe(undefined); + }); + + test('resolveRawTheme', () => { + const variables = { + '@text': 'pink', + '@size': 'mini', + }; + const raw = { + chart: { + bg: 'red', + color: '@text', + }, + responsive: '@size', + _variables: variables, + }; + extendMock.mockReturnValue(raw); + expect(create.resolveRawTheme(raw)).toEqual({ + chart: { + bg: 'red', + color: 'pink', + }, + responsive: 'mini', + _variables: variables, + }); + }); +}); diff --git a/apis/theme/src/__tests__/style-resolver.spec.js b/apis/theme/src/__tests__/style-resolver.spec.js deleted file mode 100644 index 41c507508..000000000 --- a/apis/theme/src/__tests__/style-resolver.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -describe('style-resolver', () => { - let sandbox; - let create; - let extend; - before(() => { - sandbox = sinon.createSandbox(); - extend = sandbox.stub(); - [{ default: create }] = aw.mock([[require.resolve('extend'), () => extend]], ['../style-resolver.js']); - }); - - afterEach(() => { - sandbox.reset(); - }); - - it('getStyle from root', () => { - const t = { - fontSize: '16px', - }; - const s = create('base.path', t); - - expect(s.getStyle('', 'fontSize')).to.equal('16px'); - }); - - it('getStyle from object', () => { - const t = { - object: { - bar: { - legend: { - title: { - fontSize: '13px', - }, - }, - }, - }, - fontSize: '16px', - }; - const s = create('object.bar', t); - - expect(s.getStyle('', 'fontSize')).to.equal('16px'); - expect(s.getStyle('legend', 'fontSize')).to.equal('16px'); - expect(s.getStyle('legend.content', 'fontSize')).to.equal('16px'); - expect(s.getStyle('title', 'fontSize')).to.equal('16px'); - - expect(s.getStyle('legend.title', 'fontSize')).to.equal('13px'); - expect(s.getStyle('legend.content', 'color')).to.equal(undefined); - - expect(s.getStyle('.', 'legend.title.fontSize')).to.equal('13px'); - expect(s.getStyle('legend.', 'title.fontSize')).to.equal('13px'); - expect(s.getStyle('legend.', 'content.fontSize')).to.equal(undefined); - expect(s.getStyle('legend.', 'content.color')).to.equal(undefined); - - expect(s.getStyle('table', 'fontSize')).to.equal('16px'); - expect(s.getStyle('table', 'content.fontSize')).to.equal(undefined); - }); - - it('resolveRawTheme', () => { - const variables = { - '@text': 'pink', - '@size': 'mini', - }; - const raw = { - chart: { - bg: 'red', - color: '@text', - }, - responsive: '@size', - _variables: variables, - }; - extend.returns(raw); - expect(create.resolveRawTheme(raw)).to.eql({ - chart: { - bg: 'red', - color: 'pink', - }, - responsive: 'mini', - _variables: variables, - }); - }); -}); diff --git a/apis/theme/src/__tests__/theme-scale-generator.spec.js b/apis/theme/src/__tests__/theme-scale-generator.inspect.js similarity index 78% rename from apis/theme/src/__tests__/theme-scale-generator.spec.js rename to apis/theme/src/__tests__/theme-scale-generator.inspect.js index 8879c9bd5..31c70aa8a 100644 --- a/apis/theme/src/__tests__/theme-scale-generator.spec.js +++ b/apis/theme/src/__tests__/theme-scale-generator.inspect.js @@ -4,29 +4,27 @@ describe('Theme scale generator', () => { const input = ['#ffffff', '#000000']; const base8 = ['#ffffff', '#d4d4d4', '#aaaaaa', '#7f7f7f', '#545454', '#2a2a2a', '#000000']; - beforeEach(() => {}); - - it('Should generate a pyramid', () => { + test('Should generate a pyramid', () => { const scales = [{ type: 'class', scale: input }]; scaleGenerator(scales); - expect(scales).to.have.length(1); + expect(scales.length).toBe(1); const { scale, type } = scales[0]; - expect(type).to.equals('class-pyramid'); - expect(scale.length).to.equal(8); - expect(scale[scale.length - 1].length).to.equal(7); + expect(type).toBe('class-pyramid'); + expect(scale.length).toBe(8); + expect(scale[scale.length - 1].length).toBe(7); }); - it('Should generate a correct base of colors', () => { + test('Should generate a correct base of colors', () => { const scales = [{ type: 'class', scale: input }]; scaleGenerator(scales); const { scale } = scales[0]; const colors = scale[scale.length - 1]; - expect(colors).to.deep.equal(base8); + expect(colors).toEqual(base8); }); - it('Should work correctly on a scale from the sense theme', () => { + test('Should work correctly on a scale from the sense theme', () => { const senseDivergentScale = [ '#ae1c3e', '#d24d3e', @@ -42,7 +40,7 @@ describe('Theme scale generator', () => { const scales = [{ type: 'class', scale: senseDivergentScale }]; scaleGenerator(scales); const { scale } = scales[0]; - expect(scale).to.deep.equal([ + expect(scale).toEqual([ null, ['#e6f5fe'], ['#ed875e', '#3a89c9'], diff --git a/apis/theme/src/__tests__/theme.inspect.js b/apis/theme/src/__tests__/theme.inspect.js new file mode 100644 index 000000000..b4506f387 --- /dev/null +++ b/apis/theme/src/__tests__/theme.inspect.js @@ -0,0 +1,143 @@ +import * as EventEmitter from 'node-event-emitter'; +import * as setThemeModule from '../set-theme'; +import * as paletterResolverFnModule from '../palette-resolver'; +import * as styleResolverFnModule from '../style-resolver'; +import * as contrasterFnModule from '../contraster/contraster'; +import * as luminanceFnModule from '../contraster/luminance'; +import create from '../index'; + +jest.mock('node-event-emitter'); + +describe('theme', () => { + let setThemeMock; + let paletterResolverFnMock; + let styleResolverFnMock; + let contrasterFnMock; + let luminanceFnMock; + let emitter; + let emitterEmitMock; + let emitterInitMock; + + beforeEach(() => { + setThemeMock = jest.fn(); + paletterResolverFnMock = jest.fn(); + styleResolverFnMock = jest.fn(); + contrasterFnMock = jest.fn(); + luminanceFnMock = jest.fn(); + emitterEmitMock = jest.fn(); + emitterInitMock = jest.fn(); + + jest.spyOn(setThemeModule, 'default').mockImplementation(setThemeMock); + jest.spyOn(paletterResolverFnModule, 'default').mockImplementation(paletterResolverFnMock); + jest.spyOn(styleResolverFnModule, 'default').mockImplementation(styleResolverFnMock); + styleResolverFnMock.resolveRawTheme = 'raw'; + jest.spyOn(contrasterFnModule, 'default').mockImplementation(contrasterFnMock); + jest.spyOn(luminanceFnModule, 'default').mockImplementation(luminanceFnMock); + emitter = { + prototype: { + emit: emitterEmitMock, + }, + init: emitterInitMock, + }; + + EventEmitter.EventEmitter.mockReturnValue(emitter); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + describe('initiate', () => { + beforeEach(() => { + setThemeMock.mockReturnValue('resolvedJSON'); + const getStyle = jest.fn().mockReturnValue('red'); + styleResolverFnMock.mockReturnValue({ + getStyle, + }); + }); + + test('should create paletteResolver', () => { + create(); + expect(paletterResolverFnMock).toHaveBeenCalledWith('resolvedJSON'); + }); + + test('should create contraster for dark text color', () => { + luminanceFnMock.mockReturnValue(0.19); + create(); + expect(contrasterFnMock).toHaveBeenCalledWith(['red', '#ffffff']); + }); + + test('should create contraster for light text color', () => { + create(); + expect(contrasterFnMock).toHaveBeenCalledWith(['red', '#333333']); + }); + + test("should emit 'changed' event", () => { + const t = create(); + expect(t.externalAPI.emit).toHaveBeenCalledTimes(1); + expect(t.externalAPI.emit).toHaveBeenCalledWith('changed'); + }); + }); + + describe('api', () => { + let t; + let resolved; + beforeEach(() => { + resolved = 'resolved'; + setThemeMock.mockReturnValue(resolved); + paletterResolverFnMock.mockReturnValue({ + dataScales: () => 'p scales', + dataPalettes: () => 'p data palettes', + uiPalettes: () => `p ui palettes`, + dataColors: () => 'p data colors', + uiColor: (a) => `p ui ${a}`, + }); + styleResolverFnMock.mockReturnValue({ + getStyle: () => '#eeeeee', + }); + contrasterFnMock.mockReturnValue({ getBestContrastColor: (c) => `contrast ${c}` }); + t = create().externalAPI; + }); + + test('getDataColorScales()', () => { + expect(t.getDataColorScales()).toBe('p scales'); + }); + + test('getDataColorPalettes()', () => { + expect(t.getDataColorPalettes()).toBe('p data palettes'); + }); + + test('getDataColorPickerPalettes()', () => { + expect(t.getDataColorPickerPalettes()).toBe('p ui palettes'); + }); + + test('getDataColorSpecials()', () => { + expect(t.getDataColorSpecials()).toBe('p data colors'); + }); + + test('getColorPickerColor()', () => { + expect(t.getColorPickerColor('color')).toBe('p ui color'); + }); + + test('getContrastingColorTo()', () => { + expect(t.getContrastingColorTo('color')).toBe('contrast color'); + }); + + test('getStyle()', () => { + const getStyle = jest.fn(); + getStyle.mockReturnValue('style'); + styleResolverFnMock.mockReturnValue({ + getStyle, + }); + expect(t.getStyle('base', 'path', 'attribute')).toBe('style'); + + // calling additional getStyle with same params should use cached style resolver + expect(styleResolverFnMock).toHaveBeenCalledTimes(2); + t.getStyle('base', 'path', 'attribute'); + t.getStyle('base', 'path', 'attribute'); + t.getStyle('base', 'path', 'attribute'); + expect(styleResolverFnMock).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/apis/theme/src/__tests__/theme.spec.js b/apis/theme/src/__tests__/theme.spec.js deleted file mode 100644 index a058ed27b..000000000 --- a/apis/theme/src/__tests__/theme.spec.js +++ /dev/null @@ -1,136 +0,0 @@ -describe('theme', () => { - let sandbox; - let create; - let setTheme; - let paletterResolverFn; - let styleResolverFn; - let contrasterFn; - let luminanceFn; - let emitter; - before(() => { - sandbox = sinon.createSandbox(); - setTheme = sandbox.stub(); - paletterResolverFn = sandbox.stub(); - styleResolverFn = sandbox.stub(); - styleResolverFn.resolveRawTheme = 'raw'; - contrasterFn = sandbox.stub(); - luminanceFn = sandbox.stub(); - emitter = { - prototype: { - emit: sandbox.stub(), - }, - init: sandbox.stub(), - }; - [{ default: create }] = aw.mock( - [ - [require.resolve('node-event-emitter'), () => emitter], - ['**/set-theme.js', () => setTheme], - ['**/palette-resolver.js', () => paletterResolverFn], - ['**/style-resolver.js', () => styleResolverFn], - ['**/contraster.js', () => contrasterFn], - ['**/luminance.js', () => luminanceFn], - ], - ['../index.js'] - ); - }); - - afterEach(() => { - sandbox.reset(); - }); - - describe('initiate', () => { - beforeEach(() => { - setTheme.withArgs({}, 'raw').returns('resolvedJSON'); - const getStyle = sandbox.stub(); - getStyle.returns('red'); - styleResolverFn.withArgs('', 'resolvedJSON').returns({ - getStyle, - }); - }); - - it('should create paletteResolver', () => { - create(); - expect(paletterResolverFn).to.have.been.calledWithExactly('resolvedJSON'); - }); - - it('should create contraster for dark text color', () => { - luminanceFn.returns(0.19); - create(); - expect(contrasterFn).to.have.been.calledWithExactly(['red', '#ffffff']); - }); - - it('should create contraster for light text color', () => { - create(); - expect(contrasterFn).to.have.been.calledWithExactly(['red', '#333333']); - }); - - it("should emit 'changed' event", () => { - const t = create(); - expect(t.externalAPI.emit).to.have.been.calledWithExactly('changed'); - }); - }); - - describe('api', () => { - let t; - let resolved; - beforeEach(() => { - resolved = 'resolved'; - setTheme.returns(resolved); - paletterResolverFn.returns({ - dataScales: () => 'p scales', - dataPalettes: () => 'p data palettes', - uiPalettes: () => `p ui palettes`, - dataColors: () => 'p data colors', - uiColor: (a) => `p ui ${a}`, - }); - - styleResolverFn.withArgs('', 'resolved').returns({ - getStyle: () => '#eeeeee', - }); - - contrasterFn.returns({ getBestContrastColor: (c) => `contrast ${c}` }); - - t = create().externalAPI; - }); - - it('getDataColorScales()', () => { - expect(t.getDataColorScales()).to.equal('p scales'); - }); - - it('getDataColorPalettes()', () => { - expect(t.getDataColorPalettes()).to.equal('p data palettes'); - }); - - it('getDataColorPickerPalettes()', () => { - expect(t.getDataColorPickerPalettes()).to.equal('p ui palettes'); - }); - - it('getDataColorSpecials()', () => { - expect(t.getDataColorSpecials()).to.equal('p data colors'); - }); - - it('getColorPickerColor()', () => { - expect(t.getColorPickerColor('color')).to.equal('p ui color'); - }); - - it('getContrastingColorTo()', () => { - expect(t.getContrastingColorTo('color')).to.equal('contrast color'); - }); - - it('getStyle()', () => { - const getStyle = sandbox.stub(); - getStyle.returns('style'); - styleResolverFn.withArgs('base', 'resolved').returns({ - getStyle, - }); - expect(t.getStyle('base', 'path', 'attribute')).to.equal('style'); - - // calling additional getStyle with same params should use cached style resolver - expect(styleResolverFn.callCount).to.equal(2); - t.getStyle('base', 'path', 'attribute'); - t.getStyle('base', 'path', 'attribute'); - t.getStyle('base', 'path', 'attribute'); - expect(styleResolverFn.callCount).to.equal(2); - }); - }); -}); diff --git a/apis/theme/src/contraster/__tests__/contrast.inspect.js b/apis/theme/src/contraster/__tests__/contrast.inspect.js new file mode 100644 index 000000000..6c13b8b3c --- /dev/null +++ b/apis/theme/src/contraster/__tests__/contrast.inspect.js @@ -0,0 +1,22 @@ +import contrast from '../contrast'; + +describe('contrast', () => { + test('should be 1 for same luminance', () => { + expect(contrast(0, 0)).toBe(1); + }); + + test('should be 21 when delta in luminance is 1', () => { + expect(contrast(0, 1)).toBe(21); + }); + + test('should return same value even when luminances are in wrong order', () => { + const v = 2.6; + const lums = [0.2, 0.6]; + expect(contrast(...lums)).toBe(v); + expect(contrast(...lums.reverse())).toBe(v); + }); + + test('should be 1.72727 when luminances are [0.9, 0.5]', () => { + expect(contrast(0.9, 0.5)).toBe(1.72727); + }); +}); diff --git a/apis/theme/src/contraster/__tests__/contrast.spec.js b/apis/theme/src/contraster/__tests__/contrast.spec.js deleted file mode 100644 index 8e3c7792b..000000000 --- a/apis/theme/src/contraster/__tests__/contrast.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -import contrast from '../contrast'; - -describe('contrast', () => { - it('should be 1 for same luminance', () => { - expect(contrast(0, 0)).to.equal(1); - }); - - it('should be 21 when delta in luminance is 1', () => { - expect(contrast(0, 1)).to.equal(21); - }); - - it('should return same value even when luminances are in wrong order', () => { - const v = 2.6; - const lums = [0.2, 0.6]; - expect(contrast(...lums)).to.equal(v); - expect(contrast(...lums.reverse())).to.equal(v); - }); - - it('should be 1.72727 when luminances are [0.9, 0.5]', () => { - expect(contrast(0.9, 0.5)).to.equal(1.72727); - }); -}); diff --git a/apis/theme/src/contraster/__tests__/contraster.inspect.js b/apis/theme/src/contraster/__tests__/contraster.inspect.js new file mode 100644 index 000000000..054e1b86b --- /dev/null +++ b/apis/theme/src/contraster/__tests__/contraster.inspect.js @@ -0,0 +1,49 @@ +import create from '../contraster'; +import * as luminanceModule from '../luminance'; +import * as contrastModule from '../contrast'; + +describe('contraster', () => { + let luminanceMock; + let contrastMock; + + beforeEach(() => { + luminanceMock = jest.fn(); + contrastMock = jest.fn(); + + jest.spyOn(luminanceModule, 'default').mockImplementation(luminanceMock); + jest.spyOn(contrastModule, 'default').mockImplementation(contrastMock); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + test('should return #ffffff by default when input is dark', () => { + luminanceMock.mockReturnValue(0.05); + contrastMock.mockReturnValueOnce(5); + contrastMock.mockReturnValueOnce(20); + expect(create().getBestContrastColor('#111111')).toBe('#ffffff'); + }); + + test('should return #333333 by default when input is light', () => { + luminanceMock.mockReturnValue(0.8); + contrastMock.mockReturnValueOnce(10); + contrastMock.mockReturnValueOnce(2); + expect(create().getBestContrastColor('#afa')).toBe('#333333'); + }); + + test('should return cached value', () => { + luminanceMock.mockReturnValue(0.8); + contrastMock.mockReturnValueOnce(10); + contrastMock.mockReturnValueOnce(2); + + const c = create(); + c.getBestContrastColor('#afa'); + expect(luminanceMock).toHaveBeenCalledTimes(3); + + c.getBestContrastColor('#afa'); + c.getBestContrastColor('#afa'); + expect(luminanceMock).toHaveBeenCalledTimes(3); + }); +}); diff --git a/apis/theme/src/contraster/__tests__/contraster.spec.js b/apis/theme/src/contraster/__tests__/contraster.spec.js deleted file mode 100644 index 789d64906..000000000 --- a/apis/theme/src/contraster/__tests__/contraster.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -describe('contraster', () => { - let sandbox; - let create; - let luminance; - let contrast; - before(() => { - sandbox = sinon.createSandbox(); - luminance = sandbox.stub(); - contrast = sandbox.stub(); - [{ default: create }] = aw.mock( - [ - ['**/luminance.js', () => luminance], - ['**/contrast.js', () => contrast], - ], - ['../contraster.js'] - ); - }); - - afterEach(() => { - sandbox.reset(); - }); - - beforeEach(() => { - luminance.withArgs('#ffffff').returns(1); - luminance.withArgs('#333333').returns(0.1); - }); - - it('should return #ffffff by default when input is dark', () => { - luminance.withArgs('#111111').returns(0.05); - contrast.withArgs(0.05, 0.1).returns(5); - contrast.withArgs(0.05, 1).returns(20); - expect(create().getBestContrastColor('#111111')).to.equal('#ffffff'); - }); - - it('should return #333333 by default when input is light', () => { - luminance.withArgs('#afa').returns(0.8); - contrast.withArgs(0.8, 0.1).returns(10); - contrast.withArgs(0.8, 1).returns(2); - expect(create().getBestContrastColor('#afa')).to.equal('#333333'); - }); - - it('should return cached value', () => { - luminance.withArgs('#afa').returns(0.8); - contrast.withArgs(0.8, 0.1).returns(10); - contrast.withArgs(0.8, 1).returns(2); - - const c = create(); - c.getBestContrastColor('#afa'); - expect(luminance.callCount).to.equal(3); - - c.getBestContrastColor('#afa'); - c.getBestContrastColor('#afa'); - expect(luminance.callCount).to.equal(3); - }); -}); diff --git a/apis/theme/src/contraster/__tests__/luminance.inspect.js b/apis/theme/src/contraster/__tests__/luminance.inspect.js new file mode 100644 index 000000000..3898e2f7d --- /dev/null +++ b/apis/theme/src/contraster/__tests__/luminance.inspect.js @@ -0,0 +1,31 @@ +import * as d3ColorUtil from 'd3-color'; +import luminance from '../luminance'; + +describe('luminance', () => { + let d3ColorMock; + + beforeAll(() => { + d3ColorMock = jest.fn(); + jest.spyOn(d3ColorUtil, 'color').mockImplementation(d3ColorMock); + }); + + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); + }); + + test('for #ffffff should be 1', () => { + d3ColorMock.mockReturnValue({ rgb: () => ({ r: 255, g: 255, b: 255 }) }); + expect(luminance('#ffffff')).toBe(1); + }); + + test('for #000000 should be 0', () => { + d3ColorMock.mockReturnValue({ rgb: () => ({ r: 0, g: 0, b: 0 }) }); + expect(luminance('#000000')).toBe(0); + }); + + test('for #ff6633 should be 0.31002', () => { + d3ColorMock.mockReturnValue({ rgb: () => ({ r: 255, g: 102, b: 51 }) }); + expect(luminance('#ff6633')).toBe(0.31002); + }); +}); diff --git a/apis/theme/src/contraster/__tests__/luminance.spec.js b/apis/theme/src/contraster/__tests__/luminance.spec.js deleted file mode 100644 index 2e0cb3c85..000000000 --- a/apis/theme/src/contraster/__tests__/luminance.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -describe('luminance', () => { - let sandbox; - let luminance; - let d3Color; - before(() => { - sandbox = sinon.createSandbox(); - d3Color = sandbox.stub(); - luminance = sandbox.stub(); - [{ default: luminance }] = aw.mock([['**/d3-color.js', () => ({ color: d3Color })]], ['../luminance.js']); - }); - - afterEach(() => { - sandbox.reset(); - }); - - it('for #ffffff should be 1', () => { - d3Color.withArgs('#ffffff').returns({ rgb: () => ({ r: 255, g: 255, b: 255 }) }); - expect(luminance('#ffffff')).to.equal(1); - }); - - it('for #000000 should be 0', () => { - d3Color.withArgs('#000000').returns({ rgb: () => ({ r: 0, g: 0, b: 0 }) }); - expect(luminance('#000000')).to.equal(0); - }); - - it('for #ff6633 should be 0.31002', () => { - d3Color.withArgs('#ff6633').returns({ rgb: () => ({ r: 255, g: 102, b: 51 }) }); - expect(luminance('#ff6633')).to.equal(0.31002); - }); -}); diff --git a/jest.config.js b/jest.config.js index e796242f1..30cee5a16 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,6 +8,8 @@ module.exports = { 'apis/locale/.+\\.inspect\\.[jt]sx?$', 'apis/snapshooter/.+\\.inspect\\.[jt]sx?$', 'apis/supernova/.+\\.inspect\\.[jt]sx?$', + 'apis/test-utils/.+\\.inspect\\.[jt]sx?$', + 'apis/theme/.+\\.inspect\\.[jt]sx?$', ], setupFilesAfterEnv: ['/jest.setup.js'], collectCoverageFrom: [ @@ -17,11 +19,19 @@ module.exports = { 'apis/locale/**/*.{js,jsx}', 'apis/snapshooter/**/*.{js,jsx}', 'apis/supernova/**/*.{js,jsx}', + 'apis/test-utils/**/*.{js,jsx}', + 'apis/theme/**/*.{js,jsx}', '!apis/enigma-mocker/examples/**', '!apis/enigma-mocker/index.js', '!commands/create/**/*.{js,jsx}', + '!commands/build/command.js', + '!commands/sense/command.js', + '!commands/serve/command.js', + '!commands/sense/src/ext.js', + '!commands/sense/src/empty-ext.js', '!apis/snapshooter/client.js', + '!apis/test-utils/index.js', '!**/lib/**', '!**/dist/**',