summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorErik Larkö <erik@mullvad.net>2017-07-20 09:47:00 +0200
committerErik Larkö <erik@mullvad.net>2017-07-20 09:47:00 +0200
commit8c13b784a277db52f8100e3cd5bdea17f0736d1f (patch)
treed64c0f23e9cb6cf30816b3e3c648168ec1715450
parent310cca89fcd29eb5df07d8a6b144e4fe8eeab56d (diff)
parent2ea377f3bd30a1e8d1d6392fcd224e82ff418601 (diff)
downloadmullvadvpn-8c13b784a277db52f8100e3cd5bdea17f0736d1f.tar.xz
mullvadvpn-8c13b784a277db52f8100e3cd5bdea17f0736d1f.zip
Merge branch 'switch-tests-rebased'
-rw-r--r--app/components/Switch.js55
-rw-r--r--package.json2
-rw-r--r--test/components/Switch.spec.js100
-rw-r--r--test/global.js17
-rw-r--r--yarn.lock109
5 files changed, 213 insertions, 70 deletions
diff --git a/app/components/Switch.js b/app/components/Switch.js
index 46baeb8b22..c278cfbd2c 100644
--- a/app/components/Switch.js
+++ b/app/components/Switch.js
@@ -6,43 +6,41 @@ import type { Point2d } from '../types';
const CLICK_TIMEOUT = 1000;
const MOVE_THRESHOLD = 10;
-export default class Switch extends Component {
- props: {
- isOn: boolean;
- onChange: ?((isOn: boolean) => void);
- }
+export type SwitchProps = {
+ isOn: boolean;
+ onChange: ?((isOn: boolean) => void);
+};
- defaultProps = {
- isOn: false
+export default class Switch extends Component {
+ props: SwitchProps;
+ static defaultProps: SwitchProps = {
+ isOn: false,
+ onChange: null
}
+ isCapturingMouseEvents = false;
ref: ?HTMLInputElement;
onRef = (e: HTMLInputElement) => this.ref = e;
state = {
- isTracking: false,
ignoreChange: false,
- initialPos: (null: ?Point2d),
+ initialPos: ({x: 0, y: 0}: Point2d),
startTime: (null: ?number)
}
handleMouseDown = (e: MouseEvent) => {
- const { pageX: x, pageY: y } = e;
+ const { clientX: x, clientY: y } = e;
+ this.startCapturingMouseEvents();
this.setState({
- isTracking: true,
initialPos: { x, y },
startTime: e.timeStamp
});
}
handleMouseMove = (e: MouseEvent) => {
- if(!this.state.isTracking) {
- return;
- }
-
const inputElement = this.ref;
const { x: x0 } = this.state.initialPos;
- const { pageX: x, pageY: y } = e;
+ const { clientX: x, clientY: y } = e;
const dx = Math.abs(x0 - x);
if(dx < MOVE_THRESHOLD) {
@@ -73,26 +71,17 @@ export default class Switch extends Component {
}
handleMouseUp = () => {
- if(this.state.isTracking) {
- this.setState({
- isTracking: false,
- initialPos: null
- });
- }
+ this.stopCapturingMouseEvents();
}
handleChange = (e: Event) => {
const startTime = this.state.startTime;
- const eventTarget = e.target;
+ const eventTarget: Object = e.target;
if(typeof(startTime) !== 'number') {
throw new Error('startTime must be a number.');
}
- if(!(eventTarget instanceof HTMLInputElement)) {
- throw new Error('e.target must be an instance of HTMLInputElement.');
- }
-
const dt = e.timeStamp - startTime;
if(this.state.ignoreChange) {
@@ -112,14 +101,22 @@ export default class Switch extends Component {
}
}
- componentDidMount() {
+ startCapturingMouseEvents() {
+ if(this.isCapturingMouseEvents) {
+ throw new Error('startCapturingMouseEvents() is called out of order.');
+ }
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('mouseup', this.handleMouseUp);
+ this.isCapturingMouseEvents = true;
}
- componentWillUnmount() {
+ stopCapturingMouseEvents() {
+ if(!this.isCapturingMouseEvents) {
+ throw new Error('stopCapturingMouseEvents() is called out of order.');
+ }
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
+ this.isCapturingMouseEvents = false;
}
render(): React.Element<*> {
diff --git a/package.json b/package.json
index 0d5b3a5044..772023a02d 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
"flow-bin": "^0.49.1",
"flow-typed": "^2.1.2",
"isomorphic-fetch": "^2.2.1",
- "jsdom": "^9.11.0",
+ "jsdom": "^11.1.0",
"mocha": "^3.2.0",
"npm-run-all": "^4.0.1",
"redux-mock-store": "^1.2.2",
diff --git a/test/components/Switch.spec.js b/test/components/Switch.spec.js
new file mode 100644
index 0000000000..8cdc053c24
--- /dev/null
+++ b/test/components/Switch.spec.js
@@ -0,0 +1,100 @@
+// @flow
+
+import { expect } from 'chai';
+import React from 'react';
+import ReactTestUtils, { Simulate } from 'react-dom/test-utils';
+import Switch from '../../app/components/Switch';
+
+describe('components/Switch', () => {
+
+ it('should switch on', (done) => {
+ const onChange = (isOn) => {
+ expect(isOn).to.be.true;
+ done();
+ };
+ const component = ReactTestUtils.renderIntoDocument(
+ <Switch isOn={ false } onChange={ onChange } />
+ );
+ const domNode = ReactTestUtils.findRenderedDOMComponentWithTag(component, 'input');
+
+ Simulate.mouseDown(domNode, { clientX: 100, clientY: 0 });
+ Simulate.mouseUp(domNode, { clientX: 100, clientY: 0 });
+ Simulate.change(domNode, { target: { checked: true } });
+ });
+
+ it('should switch off', (done) => {
+ const onChange = (isOn) => {
+ expect(isOn).to.be.false;
+ done();
+ };
+ const component = ReactTestUtils.renderIntoDocument(
+ <Switch isOn={ true } onChange={ onChange } />
+ );
+ const domNode = ReactTestUtils.findRenderedDOMComponentWithTag(component, 'input');
+
+ Simulate.mouseDown(domNode, { clientX: 100, clientY: 0 });
+ Simulate.mouseUp(domNode, { clientX: 100, clientY: 0 });
+ Simulate.change(domNode, { target: { checked: false } });
+ });
+
+ it('should handle left to right swipe', (done) => {
+ const onChange = (isOn) => {
+ expect(isOn).to.be.true;
+ done();
+ };
+ const component = ReactTestUtils.renderIntoDocument(
+ <Switch isOn={ false } onChange={ onChange } />
+ );
+ const domNode = ReactTestUtils.findRenderedDOMComponentWithTag(component, 'input');
+
+ Simulate.mouseDown(domNode, { clientX: 100, clientY: 0 });
+
+ // Switch listens to events on document
+ document.dispatchEvent(new MouseEvent('mousemove', { clientX: 150, clientY: 0 }));
+ document.dispatchEvent(new MouseEvent('mouseup', { clientX: 150, clientY: 0 }));
+ });
+
+ it('should handle right to left swipe', (done) => {
+ const onChange = (isOn) => {
+ expect(isOn).to.be.false;
+ done();
+ };
+ const component = ReactTestUtils.renderIntoDocument(
+ <Switch isOn={ true } onChange={ onChange } />
+ );
+ const domNode = ReactTestUtils.findRenderedDOMComponentWithTag(component, 'input');
+
+ Simulate.mouseDown(domNode, { clientX: 150, clientY: 0 });
+
+ // Switch listens to events on document
+ document.dispatchEvent(new MouseEvent('mousemove', { clientX: 100, clientY: 0 }));
+ document.dispatchEvent(new MouseEvent('mouseup', { clientX: 100, clientY: 0 }));
+ });
+
+ it('should timeout when user holds knob for too long without moving', (done) => {
+ const onChange = () => {
+ throw new Error('onChange should not be called on timeout.');
+ };
+
+ const component = ReactTestUtils.renderIntoDocument(
+ <Switch isOn={ false } onChange={ onChange } />
+ );
+ const domNode = ReactTestUtils.findRenderedDOMComponentWithTag(component, 'input');
+
+ Simulate.mouseDown(domNode, { clientX: 100, clientY: 0 });
+
+ setTimeout(() => {
+ // Switch listens to events on document
+ document.dispatchEvent(new MouseEvent('mouseup', { clientX: 100, clientY: 0 }));
+
+ try {
+ // should not trigger onChange()
+ Simulate.change(domNode);
+ done();
+ } catch(e) {
+ done(e);
+ }
+ }, 1000);
+ });
+
+}); \ No newline at end of file
diff --git a/test/global.js b/test/global.js
index 4cf366b166..7b4af72b63 100644
--- a/test/global.js
+++ b/test/global.js
@@ -1,11 +1,18 @@
import log from 'electron-log';
-import jsdom from 'jsdom';
+import { JSDOM } from 'jsdom';
before(() => {
- global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
- global.window = document.defaultView;
- global.navigator = window.navigator;
-
log.transports.console.level = false;
log.transports.file.level = false;
});
+
+beforeEach(() => {
+ const dom = new JSDOM('<!doctype html><html><body></body></html>');
+ const window = dom.window;
+ global.window = window;
+ global.document = window.document;
+ global.navigator = window.navigator;
+ global.HTMLInputElement = window.HTMLInputElement;
+ global.Event = window.Event;
+ global.MouseEvent = window.MouseEvent;
+});
diff --git a/yarn.lock b/yarn.lock
index ecdc8a87e1..63fd2d0129 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -56,6 +56,10 @@
version "4.5.2"
resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-4.5.2.tgz#8450fc442d2a59494251a5a52ae520017e2dcf0d"
+"@types/node@^6.0.46":
+ version "6.0.84"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.84.tgz#193ffe5a9f42864d425ffd9739d95b753c6a1eab"
+
"@types/node@^7.0.18":
version "7.0.29"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.29.tgz#ccfcec5b7135c7caf6c4ffb8c7f33102340d99df"
@@ -3385,6 +3389,32 @@ jschardet@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.4.2.tgz#2aa107f142af4121d145659d44f50830961e699a"
+jsdom@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.1.0.tgz#6c48d7a48ffc5c300283c312904d15da8360509b"
+ dependencies:
+ abab "^1.0.3"
+ acorn "^4.0.4"
+ acorn-globals "^3.1.0"
+ array-equal "^1.0.0"
+ content-type-parser "^1.0.1"
+ cssom ">= 0.3.2 < 0.4.0"
+ cssstyle ">= 0.2.37 < 0.3.0"
+ escodegen "^1.6.1"
+ html-encoding-sniffer "^1.0.1"
+ nwmatcher "^1.4.1"
+ parse5 "^3.0.2"
+ pn "^1.0.0"
+ request "^2.79.0"
+ request-promise-native "^1.0.3"
+ sax "^1.2.1"
+ symbol-tree "^3.2.1"
+ tough-cookie "^2.3.2"
+ webidl-conversions "^4.0.0"
+ whatwg-encoding "^1.0.1"
+ whatwg-url "^6.1.0"
+ xml-name-validator "^2.0.1"
+
jsdom@^7.0.2:
version "7.2.2"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e"
@@ -3405,30 +3435,6 @@ jsdom@^7.0.2:
whatwg-url-compat "~0.6.5"
xml-name-validator ">= 2.0.1 < 3.0.0"
-jsdom@^9.11.0:
- version "9.12.0"
- resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
- dependencies:
- abab "^1.0.3"
- acorn "^4.0.4"
- acorn-globals "^3.1.0"
- array-equal "^1.0.0"
- content-type-parser "^1.0.1"
- cssom ">= 0.3.2 < 0.4.0"
- cssstyle ">= 0.2.37 < 0.3.0"
- escodegen "^1.6.1"
- html-encoding-sniffer "^1.0.1"
- nwmatcher ">= 1.3.9 < 2.0.0"
- parse5 "^1.5.1"
- request "^2.79.0"
- sax "^1.2.1"
- symbol-tree "^3.2.1"
- tough-cookie "^2.3.2"
- webidl-conversions "^4.0.0"
- whatwg-encoding "^1.0.1"
- whatwg-url "^4.3.0"
- xml-name-validator "^2.0.1"
-
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@@ -3689,6 +3695,10 @@ lodash.some@^4.4.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
+lodash.sortby@^4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
+
lodash@^3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
@@ -4081,10 +4091,14 @@ number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-"nwmatcher@>= 1.3.7 < 2.0.0", "nwmatcher@>= 1.3.9 < 2.0.0":
+"nwmatcher@>= 1.3.7 < 2.0.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.0.tgz#b4389362170e7ef9798c3c7716d80ebc0106fccf"
+nwmatcher@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f"
+
oauth-sign@~0.8.1:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
@@ -4263,6 +4277,12 @@ parse5@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
+parse5@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.2.tgz#05eff57f0ef4577fb144a79f8b9a967a6cc44510"
+ dependencies:
+ "@types/node" "^6.0.46"
+
parsejson@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab"
@@ -4386,6 +4406,10 @@ pluralize@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762"
+pn@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/pn/-/pn-1.0.0.tgz#1cf5a30b0d806cd18f88fc41a6b5d4ad615b3ba9"
+
point-geometry@0.0.0, point-geometry@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/point-geometry/-/point-geometry-0.0.0.tgz#6fcbcad7a803b6418247dd6e49c2853c584daff7"
@@ -4810,6 +4834,20 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
+request-promise-core@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6"
+ dependencies:
+ lodash "^4.13.1"
+
+request-promise-native@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.4.tgz#86988ec8eee408e45579fce83bfd05b3adf9a155"
+ dependencies:
+ request-promise-core "1.1.1"
+ stealthy-require "^1.1.0"
+ tough-cookie ">=2.3.0"
+
request@2.78.0:
version "2.78.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.78.0.tgz#e1c8dec346e1c81923b24acdb337f11decabe9cc"
@@ -5231,6 +5269,10 @@ static-module@^1.1.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
+stealthy-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+
stream-combiner@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
@@ -5512,7 +5554,7 @@ to-utf8@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/to-utf8/-/to-utf8-0.0.1.tgz#d17aea72ff2fba39b9e43601be7b3ff72e089852"
-tough-cookie@^2.2.0, tough-cookie@^2.3.2, tough-cookie@~2.3.0:
+tough-cookie@>=2.3.0, tough-cookie@^2.2.0, tough-cookie@^2.3.2, tough-cookie@~2.3.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
dependencies:
@@ -5761,11 +5803,7 @@ webidl-conversions@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506"
-webidl-conversions@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
-
-webidl-conversions@^4.0.0:
+webidl-conversions@^4.0.0, webidl-conversions@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.1.tgz#8015a17ab83e7e1b311638486ace81da6ce206a0"
@@ -5801,12 +5839,13 @@ whatwg-url-compat@~0.6.5:
dependencies:
tr46 "~0.0.1"
-whatwg-url@^4.3.0:
- version "4.8.0"
- resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0"
+whatwg-url@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.1.0.tgz#5fc8279b93d75483b9ced8b26239854847a18578"
dependencies:
+ lodash.sortby "^4.7.0"
tr46 "~0.0.3"
- webidl-conversions "^3.0.0"
+ webidl-conversions "^4.0.1"
whet.extend@~0.9.9:
version "0.9.9"