From 9de748fc64a10e18f8f5cd4c00fda7318991b884 Mon Sep 17 00:00:00 2001 From: Sebastian Klingler Date: Fri, 8 Jun 2018 22:09:45 -0500 Subject: [PATCH] Add client.test.ts --- .circleci/config.yml | 2 +- package.json | 11 ++++---- src/client.ts | 8 +++--- test/client.test.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++ test/mocks.ts | 25 +++++++++++++++++ test/utils.test.ts | 21 ++++---------- tslint.json | 3 +- 7 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 test/client.test.ts create mode 100644 test/mocks.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index d095294..d503059 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,7 +30,7 @@ jobs: - run: command: | ./cc-test-reporter before-build - npm run test + npm run test -- --coverage ./cc-test-reporter after-build --exit-code $? # publish to npm diff --git a/package.json b/package.json index 8467a85..84b4ae6 100644 --- a/package.json +++ b/package.json @@ -38,14 +38,9 @@ "typescript": "^2.9.1" }, "jest": { - "collectCoverage": true, "collectCoverageFrom": [ "src/**" ], - "transform": { - "^.+\\.tsx?$": "ts-jest" - }, - "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", "moduleFileExtensions": [ "ts", "tsx", @@ -53,6 +48,10 @@ "jsx", "json", "node" - ] + ], + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$" } } diff --git a/src/client.ts b/src/client.ts index dca3614..6194481 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5,7 +5,7 @@ import { StylesTarget } from 'typestyle/lib/internal/typestyle'; import { alternateFirstInvocation, updateVNode } from './utils'; -const removeServersideStyles = (styleElementSelector: string) => { +export const removeElement = (styleElementSelector: string) => { const previousStyleTarget = document.querySelector(styleElementSelector); if (previousStyleTarget) { @@ -13,14 +13,14 @@ const removeServersideStyles = (styleElementSelector: string) => { } }; -const makeDomUpdater = (styleElementSelector: string | undefined = undefined) => (oldNode: VNode, newNode: VNode): void => { +export const makeDomUpdater = (styleElementSelector: string | undefined = undefined) => (oldNode: VNode, newNode: VNode): void => { if (newNode.elm) { const elm: Element = newNode.elm as Element; updateVNode(newNode, (name, value) => elm.setAttribute(name, value)); } if (typeof styleElementSelector !== 'undefined') { - removeServersideStyles(styleElementSelector); + removeElement(styleElementSelector); } }; @@ -32,7 +32,7 @@ export const makeModule = (styleElementSelector: string | undefined = undefined) return { create: updateDOM, - update: updateDOM, + update: updateDOM } as Module; }; diff --git a/test/client.test.ts b/test/client.test.ts new file mode 100644 index 0000000..1781df1 --- /dev/null +++ b/test/client.test.ts @@ -0,0 +1,67 @@ +import { h } from 'snabbdom'; +import { VNode } from 'snabbdom/vnode'; +import { style } from 'typestyle'; +import * as client from '../src/client'; +import * as utils from '../src/utils'; +import * as mocks from './mocks'; + +jest.mock('typestyle'); + +describe('removeElement', () => { + + it('removes an element given a selector', () => { + + const element = document.createElement('style'); + element.id = 'styles'; + document.head.appendChild(element); + + client.removeElement(mocks.styleElementSelector()); + expect(document.head.contains(element)).toBe(false); + }); + + it('doesn\'t error when not given a selector', () => { + client.removeElement(); + }); +}); + +describe('makeDomUpdater', () => { + it('calls updateVNode when passed a VNode with an element', () => { + utils.updateVNode = jest.fn(); + client.makeDomUpdater()(mocks.vNodeWithElm(), mocks.vNodeWithElm(); + expect(utils.updateVNode.mock.calls.length).toBe(1); + }); + + it('does not call updateVNode when create is passed a VNode without an element', () => { + utils.updateVNode = jest.fn(); + client.makeDomUpdater()(mocks.vNode(), mocks.vNode(); + expect(utils.updateVNode.mock.calls.length).toBe(0); + }); + + it('passes removeElement the style element selector when create is called', () => { + client.removeElement = jest.fn(); + client.makeDomUpdater(mocks.styleElementSelector())(mocks.vNode(), mocks.vNode(); + expect(client.removeElement.mock.calls.length).toBe(1); + expect(client.removeElement.mock.calls[0][0]).toBe(mocks.styleElementSelector()); + }); + +}); + +describe('makeModule', () => { + + const module; + + beforeEach(() => { + module = client.makeModule(mocks.styleElementSelector()); + }); + + it('returns a module that calls that performs the same function on create and update', () => { + expect(module.update).toEqual(module.create); + }); + + it('calls makeDomUpdater with the styleElementSelector', () => { + const spy = jest.fn(); + client.makeDomUpdater = () => spy; + module.create(mocks.vNodeWithElm(), mocks.vNodeWithElm()); + expect(spy.mock.calls.length).toBe(1); + }); +}); diff --git a/test/mocks.ts b/test/mocks.ts new file mode 100644 index 0000000..bf39239 --- /dev/null +++ b/test/mocks.ts @@ -0,0 +1,25 @@ +import { h } from 'snabbdom'; + +export const styleElementSelector = () => '#styles'; + +export const css = () => ({ + color: 'blue' +}); + +export const oldClassName = () => 'test'; + +export const vNode = () => h('div', { + css: css(), + props: { + className: oldClassName() + } + }); + +export const vNodeWithElm = () => { + const node = vNode(); + node.elm = { + setAttribute: jest.fn(); + }; + + return node; +}; diff --git a/test/utils.test.ts b/test/utils.test.ts index bea86ed..a07ab05 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -1,7 +1,7 @@ -import * as utils from '../src/utils'; -import { style } from 'typestyle'; -import { h } from 'snabbdom'; import { VNode } from 'snabbdom/vnode'; +import { style } from 'typestyle'; +import * as utils from '../src/utils'; +import * as mocks from './mocks'; jest.mock('typestyle'); @@ -45,30 +45,21 @@ describe('makeClassName', () => { describe('updateVNode', () => { - const css; - const oldClassName; const attributeAccessor; beforeEach(() => { - css = { - color: 'blue', - }; - - oldClassName = 'test'; - const vNode = h('div', { css, props: { className: oldClassName} }); attributeAccessor = jest.fn(); utils.makeClassName = jest.fn(() => 'className'); - - utils.updateVNode(vNode, attributeAccessor); + utils.updateVNode(mocks.vNode(), attributeAccessor); }); it('calls style with the vnode css', () => { - expect(style.mock.calls[0][0]).toEqual(css); + expect(style.mock.calls[0][0]).toEqual(mocks.css()); }); it('calls makeClassName with old classname and new classname', () => { const newClassName = style.mock.results[0].value; - expect(utils.makeClassName.mock.calls[0][0]).toBe(oldClassName); + expect(utils.makeClassName.mock.calls[0][0]).toBe(mocks.oldClassName()); expect(utils.makeClassName.mock.calls[0][1]).toBe(newClassName); }); diff --git a/tslint.json b/tslint.json index f750b92..0418bd9 100644 --- a/tslint.json +++ b/tslint.json @@ -1,7 +1,8 @@ { "extends": "tslint:recommended", "rules": { + "interface-name": [false], "quotemark": [true, "single"], - "interface-name": [false] + "trailing-comma": [true, {"multiline": "never", "singleline": "never"}] } } \ No newline at end of file