Skip to content

Commit

Permalink
fix: unit test fixes w/ ugly manual mock
Browse files Browse the repository at this point in the history
  • Loading branch information
David Atchley committed Mar 15, 2017
1 parent 0af727c commit 987a857
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 71 deletions.
5 changes: 4 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
{ "presets": ["es2015", "react"] }
{
"presets": ["es2015", "react"],
"plugins": ["transform-object-rest-spread"]
}
4 changes: 3 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"extends": "eslint-config-airbnb",
"parser": "babel-eslint",
"env": {
"browser": true,
"jest": true,
Expand All @@ -10,7 +11,8 @@
"react/jsx-uses-vars": 2,
"react/react-in-jsx-scope": 2,
"comma-dangle": 0,
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }]
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
"space-before-function-paren": 0
},
"plugins": [
"react"
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"build:umd": "webpack && NODE_ENV=production webpack",
"build:analyze": "webpack --json > ./lib/build-stats.json",
"lint": "eslint src examples",
"test": "jest",
"test:watch": "jest --watch",
"test": "jest --env=jsdom",
"test:watch": "jest --env=jsdom --watch",
"test:cov": "jest --coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"prepublish": "npm run lint && npm run test && npm run clean && npm run build && npm run build:umd",
"release": "standard-version"
Expand All @@ -33,8 +33,10 @@
"ava": "^0.16.0",
"babel-cli": "^6.4.5",
"babel-core": "^6.4.5",
"babel-eslint": "^6.1.2",
"babel-jest": "^10.0.2",
"babel-loader": "^6.2.1",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"co-exec": "^1.0",
Expand Down
94 changes: 94 additions & 0 deletions src/__mocks__/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// __mocks__/utils.js
import values from 'lodash/values';

const utils = require.requireActual('../utils');

const elementMap = {};

// Takes an object with the following properties
// { 'parent': { width, height, top, left }, ... }
const __setMockElements = (mapping) => {
/* eslint-disable no-param-reassign */
const elements = Object.keys(mapping).reduce((acc, key) => {
const styles = mapping[key].styles || {};
acc[key] = Object.assign({}, mapping[key], {
clientWidth: mapping[key].width || 0,
clientHeight: mapping[key].height || 0,
offsetWidth: mapping[key].width || 0,
offsetHeight: mapping[key].height || 0,
scrollWidth: mapping[key].width || 0,
scrollHeight: mapping[key].height || 0,
_styles: {},
style: {
lineHeight: '1',
fontSize: '16px',
...styles
},
getBoundingClientRect() {
return {
top: mapping[key].top || 0,
bottom: this.offsetHeight,
left: mapping[key].left || 0,
right: this.offsetWidth
};
}
});

// Treat fontSize special, inc/dec the width for testing purposes
acc[key]._styles.fontSize = styles.fontSize || '16px';
/* eslint-disable object-shorthand */
Object.defineProperty(acc[key].style, 'fontSize', {
get: function() { return elementMap[key]._styles.fontSize; },
set: function(val) {
const curSize = parseFloat(elementMap[key]._styles.fontSize);
const newSize = parseFloat(val);
const adj = (newSize - curSize) / 10;
if (curSize < newSize) {
elementMap[key].offsetWidth += adj;
elementMap[key].clientWidth += adj;
elementMap[key].scrollWidth += adj;
}
if (curSize > newSize) {
elementMap[key].offsetWidth += adj;
elementMap[key].clientWidth += adj;
elementMap[key].scrollWidth += adj;
}
elementMap[key]._styles.fontSize = val;
},
enumerable: true,
configurable: true
});
return acc;
}, {});

Object.assign(elementMap, elements);
};
utils.__setMockElements = __setMockElements;

utils.__getMockElement = (key) => elementMap[key];

utils.setRef = (name, context) => () => {
context[name] = elementMap[name];
};
utils.setRef._isMock = true;

utils.getStyle = (el, prop) => elementMap[el.className][utils.camelize(prop)];

const origGetOverflow = utils.getOverflow;
utils.getOverflow = (/* parent, child */) => {
const parent = elementMap.wrapper;
const child = elementMap.content;
return origGetOverflow(parent, child);
};

// const origHasOverflow = utils.hasOverflow;
utils.hasOverflow = (/* parent, child */) => {
const parent = elementMap.wrapper;
const child = elementMap.content;
if (child.style.position && child.style.position === 'absolute') {
return values(utils.getOverflow(parent, child)).some(v => v);
}
return (parent.clientWidth <= parent.scrollWidth || parent.clientHeight <= parent.scrollHeight);
};

module.exports = utils;
29 changes: 21 additions & 8 deletions src/__tests__/index.tests.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
jest.setMock('../utils', require('../__mocks__/utils'));

import React from 'react';
import { shallow } from 'enzyme';
import { mount, shallow } from 'enzyme';
import ScaleText from '../index';

// jest.mock('../utils');
const utils = require('utils');


describe('ScaleText', () => {
it('renders correctly', () => {
Expand All @@ -13,26 +18,34 @@ describe('ScaleText', () => {
// Element proeperties such as clientHeight/Width, scrollHeight/Width
// getBoundingClientRect(), etc. jsdom doesn't polyfill those, as it
// doesn't have a full rendering engine.
/* it('renders correctly, with minFontSize', () => {
it('renders correctly, with minFontSize', () => {
utils.__setMockElements({
wrapper: { width: 100, height: 100, top: 0, left: 0 },
content: { width: 105, height: 105, top: 0, left: 0 }
});
const pStyles = { width: '100px', height: '100px' };
const wrapper = mount(
<div className="parent" style={pStyles}>
<ScaleText minFontSize={12}><p>Some text</p></ScaleText>
</div>
);
console.log('offsetWidth: ', wrapper.find('.parent').offsetWidth);
const fontSize = parseFloat(wrapper.find('p').getDOMNode().style.fontSize, 10);
expect(fontSize).toBeGreaterThanOrEqual(12);
wrapper.update();
const fontSize = utils.__getMockElement('content').style.fontSize;
expect(parseFloat(fontSize, 10)).toBeGreaterThanOrEqual(12);
wrapper.unmount();
});

it('renders correctly, with maxFontSize', () => {
utils.__setMockElements({
wrapper: { width: 800, height: 800, top: 0, left: 0 },
content: { width: 100, height: 100, top: 0, left: 0 }
});
const pStyles = { width: '800px', height: '800px' };
const wrapper = mount(
<div style={pStyles}><ScaleText maxFontSize={20}><p>Some text</p></ScaleText></div>
);
const fontSize = parseFloat(wrapper.find('p').getDOMNode().style.fontSize, 10);
expect(fontSize).toBeLessThanOrEqual(20);
const fontSize = utils.__getMockElement('content').style.fontSize;
expect(parseFloat(fontSize, 10)).toBeLessThanOrEqual(20);
wrapper.unmount();
});*/
});
});
46 changes: 1 addition & 45 deletions src/__tests__/utils.tests.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,4 @@
import { camelize, getStyle, getOverflow, hasOverflow } from '../utils';

function createMockDiv(width, height, styles = {}) {
const div = document.createElement('div');
Object.assign(div.style, {
width: `${width}px`,
height: `${height}px`,
}, styles);

// we have to mock this for jsdom.
div.getBoundingClientRect = () => ({
width: div.style.width || width,
height: div.style.height || height,
top: 0,
left: 0,
right: div.style.width || width,
bottom: div.style.height || height,
});
div.prototype.clientWidth = div.clientWidth || width;
div.prototype.scrollWidth = div.scrollWidth || width;
div.prototype.clientHeight = div.clientHeight || height;
div.prototype.scrollHeight = div.scrollHeight || height;
return div;
}
import { camelize, getStyle } from '../utils';

describe('camelize()', () => {
it('correctly camelizes a dashed string', () => {
Expand All @@ -47,24 +24,3 @@ describe('getStyle()', () => {
});
});

describe.skip('getOverflow() + hasOverflow()', () => {
// TODO: Find a way to mock clientHeight/Width and scrollHeight/Width
// so we can unit test these methods
it('detects and tests for overflow', () => {
document.body.innerHTML = '<html><body></body></html>';

const parent = createMockDiv(100, 100, { overflow: 'hidden' });
document.body.appendChild(parent);
const child = createMockDiv(200, 200);
parent.appendChild(child);

expect(getOverflow(parent, child)).toMatchObject({ horizontal: true, vertical: true });
expect(hasOverflow(parent, child)).toEqual(true);
child.style.height = '100px';
expect(getOverflow(parent, child)).toMatchObject({ horizontal: true, vertical: false });
expect(hasOverflow(parent, child)).toEqual(true);
child.style.width = '100px';
expect(getOverflow(parent, child)).toMatchObject({ horizontal: false, vertical: false });
expect(hasOverflow(parent, child)).toEqual(false);
});
});
18 changes: 4 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import React, { Component, PropTypes } from 'react';
import { hasOverflow } from './utils';
// import debounce from 'lodash/debounce';
import { setRef, hasOverflow } from './utils';
import throttle from 'lodash/throttle';

class ScaleText extends Component {

constructor(props) {
super(props);
this.state = {
wrapper: null,
content: null,
fontSize: null
};
}

componentDidMount() {
this.handleResize = throttle(this.scale.bind(this), 50, { leading: true });
window.addEventListener('resize', this.handleResize);
Expand Down Expand Up @@ -61,8 +51,8 @@ class ScaleText extends Component {

render() {
const { children } = this.props;
const contentRef = (c) => { this.content = c; };
const wrapperRef = (c) => { this.wrapper = c; };
const contentRef = setRef('content', this); // (c) => { this.content = c; };
const wrapperRef = setRef('wrapper', this); // (c) => { this.wrapper = c; };
const wrapStyle = {
display: 'inline-block',
overflow: 'hidden',
Expand All @@ -71,7 +61,7 @@ class ScaleText extends Component {
};

return (
<div ref={wrapperRef} style={wrapStyle}>
<div className="scaletext-wrapper" ref={wrapperRef} style={wrapStyle}>
{ React.Children.map(children, (child) =>
React.cloneElement(child, { ref: contentRef })
)[0]
Expand Down
5 changes: 5 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import values from 'lodash/values';
export const camelize = (str) =>
str.replace(/\-(\w)/g, (s, letter) => letter.toUpperCase());

// Used to create ref setters so we can mock elements in tests
/* eslint-disable no-param-reassign */
export const setRef = (name, context) => (e) => { context[name] = e; };
/* eslint-enable no-param-reassign */

// Get the current style property value for the given element
export function getStyle(el, styleProp) {
if (el.currentStyle) {
Expand Down

0 comments on commit 987a857

Please sign in to comment.