@@ -238,7 +232,7 @@ export default class FilePatchView extends React.Component {
registerCommands() {
return (
-
+
@@ -696,7 +690,7 @@ export default class FilePatchView extends React.Component {
@autobind
focus() {
- this.element.focus();
+ this.refElement.get().focus();
}
@autobind
diff --git a/package-lock.json b/package-lock.json
index ec6077671c..9434263966 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "github",
- "version": "0.13.0",
+ "version": "0.13.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -10,6 +10,12 @@
"integrity": "sha1-nK+xca+CMpSQNTtIFvAzR6oVCjA=",
"dev": true
},
+ "@types/node": {
+ "version": "9.6.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.4.tgz",
+ "integrity": "sha512-Awg4BcUYiZtNKoveGOu654JVPt11V/KIC77iBz8NweyoOAZpz5rUJfPDwwD+ajfTs2HndbTCEB8IuLfX9m/mmw==",
+ "dev": true
+ },
"acorn": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz",
@@ -1658,27 +1664,17 @@
}
},
"cheerio": {
- "version": "0.22.0",
- "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
- "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=",
+ "version": "1.0.0-rc.2",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz",
+ "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=",
"dev": true,
"requires": {
"css-select": "1.2.0",
"dom-serializer": "0.1.0",
"entities": "1.1.1",
"htmlparser2": "3.9.2",
- "lodash.assignin": "4.2.0",
- "lodash.bind": "4.2.1",
- "lodash.defaults": "4.2.0",
- "lodash.filter": "4.6.0",
- "lodash.flatten": "4.4.0",
- "lodash.foreach": "4.5.0",
- "lodash.map": "4.6.0",
- "lodash.merge": "4.6.0",
- "lodash.pick": "4.4.0",
- "lodash.reduce": "4.6.0",
- "lodash.reject": "4.6.0",
- "lodash.some": "4.6.0"
+ "lodash": "4.17.5",
+ "parse5": "3.0.3"
}
},
"chownr": {
@@ -2074,6 +2070,12 @@
"resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz",
"integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k="
},
+ "discontinuous-range": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+ "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
+ "dev": true
+ },
"doctrine": {
"version": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
"integrity": "sha512-i5aQLQvEyAhw7XI4mbKxyrVdkqIc4OsCh9Z0XQof9X/ANftd0ZN1M4qz+TSU2VSokVwl23kXDvhnC4F4W+ip/g==",
@@ -2185,21 +2187,51 @@
"dev": true
},
"enzyme": {
- "version": "2.9.1",
- "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-2.9.1.tgz",
- "integrity": "sha1-B9XOaRJBJA+4F78sSxjW5TAkDfY=",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.3.0.tgz",
+ "integrity": "sha512-l8csyPyLmtxskTz6pX9W8eDOyH1ckEtDttXk/vlFWCjv00SkjTjtoUrogqp4yEvMyneU9dUJoOLnqFoiHb8IHA==",
"dev": true,
"requires": {
- "cheerio": "0.22.0",
- "function.prototype.name": "1.0.3",
+ "cheerio": "1.0.0-rc.2",
+ "function.prototype.name": "1.1.0",
+ "has": "1.0.1",
+ "is-boolean-object": "1.0.0",
+ "is-callable": "1.1.3",
+ "is-number-object": "1.0.3",
+ "is-string": "1.0.4",
"is-subset": "0.1.1",
"lodash": "4.17.5",
+ "object-inspect": "1.5.0",
"object-is": "1.0.1",
- "object.assign": "4.0.4",
+ "object.assign": "4.1.0",
"object.entries": "1.0.4",
"object.values": "1.0.4",
- "prop-types": "15.6.1",
- "uuid": "3.1.0"
+ "raf": "3.4.0",
+ "rst-selector-parser": "2.2.3"
+ }
+ },
+ "enzyme-adapter-react-15": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-react-15/-/enzyme-adapter-react-15-1.0.5.tgz",
+ "integrity": "sha512-GxQ+ZYbo6YFwwpaLc9LLyAwsx+F1au628/+hwTx3XV2OiuvHGyWgC/r1AAK1HlDRjujzfwwMNZTc/JxkjIuYVg==",
+ "dev": true,
+ "requires": {
+ "enzyme-adapter-utils": "1.3.0",
+ "lodash": "4.17.5",
+ "object.assign": "4.1.0",
+ "object.values": "1.0.4",
+ "prop-types": "15.6.1"
+ }
+ },
+ "enzyme-adapter-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.3.0.tgz",
+ "integrity": "sha512-vVXSt6uDv230DIv+ebCG66T1Pm36Kv+m74L1TrF4kaE7e1V7Q/LcxO0QRkajk5cA6R3uu9wJf5h13wOTezTbjA==",
+ "dev": true,
+ "requires": {
+ "lodash": "4.17.5",
+ "object.assign": "4.1.0",
+ "prop-types": "15.6.1"
}
},
"errno": {
@@ -2231,14 +2263,14 @@
}
},
"es-abstract": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz",
- "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==",
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz",
+ "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==",
"dev": true,
"requires": {
"es-to-primitive": "1.1.1",
"function-bind": "1.1.1",
- "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+ "has": "1.0.1",
"is-callable": "1.1.3",
"is-regex": "1.0.4"
},
@@ -2462,7 +2494,7 @@
"doctrine": "1.5.0",
"eslint-import-resolver-node": "0.3.1",
"eslint-module-utils": "2.1.1",
- "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+ "has": "1.0.1",
"lodash.cond": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
"minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"read-pkg-up": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz"
@@ -2565,7 +2597,7 @@
"dev": true,
"requires": {
"doctrine": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
- "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+ "has": "1.0.1",
"jsx-ast-utils": "1.4.1"
}
},
@@ -2962,9 +2994,9 @@
"dev": true
},
"function.prototype.name": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.0.3.tgz",
- "integrity": "sha512-5EblxZUdioXi2JiMZ9FUbwYj40eQ9MFHyzFLBSPdlRl3SO8l7SLWuAnQ/at/1Wi4hjJwME/C5WpF2ZfAc8nGNw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz",
+ "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==",
"dev": true,
"requires": {
"define-properties": "1.1.2",
@@ -3135,7 +3167,8 @@
}
},
"has": {
- "version": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
"integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
"dev": true,
"requires": {
@@ -3149,6 +3182,12 @@
"ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz"
}
},
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "dev": true
+ },
"has-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
@@ -3363,6 +3402,12 @@
"version": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
+ "is-boolean-object": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz",
+ "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=",
+ "dev": true
+ },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -3475,6 +3520,12 @@
}
}
},
+ "is-number-object": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz",
+ "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=",
+ "dev": true
+ },
"is-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
@@ -3540,7 +3591,7 @@
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
- "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz"
+ "has": "1.0.1"
}
},
"is-resolvable": {
@@ -3553,6 +3604,12 @@
"version": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
+ "is-string": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz",
+ "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=",
+ "dev": true
+ },
"is-subset": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
@@ -4043,18 +4100,6 @@
"integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
"dev": true
},
- "lodash.assignin": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
- "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=",
- "dev": true
- },
- "lodash.bind": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
- "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=",
- "dev": true
- },
"lodash.cond": {
"version": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
"integrity": "sha512-RWjUhzGbzG/KfDwk+onqdXvrsNv47G9UCMJgSKalPTSqJQyxZhQophG9jgqLf+15TIbZ5a/yG2YKOWsH3dVy9A==",
@@ -4070,28 +4115,10 @@
"lodash._isiterateecall": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz"
}
},
- "lodash.defaults": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
- "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=",
- "dev": true
- },
- "lodash.filter": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz",
- "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=",
- "dev": true
- },
- "lodash.flatten": {
+ "lodash.flattendeep": {
"version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
- "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=",
- "dev": true
- },
- "lodash.foreach": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
- "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=",
+ "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
+ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
"dev": true
},
"lodash.isarguments": {
@@ -4119,47 +4146,11 @@
"lodash.isarray": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz"
}
},
- "lodash.map": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
- "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=",
- "dev": true
- },
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
},
- "lodash.merge": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz",
- "integrity": "sha1-aYhLoUSsM/5plzemCG3v+t0PicU=",
- "dev": true
- },
- "lodash.pick": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
- "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=",
- "dev": true
- },
- "lodash.reduce": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
- "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=",
- "dev": true
- },
- "lodash.reject": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz",
- "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=",
- "dev": true
- },
- "lodash.some": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
- "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=",
- "dev": true
- },
"lolex": {
"version": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz",
"integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=",
@@ -4605,6 +4596,18 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
+ "nearley": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.13.0.tgz",
+ "integrity": "sha512-ioYYogSaZhFlCpRizQgY3UT3G1qFXmHGY/5ozoFE3dMfiCRAeJfh+IPE3/eh9gCZvqLhPCWb4bLt7Bqzo+1mLQ==",
+ "dev": true,
+ "requires": {
+ "nomnom": "1.6.2",
+ "railroad-diagrams": "1.0.0",
+ "randexp": "0.4.6",
+ "semver": "5.4.1"
+ }
+ },
"next-tick": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz",
@@ -4625,6 +4628,16 @@
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
"integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs="
},
+ "nomnom": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz",
+ "integrity": "sha1-hKZqJgF0QI/Ft3oY+IjszET7aXE=",
+ "dev": true,
+ "requires": {
+ "colors": "0.5.1",
+ "underscore": "1.4.4"
+ }
+ },
"normalize-package-data": {
"version": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=",
@@ -4725,6 +4738,12 @@
}
}
},
+ "object-inspect": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.5.0.tgz",
+ "integrity": "sha512-UmOFbHbwvv+XHj7BerrhVq+knjceBdkvU5AriwLMvhv2qi+e7DJzxfBeFpILEjVzCp+xA+W/pIf06RGPWlZNfw==",
+ "dev": true
+ },
"object-is": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz",
@@ -4746,13 +4765,14 @@
}
},
"object.assign": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz",
- "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"dev": true,
"requires": {
"define-properties": "1.1.2",
"function-bind": "1.1.1",
+ "has-symbols": "1.0.0",
"object-keys": "1.0.11"
},
"dependencies": {
@@ -4771,17 +4791,9 @@
"dev": true,
"requires": {
"define-properties": "1.1.2",
- "es-abstract": "1.9.0",
- "function-bind": "1.1.1",
- "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz"
- },
- "dependencies": {
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
- }
+ "es-abstract": "1.11.0",
+ "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
+ "has": "1.0.1"
}
},
"object.pick": {
@@ -4799,17 +4811,9 @@
"dev": true,
"requires": {
"define-properties": "1.1.2",
- "es-abstract": "1.9.0",
- "function-bind": "1.1.1",
- "has": "https://registry.npmjs.org/has/-/has-1.0.1.tgz"
- },
- "dependencies": {
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
- "dev": true
- }
+ "es-abstract": "1.11.0",
+ "function-bind": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
+ "has": "1.0.1"
}
},
"once": {
@@ -4881,7 +4885,7 @@
"p-limit": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
- "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
+ "integrity": "sha1-DpK2vty1nwIsE9DxlJ3ILRWQnxw=",
"requires": {
"p-try": "1.0.0"
}
@@ -4906,6 +4910,15 @@
"error-ex": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz"
}
},
+ "parse5": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
+ "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "9.6.4"
+ }
+ },
"pascalcase": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -5125,6 +5138,31 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
"integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg="
},
+ "raf": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.0.tgz",
+ "integrity": "sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw==",
+ "dev": true,
+ "requires": {
+ "performance-now": "2.1.0"
+ }
+ },
+ "railroad-diagrams": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+ "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
+ "dev": true
+ },
+ "randexp": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
+ "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+ "dev": true,
+ "requires": {
+ "discontinuous-range": "1.0.0",
+ "ret": "0.1.15"
+ }
+ },
"react": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
@@ -5573,6 +5611,16 @@
"glob": "7.1.2"
}
},
+ "rst-selector-parser": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
+ "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=",
+ "dev": true,
+ "requires": {
+ "lodash.flattendeep": "4.4.0",
+ "nearley": "2.13.0"
+ }
+ },
"run-async": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
@@ -6317,6 +6365,12 @@
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz",
"integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o="
},
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
+ "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=",
+ "dev": true
+ },
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
diff --git a/package.json b/package.json
index 69361825a3..779c20d772 100644
--- a/package.json
+++ b/package.json
@@ -79,7 +79,8 @@
"chai-as-promised": "^5.3.0",
"dedent-js": "^1.0.1",
"electron-devtools-installer": "^2.2.3",
- "enzyme": "^2.9.1",
+ "enzyme": "^3.3.0",
+ "enzyme-adapter-react-15": "^1.0.5",
"eslint": "^3.19.0",
"eslint-config-fbjs": "2.0.0-alpha.1",
"eslint-config-standard": "^10.2.0",
diff --git a/test/controllers/commit-controller.test.js b/test/controllers/commit-controller.test.js
index 228d696135..32a0e2ba8d 100644
--- a/test/controllers/commit-controller.test.js
+++ b/test/controllers/commit-controller.test.js
@@ -193,6 +193,7 @@ describe('CommitController', function() {
wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')('message in box');
await assert.async.equal(workspace.getActiveTextEditor().getPath(), wrapper.instance().getCommitMessagePath());
+ wrapper.update();
assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));
const editor = workspace.getActiveTextEditor();
@@ -208,7 +209,7 @@ describe('CommitController', function() {
workspace.getActiveTextEditor().destroy();
assert.isTrue(wrapper.find('CommitView').prop('deactivateCommitBox'));
- await assert.async.strictEqual(wrapper.find('CommitView').prop('message'), 'message in editor');
+ await assert.async.strictEqual(wrapper.update().find('CommitView').prop('message'), 'message in editor');
});
it('activates editor if already opened but in background', async function() {
@@ -253,7 +254,7 @@ describe('CommitController', function() {
wrapper.find('CommitView').prop('toggleExpandedCommitMessageEditor')();
await assert.async.lengthOf(wrapper.instance().getCommitMessageEditors(), 0);
assert.isTrue(atomEnvironment.applicationDelegate.confirm.called);
- await assert.async.strictEqual(wrapper.find('CommitView').prop('message'), 'make some new changes');
+ await assert.async.strictEqual(wrapper.update().find('CommitView').prop('message'), 'make some new changes');
});
});
diff --git a/test/controllers/editor-conflict-controller.test.js b/test/controllers/editor-conflict-controller.test.js
index 0e34962b3a..61ef4b376c 100644
--- a/test/controllers/editor-conflict-controller.test.js
+++ b/test/controllers/editor-conflict-controller.test.js
@@ -284,6 +284,7 @@ describe('EditorConflictController', function() {
editor.setCursorBufferPosition([16, 6]); // On "Your middle changes"
commandRegistry.dispatch(editorView, 'github:dismiss-conflict');
+ wrapper.update();
assert.equal(resolutionProgress.getRemaining(editor.getPath()), 2);
assert.include(editor.getText(), 'Text in between 0 and 1.\n\n' +
@@ -301,6 +302,8 @@ describe('EditorConflictController', function() {
fs.writeFileSync(fixtureFile, onlyTwoMarkers);
await assert.async.equal(wrapper.state('conflicts').size, 2);
+ wrapper.update();
+
assert.lengthOf(wrapper.find(ConflictController), 2);
assert.equal(resolutionProgress.getRemaining(fixtureFile), 2);
});
diff --git a/test/controllers/file-patch-controller.test.js b/test/controllers/file-patch-controller.test.js
index 94d6915ca0..7143f1fb81 100644
--- a/test/controllers/file-patch-controller.test.js
+++ b/test/controllers/file-patch-controller.test.js
@@ -42,7 +42,6 @@ function createComponent(repository, filePath) {
openFiles = sinon.spy();
getSelectedStagingViewItems = sinon.spy();
-
getRepositoryForWorkdir = () => repository;
resolutionProgress = new ResolutionProgress();
@@ -72,6 +71,16 @@ function createComponent(repository, filePath) {
);
}
+async function refreshRepository(wrapper) {
+ const workDir = wrapper.prop('workingDirectoryPath');
+ const repository = wrapper.prop('getRepositoryForWorkdir')(workDir);
+
+ const promise = wrapper.prop('switchboard').getFinishRepositoryRefreshPromise();
+ repository.refresh();
+ await promise;
+ wrapper.update();
+}
+
describe('FilePatchController', function() {
afterEach(function() {
atomEnv.destroy();
@@ -137,9 +146,9 @@ describe('FilePatchController', function() {
const wrapper = mount(React.cloneElement(component, {largeDiffLineThreshold: 5}));
-
- await assert.async.isTrue(wrapper.find('.large-file-patch').exists());
+ await assert.async.isTrue(wrapper.update().find('.large-file-patch').exists());
wrapper.find('.large-file-patch').find('button').simulate('click');
+
assert.isTrue(wrapper.find('HunkView').exists());
});
@@ -161,17 +170,17 @@ describe('FilePatchController', function() {
filePath: filePatch1.getPath(), largeDiffLineThreshold: 5,
}));
- await assert.async.isTrue(wrapper.find('.large-file-patch').exists());
+ await assert.async.isTrue(wrapper.update().find('.large-file-patch').exists());
wrapper.find('.large-file-patch').find('button').simulate('click');
assert.isTrue(wrapper.find('HunkView').exists());
getFilePatchForPath.returns(filePatch2);
wrapper.setProps({filePath: filePatch2.getPath()});
- await assert.async.isTrue(wrapper.find('.large-file-patch').exists());
+ await assert.async.isTrue(wrapper.update().find('.large-file-patch').exists());
getFilePatchForPath.returns(filePatch1);
wrapper.setProps({filePath: filePatch1.getPath()});
- assert.isTrue(wrapper.find('HunkView').exists());
+ assert.isTrue(wrapper.update().find('HunkView').exists());
});
});
@@ -188,8 +197,7 @@ describe('FilePatchController', function() {
assert.equal(wrapper.state('stagingStatus'), 'unstaged');
fs.writeFileSync(path.join(workdirPath, 'file.txt'), 'change\nand again!', 'utf8');
- repository.refresh();
- await wrapper.instance().onRepoRefresh(repository);
+ await refreshRepository(wrapper);
assert.notEqual(originalFilePatch, wrapper.state('filePatch'));
assert.equal(wrapper.state('stagingStatus'), 'unstaged');
@@ -210,7 +218,8 @@ describe('FilePatchController', function() {
getFilePatchForPath.returns(filePatch);
wrapper.instance().onRepoRefresh(repository);
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
assert.isTrue(wrapper.find('HunkView').text().includes('@@ -0,1 +0,1 @@'));
});
@@ -224,7 +233,7 @@ describe('FilePatchController', function() {
let view0;
await until(() => {
- view0 = wrapper.find('FilePatchView').shallow();
+ view0 = wrapper.update().find('FilePatchView').shallow();
return view0.find({hunk: hunk1}).exists();
});
assert.isTrue(view0.find({hunk: hunk2}).exists());
@@ -236,7 +245,7 @@ describe('FilePatchController', function() {
wrapper.instance().onRepoRefresh(repository);
let view1;
await until(() => {
- view1 = wrapper.find('FilePatchView').shallow();
+ view1 = wrapper.update().find('FilePatchView').shallow();
return view1.find({hunk: hunk3}).exists();
});
assert.isTrue(view1.find({hunk: hunk1}).exists());
@@ -249,7 +258,7 @@ describe('FilePatchController', function() {
const wrapper = mount(component);
- await assert.async.isTrue(wrapper.find('FilePatchView').exists());
+ await assert.async.isTrue(wrapper.update().find('Commands').exists());
commandRegistry.dispatch(wrapper.find('FilePatchView').getDOMNode(), 'core:move-right');
assert.isTrue(didSurfaceFile.calledWith(filePath, 'unstaged'));
});
@@ -274,11 +283,12 @@ describe('FilePatchController', function() {
const wrapper = mount(React.cloneElement(component, {openFiles: openFilesStub}));
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
wrapper.find('LineView').simulate('mousedown', {button: 0, detail: 1});
window.dispatchEvent(new MouseEvent('mouseup'));
commandRegistry.dispatch(wrapper.find('FilePatchView').getDOMNode(), 'github:open-file');
+ wrapper.update();
await assert.async.isTrue(editorSpy.setCursorBufferPosition.called);
@@ -328,7 +338,7 @@ describe('FilePatchController', function() {
assert.equal((await indexModeAndOid(repository, deletedSymlinkAddedFilePath)).mode, '100644');
// Unstage a couple added lines, but not all
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise0 = switchboard.getFinishStageOperationPromise();
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('LineView').at(1).find('.github-HunkView-line').simulate('mousedown', {button: 0, detail: 1});
@@ -337,7 +347,8 @@ describe('FilePatchController', function() {
hunkView0.find('button.github-HunkView-stageButton').simulate('click');
await opPromise0;
- repository.refresh();
+ await refreshRepository(wrapper);
+
// index shows symlink deletions still staged, only a couple of lines have been unstaged
assert.equal((await indexModeAndOid(repository, deletedSymlinkAddedFilePath)).mode, '100644');
assert.autocrlfEqual(await repository.readFileFromIndex(deletedSymlinkAddedFilePath), 'qux\nbaz\nzoo\n');
@@ -359,7 +370,7 @@ describe('FilePatchController', function() {
assert.autocrlfEqual(await repository.readFileFromIndex(deletedFileAddedSymlinkPath), 'foo\nbar\nbaz\n\n');
// stage a couple of lines, but not all
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise0 = switchboard.getFinishStageOperationPromise();
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('LineView').at(1).find('.github-HunkView-line').simulate('mousedown', {button: 0, detail: 1});
@@ -368,7 +379,8 @@ describe('FilePatchController', function() {
hunkView0.find('button.github-HunkView-stageButton').simulate('click');
await opPromise0;
- repository.refresh();
+ await refreshRepository(wrapper);
+
// index shows symlink change has not been staged, a couple of lines have been deleted
assert.equal((await indexModeAndOid(repository, deletedFileAddedSymlinkPath)).mode, '100644');
assert.autocrlfEqual(await repository.readFileFromIndex(deletedFileAddedSymlinkPath), 'foo\n\n');
@@ -392,7 +404,7 @@ describe('FilePatchController', function() {
assert.equal((await indexModeAndOid(repository, deletedSymlinkAddedFilePath)).mode, '120000');
// Stage a couple added lines, but not all
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise0 = switchboard.getFinishStageOperationPromise();
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('LineView').at(1).find('.github-HunkView-line').simulate('mousedown', {button: 0, detail: 1});
@@ -401,7 +413,8 @@ describe('FilePatchController', function() {
hunkView0.find('button.github-HunkView-stageButton').simulate('click');
await opPromise0;
- repository.refresh();
+ await refreshRepository(wrapper);
+
// index no longer shows file is symlink (symlink has been deleted), now a regular file with contents
assert.equal((await indexModeAndOid(repository, deletedSymlinkAddedFilePath)).mode, '100644');
assert.autocrlfEqual(await repository.readFileFromIndex(deletedSymlinkAddedFilePath), 'foo\nbar\n');
@@ -423,7 +436,7 @@ describe('FilePatchController', function() {
assert.equal((await indexModeAndOid(repository, deletedFileAddedSymlinkPath)).mode, '120000');
// unstage a couple of lines, but not all
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise0 = switchboard.getFinishStageOperationPromise();
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('LineView').at(1).find('.github-HunkView-line').simulate('mousedown', {button: 0, detail: 1});
@@ -432,7 +445,8 @@ describe('FilePatchController', function() {
hunkView0.find('button.github-HunkView-stageButton').simulate('click');
await opPromise0;
- repository.refresh();
+ await refreshRepository(wrapper);
+
// index no longer shows file is symlink (symlink creation has been unstaged), shows contents of file that existed prior to symlink
assert.equal((await indexModeAndOid(repository, deletedFileAddedSymlinkPath)).mode, '100644');
assert.autocrlfEqual(await repository.readFileFromIndex(deletedFileAddedSymlinkPath), 'bar\nbaz\n');
@@ -453,14 +467,15 @@ describe('FilePatchController', function() {
assert.equal((await indexModeAndOid(repository, deletedFileAddedSymlinkPath)).mode, '100644');
// stage all deleted lines
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise0 = switchboard.getFinishStageOperationPromise();
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('.github-HunkView-title').simulate('click');
hunkView0.find('button.github-HunkView-stageButton').simulate('click');
await opPromise0;
- repository.refresh();
+ await refreshRepository(wrapper);
+
// File is not on index, file deletion has been staged
assert.isNull(await indexModeAndOid(repository, deletedFileAddedSymlinkPath));
const {stagedFiles, unstagedFiles} = await repository.getStatusesForChangedFiles();
@@ -485,14 +500,15 @@ describe('FilePatchController', function() {
assert.equal((await indexModeAndOid(repository, deletedSymlinkAddedFilePath)).mode, '100644');
// unstage all added lines
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise0 = switchboard.getFinishStageOperationPromise();
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('.github-HunkView-title').simulate('click');
hunkView0.find('button.github-HunkView-stageButton').simulate('click');
await opPromise0;
- repository.refresh();
+ await refreshRepository(wrapper);
+
// File is not on index, file creation has been unstaged
assert.isNull(await indexModeAndOid(repository, deletedSymlinkAddedFilePath));
const {stagedFiles, unstagedFiles} = await repository.getStatusesForChangedFiles();
@@ -524,11 +540,10 @@ describe('FilePatchController', function() {
const wrapper = mount(component);
- // selectNext()
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
commandRegistry.dispatch(wrapper.find('FilePatchView').getDOMNode(), 'core:move-down');
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const hunkView0 = wrapper.find('HunkView').at(0);
assert.isFalse(hunkView0.prop('isSelected'));
const opPromise0 = switchboard.getFinishStageOperationPromise();
@@ -558,7 +573,6 @@ describe('FilePatchController', function() {
it('stages and unstages individual lines when the stage button is clicked on a hunk with selected lines', async function() {
const absFilePath = path.join(workdirPath, filePath);
-
const originalLines = fs.readFileSync(absFilePath, 'utf8').split('\n');
// write some unstaged changes
@@ -574,7 +588,7 @@ describe('FilePatchController', function() {
// stage a subset of lines from first hunk
const wrapper = mount(component);
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise0 = switchboard.getFinishStageOperationPromise();
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('LineView').at(1).find('.github-HunkView-line').simulate('mousedown', {button: 0, detail: 1});
@@ -583,7 +597,8 @@ describe('FilePatchController', function() {
hunkView0.find('button.github-HunkView-stageButton').simulate('click');
await opPromise0;
- repository.refresh();
+ await refreshRepository(wrapper);
+
const expectedLines0 = originalLines.slice();
expectedLines0.splice(1, 1,
'this is a modified line',
@@ -592,16 +607,12 @@ describe('FilePatchController', function() {
assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), expectedLines0.join('\n'));
// stage remaining lines in hunk
- const updatePromise1 = switchboard.getChangePatchPromise();
- const unstagedFilePatch1 = await repository.getFilePatchForPath('sample.js');
- wrapper.setState({filePatch: unstagedFilePatch1});
- await updatePromise1;
-
const opPromise1 = switchboard.getFinishStageOperationPromise();
wrapper.find('HunkView').at(0).find('button.github-HunkView-stageButton').simulate('click');
await opPromise1;
- repository.refresh();
+ await refreshRepository(wrapper);
+
const expectedLines1 = originalLines.slice();
expectedLines1.splice(1, 1,
'this is a modified line',
@@ -611,13 +622,8 @@ describe('FilePatchController', function() {
assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), expectedLines1.join('\n'));
// unstage a subset of lines from the first hunk
- const updatePromise2 = switchboard.getChangePatchPromise();
- const stagedFilePatch2 = await repository.getFilePatchForPath('sample.js', {staged: true});
- wrapper.setState({
- filePatch: stagedFilePatch2,
- stagingStatus: 'staged',
- });
- await updatePromise2;
+ wrapper.setState({stagingStatus: 'staged'});
+ await refreshRepository(wrapper);
const hunkView2 = wrapper.find('HunkView').at(0);
hunkView2.find('LineView').at(1).find('.github-HunkView-line')
@@ -631,7 +637,8 @@ describe('FilePatchController', function() {
hunkView2.find('button.github-HunkView-stageButton').simulate('click');
await opPromise2;
- repository.refresh();
+ await refreshRepository(wrapper);
+
const expectedLines2 = originalLines.slice();
expectedLines2.splice(2, 0,
'this is a new line',
@@ -640,13 +647,6 @@ describe('FilePatchController', function() {
assert.autocrlfEqual(await repository.readFileFromIndex('sample.js'), expectedLines2.join('\n'));
// unstage the rest of the hunk
- const updatePromise3 = switchboard.getChangePatchPromise();
- const stagedFilePatch3 = await repository.getFilePatchForPath('sample.js', {staged: true});
- wrapper.setState({
- filePatch: stagedFilePatch3,
- });
- await updatePromise3;
-
commandRegistry.dispatch(wrapper.find('FilePatchView').getDOMNode(), 'github:toggle-patch-selection-mode');
const opPromise3 = switchboard.getFinishStageOperationPromise();
@@ -669,7 +669,7 @@ describe('FilePatchController', function() {
initialStagingStatus: 'staged',
}));
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const opPromise = switchboard.getFinishStageOperationPromise();
wrapper.find('HunkView').at(0).find('button.github-HunkView-stageButton').simulate('click');
@@ -690,7 +690,7 @@ describe('FilePatchController', function() {
initialStagingStatus: 'staged',
}));
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const viewNode = wrapper.find('FilePatchView').getDOMNode();
commandRegistry.dispatch(viewNode, 'github:toggle-patch-selection-mode');
@@ -723,7 +723,7 @@ describe('FilePatchController', function() {
const wrapper = mount(component);
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
const hunkView0 = wrapper.find('HunkView').at(0);
hunkView0.find('LineView').at(1).find('.github-HunkView-line')
.simulate('mousedown', {button: 0, detail: 1});
@@ -739,7 +739,7 @@ describe('FilePatchController', function() {
const changePatchPromise = switchboard.getChangePatchPromise();
// assert that only line 1 has been staged
- repository.refresh(); // clear the cached file patches
+ await refreshRepository(wrapper); // clear the cached file patches
let expectedLines = originalLines.slice();
expectedLines.splice(1, 0,
'this is a modified line',
@@ -747,6 +747,7 @@ describe('FilePatchController', function() {
let actualLines = await repository.readFileFromIndex(filePath);
assert.autocrlfEqual(actualLines, expectedLines.join('\n'));
await changePatchPromise;
+ wrapper.update();
const hunkView1 = wrapper.find('HunkView').at(0);
hunkView1.find('LineView').at(2).find('.github-HunkView-line')
@@ -782,7 +783,7 @@ describe('FilePatchController', function() {
const wrapper = mount(component);
- await assert.async.isTrue(wrapper.find('HunkView').exists());
+ await assert.async.isTrue(wrapper.update().find('HunkView').exists());
// ensure staging the same hunk twice does not cause issues
// second stage action is a no-op since the first staging operation is in flight
@@ -792,7 +793,7 @@ describe('FilePatchController', function() {
await hunk1StagingPromise;
const patchPromise0 = switchboard.getChangePatchPromise();
- repository.refresh(); // clear the cached file patches
+ await refreshRepository(wrapper); // clear the cached file patches
const modifiedFilePatch = await repository.getFilePatchForPath(filePath);
wrapper.setState({filePatch: modifiedFilePatch});
await patchPromise0;
diff --git a/test/controllers/git-tab-controller.test.js b/test/controllers/git-tab-controller.test.js
index 51bf2a5b94..12c5ac81de 100644
--- a/test/controllers/git-tab-controller.test.js
+++ b/test/controllers/git-tab-controller.test.js
@@ -72,11 +72,11 @@ describe('GitTabController', function() {
app = React.cloneElement(app, {repository});
const wrapper = mount(app);
- assert.isTrue(wrapper.hasClass('is-loading'));
+ assert.isTrue(wrapper.find('.github-Panel').hasClass('is-loading'));
assert.lengthOf(wrapper.find('EtchWrapper'), 1);
assert.lengthOf(wrapper.find('CommitController'), 1);
- await assert.async.isFalse(wrapper.hasClass('is-loading'));
+ await assert.async.isFalse(wrapper.update().find('.github-Panel').hasClass('is-loading'));
assert.lengthOf(wrapper.find('EtchWrapper'), 1);
assert.lengthOf(wrapper.find('CommitController'), 1);
});
@@ -87,8 +87,8 @@ describe('GitTabController', function() {
app = React.cloneElement(app, {repository});
const wrapper = mount(app);
- assert.isTrue(wrapper.hasClass('is-empty'));
- assert.lengthOf(wrapper.find('.no-repository'), 1);
+ assert.isTrue(wrapper.find('.is-empty').exists());
+ assert.isTrue(wrapper.find('.no-repository').exists());
});
it('keeps the state of the GitTabView in sync with the assigned repository', async function() {
@@ -111,17 +111,17 @@ describe('GitTabController', function() {
// Fetches data when a new repository is assigned
// Does not update repository instance variable until that data is fetched
wrapper.setProps({repository: repository1});
- await assert.async.deepEqual(wrapper.find('GitTabView').prop('unstagedChanges'), await repository1.getUnstagedChanges());
+ await assert.async.deepEqual(wrapper.update().find('GitTabView').prop('unstagedChanges'), await repository1.getUnstagedChanges());
wrapper.setProps({repository: repository2});
- await assert.async.deepEqual(wrapper.find('GitTabView').prop('unstagedChanges'), await repository2.getUnstagedChanges());
+ await assert.async.deepEqual(wrapper.update().find('GitTabView').prop('unstagedChanges'), await repository2.getUnstagedChanges());
// Fetches data and updates child view when the repository is mutated
fs.writeFileSync(path.join(workdirPath2, 'a.txt'), 'a change\n');
fs.unlinkSync(path.join(workdirPath2, 'b.txt'));
repository2.refresh();
- await assert.async.deepEqual(wrapper.find('GitTabView').prop('unstagedChanges'), await repository2.getUnstagedChanges());
+ await assert.async.deepEqual(wrapper.update().find('GitTabView').prop('unstagedChanges'), await repository2.getUnstagedChanges());
});
it('fetches conflict marker counts for conflicting files', async function() {
@@ -153,18 +153,17 @@ describe('GitTabController', function() {
const confirm = sinon.stub();
app = React.cloneElement(app, {repository, confirm});
const wrapper = mount(app);
- const view = wrapper.find('GitTabView');
- await assert.async.isTrue(view.prop('isMerging'));
- assert.notEqual(view.prop('mergeConflicts').length, 0);
- assert.isOk(view.prop('mergeMessage'));
+ await assert.async.isTrue(wrapper.update().find('GitTabView').prop('isMerging'));
+ assert.notEqual(wrapper.find('GitTabView').prop('mergeConflicts').length, 0);
+ assert.isOk(wrapper.find('GitTabView').prop('mergeMessage'));
confirm.returns(0);
await wrapper.instance().getWrappedComponentInstance().abortMerge();
- await assert.async.lengthOf(view.prop('mergeConflicts'), 0);
- assert.isFalse(view.prop('isMerging'));
- assert.isNull(view.prop('mergeMessage'));
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('mergeConflicts'), 0);
+ assert.isFalse(wrapper.find('GitTabView').prop('isMerging'));
+ assert.isNull(wrapper.find('GitTabView').prop('mergeMessage'));
});
});
@@ -243,7 +242,7 @@ describe('GitTabController', function() {
app = React.cloneElement(app, {repository});
const wrapper = mount(app);
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('unstagedChanges'), 3);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('unstagedChanges'), 3);
const controller = wrapper.instance().getWrappedComponentInstance();
const gitTab = controller.refView;
@@ -268,7 +267,7 @@ describe('GitTabController', function() {
const wrapper = mount(app);
const controller = wrapper.instance().getWrappedComponentInstance();
- assert.isTrue(wrapper.hasClass('is-empty'));
+ assert.isTrue(wrapper.find('.is-empty').exists());
assert.lengthOf(wrapper.find('.no-repository'), 1);
controller.rememberLastFocus({target: null});
@@ -336,7 +335,7 @@ describe('GitTabController', function() {
app = React.cloneElement(app, {repository});
wrapper = mount(app);
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('unstagedChanges'), 3);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('unstagedChanges'), 3);
extractReferences();
});
@@ -344,7 +343,7 @@ describe('GitTabController', function() {
it('blurs on tool-panel:unfocus', function() {
sinon.spy(workspace.getActivePane(), 'activate');
- commandRegistry.dispatch(wrapper.find('.github-Panel').getNode(), 'tool-panel:unfocus');
+ commandRegistry.dispatch(wrapper.find('.github-Panel').getDOMNode(), 'tool-panel:unfocus');
assert.isTrue(workspace.getActivePane().activate.called);
});
@@ -444,7 +443,7 @@ describe('GitTabController', function() {
app = React.cloneElement(app, {repository, ensureGitTab});
const wrapper = mount(app);
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('unstagedChanges'), 2);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('unstagedChanges'), 2);
const gitTab = wrapper.instance().getWrappedComponentInstance().refView;
const stagingView = gitTab.refStagingView.getWrappedComponent();
@@ -468,7 +467,7 @@ describe('GitTabController', function() {
await assert.async.lengthOf(stagingView.props.unstagedChanges, 1);
assert.lengthOf(stagingView.props.stagedChanges, 1);
- commitView.find('atom-text-editor').getNode().getModel().setText('Make it so');
+ commitView.find('atom-text-editor').instance().getModel().setText('Make it so');
commitView.find('.github-CommitView-commit').simulate('click');
await assert.async.equal((await repository.getLastCommit()).getMessageSubject(), 'Make it so');
@@ -484,7 +483,7 @@ describe('GitTabController', function() {
app = React.cloneElement(app, {repository, confirm});
const wrapper = mount(app);
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('mergeConflicts'), 5);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('mergeConflicts'), 5);
const stagingView = wrapper.instance().getWrappedComponentInstance().refView.refStagingView.getWrappedComponent();
assert.equal(stagingView.props.mergeConflicts.length, 5);
@@ -607,7 +606,7 @@ describe('GitTabController', function() {
await repository.commit(commitMessage);
getLastCommit = () => {
- return wrapper.find('RecentCommitView').getNodes()[0].props.commit;
+ return wrapper.update().find('RecentCommitView').at(0).instance().props.commit;
};
await assert.async.strictEqual(getLastCommit().getMessageSubject(), commitMessage);
@@ -620,16 +619,16 @@ describe('GitTabController', function() {
// stage some changes
fs.writeFileSync(path.join(workdirPath, 'new-file.txt'), 'oh\nem\ngee\n');
await repository.stageFiles(['new-file.txt']);
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('stagedChanges'), 1);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('stagedChanges'), 1);
// ensure that the commit editor is empty
- assert.strictEqual(wrapper.find('CommitView').getNode().editor.getText(), '');
+ assert.strictEqual(wrapper.find('CommitView').instance().editor.getText(), '');
commandRegistry.dispatch(workspaceElement, 'github:amend-last-commit');
await assert.async.deepEqual(repository.commit.args[0][1], {amend: true, coAuthors: []});
// amending should commit all unstaged changes
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('stagedChanges'), 0);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('stagedChanges'), 0);
// commit message from previous commit should be used
const lastCommit = getLastCommit();
@@ -642,10 +641,10 @@ describe('GitTabController', function() {
// new commit message
const newMessage = 'such new very message';
const commitView = wrapper.find('CommitView');
- commitView.getNode().editor.setText(newMessage);
+ commitView.instance().editor.setText(newMessage);
// no staged changes
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('stagedChanges'), 0);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('stagedChanges'), 0);
commandRegistry.dispatch(workspaceElement, 'github:amend-last-commit');
await assert.async.deepEqual(repository.commit.args[0][1], {amend: true, coAuthors: []});
@@ -663,7 +662,7 @@ describe('GitTabController', function() {
// add co author
const author = {email: 'foo@bar.com', name: 'foo bar'};
- const commitView = wrapper.find('CommitView').getNode();
+ const commitView = wrapper.find('CommitView').instance();
commitView.setState({showCoAuthorInput: true});
commitView.onSelectedCoAuthorsChanged([author]);
@@ -682,7 +681,7 @@ describe('GitTabController', function() {
// add co author
const author = {email: 'foo@bar.com', name: 'foo bar'};
- const commitView = wrapper.find('CommitView').getNode();
+ const commitView = wrapper.find('CommitView').instance();
commitView.setState({showCoAuthorInput: true});
commitView.onSelectedCoAuthorsChanged([author]);
const newMessage = 'Star Wars: A New Message';
@@ -714,7 +713,7 @@ describe('GitTabController', function() {
assert.strictEqual(getLastCommit().getMessageSubject(), message);
// buh bye co author
- const commitView = wrapper.find('CommitView').getNode();
+ const commitView = wrapper.find('CommitView').instance();
assert.strictEqual(commitView.editor.getText(), '');
commitView.onSelectedCoAuthorsChanged([]);
@@ -766,14 +765,14 @@ describe('GitTabController', function() {
assert.deepEqual(wrapper.find('CommitView').prop('selectedCoAuthors'), []);
- await assert.async.lengthOf(wrapper.find('.github-RecentCommit-undoButton'), 1);
+ await assert.async.lengthOf(wrapper.update().find('.github-RecentCommit-undoButton'), 1);
wrapper.find('.github-RecentCommit-undoButton').simulate('click');
let commitMessages = wrapper.find('.github-RecentCommit-message').map(node => node.text());
// ensure that the co author trailer is stripped from commit message
assert.deepEqual(commitMessages, [commitSubject, 'Initial commit']);
- await assert.async.lengthOf(wrapper.find('GitTabView').prop('stagedChanges'), 1);
+ await assert.async.lengthOf(wrapper.update().find('GitTabView').prop('stagedChanges'), 1);
assert.deepEqual(wrapper.find('GitTabView').prop('stagedChanges'), [{
filePath: 'new-file.txt',
status: 'added',
diff --git a/test/controllers/repository-conflict-controller.test.js b/test/controllers/repository-conflict-controller.test.js
index 822a938081..af3963a05e 100644
--- a/test/controllers/repository-conflict-controller.test.js
+++ b/test/controllers/repository-conflict-controller.test.js
@@ -65,7 +65,7 @@ describe('RepositoryConflictController', () => {
app = React.cloneElement(app, {repository});
const wrapper = mount(app);
- await assert.async.equal(wrapper.find(EditorConflictController).length, 2);
+ await assert.async.equal(wrapper.update().find(EditorConflictController).length, 2);
});
});
diff --git a/test/controllers/root-controller.test.js b/test/controllers/root-controller.test.js
index b8ac62c1c2..3ddc313c9b 100644
--- a/test/controllers/root-controller.test.js
+++ b/test/controllers/root-controller.test.js
@@ -316,6 +316,7 @@ describe('RootController', function() {
const wrapper = shallow(app);
wrapper.instance().initializeRepo();
+ wrapper.update();
assert.lengthOf(wrapper.find('Panel').find({location: 'modal'}).find('InitDialog'), 1);
});
@@ -325,6 +326,7 @@ describe('RootController', function() {
const wrapper = shallow(app);
wrapper.instance().initializeRepo();
+ wrapper.update();
const dialog = wrapper.find('InitDialog');
dialog.prop('didAccept')('/a/path');
resolveInit();
@@ -337,9 +339,11 @@ describe('RootController', function() {
const wrapper = shallow(app);
wrapper.instance().initializeRepo();
+ wrapper.update();
const dialog = wrapper.find('InitDialog');
dialog.prop('didCancel')();
+ wrapper.update();
assert.isFalse(wrapper.find('InitDialog').exists());
});
@@ -350,6 +354,7 @@ describe('RootController', function() {
const wrapper = shallow(app);
wrapper.instance().initializeRepo();
+ wrapper.update();
const dialog = wrapper.find('InitDialog');
const acceptPromise = dialog.prop('didAccept')('/a/path');
const err = new GitError('git init exited with status 1');
@@ -357,6 +362,7 @@ describe('RootController', function() {
rejectInit(err);
await acceptPromise;
+ wrapper.update();
assert.isFalse(wrapper.find('InitDialog').exists());
assert.isTrue(notificationManager.addError.calledWith(
'Unable to initialize git repository in /a/path',
@@ -380,12 +386,14 @@ describe('RootController', function() {
it('renders the modal clone panel', function() {
wrapper.instance().openCloneDialog();
+ wrapper.update();
assert.lengthOf(wrapper.find('Panel').find({location: 'modal'}).find('CloneDialog'), 1);
});
it('triggers the clone callback on accept', function() {
wrapper.instance().openCloneDialog();
+ wrapper.update();
const dialog = wrapper.find('CloneDialog');
dialog.prop('didAccept')('git@github.com:atom/github.git', '/home/me/github');
@@ -396,17 +404,20 @@ describe('RootController', function() {
it('marks the clone dialog as in progress during clone', async function() {
wrapper.instance().openCloneDialog();
+ wrapper.update();
const dialog = wrapper.find('CloneDialog');
assert.isFalse(dialog.prop('inProgress'));
const acceptPromise = dialog.prop('didAccept')('git@github.com:atom/github.git', '/home/me/github');
+ wrapper.update();
assert.isTrue(wrapper.find('CloneDialog').prop('inProgress'));
resolveClone();
await acceptPromise;
+ wrapper.update();
assert.isFalse(wrapper.find('CloneDialog').exists());
});
@@ -414,6 +425,7 @@ describe('RootController', function() {
sinon.stub(notificationManager, 'addError');
wrapper.instance().openCloneDialog();
+ wrapper.update();
const dialog = wrapper.find('CloneDialog');
assert.isFalse(dialog.prop('inProgress'));
@@ -424,6 +436,7 @@ describe('RootController', function() {
rejectClone(err);
await acceptPromise;
+ wrapper.update();
assert.isFalse(wrapper.find('CloneDialog').exists());
assert.isTrue(notificationManager.addError.calledWith(
'Unable to clone git@github.com:nope/nope.git',
@@ -433,10 +446,12 @@ describe('RootController', function() {
it('dismisses the clone panel on cancel', function() {
wrapper.instance().openCloneDialog();
+ wrapper.update();
const dialog = wrapper.find('CloneDialog');
dialog.prop('didCancel')();
+ wrapper.update();
assert.lengthOf(wrapper.find('CloneDialog'), 0);
assert.isFalse(cloneRepositoryForProjectPath.called);
});
@@ -454,6 +469,7 @@ describe('RootController', function() {
prompt: 'Password plz',
includeUsername: true,
});
+ wrapper.update();
const dialog = wrapper.find('Panel').find({location: 'modal'}).find('CredentialDialog');
assert.isTrue(dialog.exists());
@@ -466,9 +482,12 @@ describe('RootController', function() {
prompt: 'Speak "friend" and enter',
includeUsername: false,
});
+ wrapper.update();
wrapper.find('CredentialDialog').prop('onSubmit')({password: 'friend'});
assert.deepEqual(await credentialPromise, {password: 'friend'});
+
+ wrapper.update();
assert.isFalse(wrapper.find('CredentialDialog').exists());
});
@@ -477,9 +496,12 @@ describe('RootController', function() {
prompt: 'Enter the square root of 1244313452349528345',
includeUsername: false,
});
+ wrapper.update();
wrapper.find('CredentialDialog').prop('onCancel')();
await assert.isRejected(credentialPromise);
+
+ wrapper.update();
assert.isFalse(wrapper.find('CredentialDialog').exists());
});
});
diff --git a/test/controllers/status-bar-tile-controller.test.js b/test/controllers/status-bar-tile-controller.test.js
index 37883e2c72..951c07611d 100644
--- a/test/controllers/status-bar-tile-controller.test.js
+++ b/test/controllers/status-bar-tile-controller.test.js
@@ -44,7 +44,7 @@ describe('StatusBarTileController', function() {
});
function getTooltipNode(wrapper, selector) {
- const ts = tooltips.findTooltips(wrapper.find(selector).node.element);
+ const ts = tooltips.findTooltips(wrapper.find(selector).getDOMNode());
assert.lengthOf(ts, 1);
ts[0].show();
return ts[0].getTooltipElement();
@@ -57,6 +57,7 @@ describe('StatusBarTileController', function() {
const wrapper = mount(React.cloneElement(component, {repository}));
await wrapper.instance().refreshModelData();
+ wrapper.update();
assert.equal(wrapper.find(BranchView).prop('currentBranch').name, 'master');
assert.lengthOf(wrapper.find(BranchView).find('.github-branch-detached'), 0);
@@ -69,6 +70,7 @@ describe('StatusBarTileController', function() {
const wrapper = mount(React.cloneElement(component, {repository}));
await wrapper.instance().refreshModelData();
+ wrapper.update();
assert.equal(wrapper.find(BranchView).prop('currentBranch').name, 'master~2');
assert.lengthOf(wrapper.find(BranchView).find('.github-branch-detached'), 1);
@@ -95,6 +97,7 @@ describe('StatusBarTileController', function() {
const wrapper = mount(React.cloneElement(component, {repository}));
await wrapper.instance().refreshModelData();
+ wrapper.update();
const tip = getTooltipNode(wrapper, BranchView);
const selectList = tip.querySelector('select');
@@ -281,6 +284,7 @@ describe('StatusBarTileController', function() {
statusBarTile = mount(React.cloneElement(component, {repository}));
await statusBarTile.instance().refreshModelData();
+ statusBarTile.update();
sinon.spy(repository, 'fetch');
sinon.spy(repository, 'push');
@@ -318,6 +322,7 @@ describe('StatusBarTileController', function() {
statusBarTile = mount(React.cloneElement(component, {repository}));
await statusBarTile.instance().refreshModelData();
+ statusBarTile.update();
sinon.spy(repository, 'fetch');
sinon.spy(repository, 'push');
@@ -356,6 +361,7 @@ describe('StatusBarTileController', function() {
statusBarTile = mount(React.cloneElement(component, {repository}));
await statusBarTile.instance().refreshModelData();
+ statusBarTile.update();
sinon.spy(repository, 'fetch');
sinon.spy(repository, 'push');
@@ -394,6 +400,7 @@ describe('StatusBarTileController', function() {
statusBarTile = mount(React.cloneElement(component, {repository}));
await statusBarTile.instance().refreshModelData();
+ statusBarTile.update();
sinon.spy(repository, 'fetch');
sinon.spy(repository, 'push');
@@ -433,6 +440,7 @@ describe('StatusBarTileController', function() {
statusBarTile = mount(React.cloneElement(component, {repository}));
await statusBarTile.instance().refreshModelData();
+ statusBarTile.update();
sinon.spy(repository, 'fetch');
sinon.spy(repository, 'push');
@@ -473,6 +481,7 @@ describe('StatusBarTileController', function() {
statusBarTile = mount(React.cloneElement(component, {repository}));
await statusBarTile.instance().refreshModelData();
+ statusBarTile.update();
sinon.spy(repository, 'fetch');
sinon.spy(repository, 'push');
@@ -503,6 +512,7 @@ describe('StatusBarTileController', function() {
statusBarTile = mount(React.cloneElement(component, {repository}));
await statusBarTile.instance().refreshModelData();
+ statusBarTile.update();
sinon.spy(repository, 'fetch');
sinon.spy(repository, 'push');
diff --git a/test/decorators/observe-model.test.js b/test/decorators/observe-model.test.js
index 314900655b..4bbb60171c 100644
--- a/test/decorators/observe-model.test.js
+++ b/test/decorators/observe-model.test.js
@@ -51,25 +51,25 @@ describe('ObserveModelDecorator', function() {
assert.isTrue(wrapper.is('ObserveModelDecorator(TestComponent)'));
- await assert.async.equal(wrapper.find('TestComponent').prop('one'), 1);
- await assert.async.equal(wrapper.find('TestComponent').prop('two'), 2);
- await assert.async.equal(wrapper.find('TestComponent').prop('testModel'), model);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), 1);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), 2);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('testModel'), model);
model.update({one: 'one', two: 'two'});
- await assert.async.equal(wrapper.find('TestComponent').prop('one'), 'one');
- await assert.async.equal(wrapper.find('TestComponent').prop('two'), 'two');
- await assert.async.equal(wrapper.find('TestComponent').prop('testModel'), model);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), 'one');
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), 'two');
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('testModel'), model);
wrapper.setProps({testModel: null});
- await assert.async.equal(wrapper.find('TestComponent').prop('one'), undefined);
- await assert.async.equal(wrapper.find('TestComponent').prop('two'), undefined);
- await assert.async.isNull(wrapper.find('TestComponent').prop('testModel'));
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), undefined);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), undefined);
+ await assert.async.isNull(wrapper.update().find('TestComponent').prop('testModel'));
const model2 = new TestModel({one: 1, two: 2});
wrapper.setProps({testModel: model2});
- await assert.async.equal(wrapper.find('TestComponent').prop('one'), 1);
- await assert.async.equal(wrapper.find('TestComponent').prop('two'), 2);
- await assert.async.equal(wrapper.find('TestComponent').prop('testModel'), model2);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('one'), 1);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('two'), 2);
+ await assert.async.equal(wrapper.update().find('TestComponent').prop('testModel'), model2);
});
it('hosts static methods', function() {
diff --git a/test/models/ref-holder.test.js b/test/models/ref-holder.test.js
new file mode 100644
index 0000000000..97a62791fd
--- /dev/null
+++ b/test/models/ref-holder.test.js
@@ -0,0 +1,68 @@
+import RefHolder from '../../lib/models/ref-holder';
+
+describe('RefHolder', function() {
+ let sub;
+
+ afterEach(function() {
+ if (sub) { sub.dispose(); }
+ });
+
+ it('begins empty', function() {
+ const h = new RefHolder();
+ assert.isTrue(h.isEmpty());
+ assert.throws(() => h.get(), /empty/);
+ });
+
+ it('does not become populated when assigned null', function() {
+ const h = new RefHolder();
+ h.setter(null);
+ assert.isTrue(h.isEmpty());
+ });
+
+ it('provides synchronous access to its current value', function() {
+ const h = new RefHolder();
+ h.setter(1234);
+ assert.isFalse(h.isEmpty());
+ assert.strictEqual(h.get(), 1234);
+ });
+
+ it('notifies subscribers when it becomes available', function() {
+ const h = new RefHolder();
+ const callback = sinon.spy();
+ sub = h.observe(callback);
+
+ h.setter(1);
+ assert.isTrue(callback.calledWith(1));
+
+ h.setter(2);
+ assert.isTrue(callback.calledWith(2));
+
+ sub.dispose();
+
+ h.setter(3);
+ assert.isFalse(callback.calledWith(3));
+ });
+
+ it('immediately notifies new subscribers if it is already available', function() {
+ const h = new RefHolder();
+ h.setter(12);
+
+ const callback = sinon.spy();
+ sub = h.observe(callback);
+ assert.isTrue(callback.calledWith(12));
+ });
+
+ describe('.on()', function() {
+ it('returns an existing RefHolder as-is', function() {
+ const original = new RefHolder();
+ const wrapped = RefHolder.on(original);
+ assert.strictEqual(original, wrapped);
+ });
+
+ it('wraps a non-RefHolder value with a RefHolder set to it', function() {
+ const wrapped = RefHolder.on(9000);
+ assert.isFalse(wrapped.isEmpty());
+ assert.strictEqual(wrapped.get(), 9000);
+ });
+ });
+});
diff --git a/test/runner.js b/test/runner.js
index 68b438a27f..7066d32479 100644
--- a/test/runner.js
+++ b/test/runner.js
@@ -16,6 +16,10 @@ module.exports = createRunner({
reporter: process.env.MOCHA_REPORTER || 'spec',
overrideTestPaths: [/spec$/, /test/],
}, mocha => {
+ const Enzyme = require('enzyme');
+ const Adapter = require('enzyme-adapter-react-15');
+ Enzyme.configure({adapter: new Adapter()});
+
require('mocha-stress');
mocha.timeout(parseInt(process.env.MOCHA_TIMEOUT || '5000', 10));
diff --git a/test/views/clone-dialog.test.js b/test/views/clone-dialog.test.js
index 200732a459..fd3c9a1f39 100644
--- a/test/views/clone-dialog.test.js
+++ b/test/views/clone-dialog.test.js
@@ -69,6 +69,7 @@ describe('CloneDialog', function() {
it('disables the clone button with no remote URL', function() {
setTextIn('.github-ProjectPath atom-text-editor', path.join('somewhere', 'else'));
setTextIn('.github-CloneUrl atom-text-editor', '');
+ wrapper.update();
assert.isTrue(wrapper.find('button.icon-repo-clone').prop('disabled'));
});
@@ -76,6 +77,7 @@ describe('CloneDialog', function() {
it('disables the clone button with no project path', function() {
setTextIn('.github-ProjectPath atom-text-editor', '');
setTextIn('.github-CloneUrl atom-text-editor', 'git@github.com:atom/github.git');
+ wrapper.update();
assert.isTrue(wrapper.find('button.icon-repo-clone').prop('disabled'));
});
@@ -83,6 +85,7 @@ describe('CloneDialog', function() {
it('enables the clone button when both text boxes are populated', function() {
setTextIn('.github-ProjectPath atom-text-editor', path.join('somewhere', 'else'));
setTextIn('.github-CloneUrl atom-text-editor', 'git@github.com:atom/github.git');
+ wrapper.update();
assert.isFalse(wrapper.find('button.icon-repo-clone').prop('disabled'));
});
diff --git a/test/views/co-author-form.test.js b/test/views/co-author-form.test.js
index 53ab56136b..cc1167f82f 100644
--- a/test/views/co-author-form.test.js
+++ b/test/views/co-author-form.test.js
@@ -35,7 +35,7 @@ describe('CoAuthorForm', function() {
const name = 'Original Name';
app = React.cloneElement(app, {name});
wrapper = mount(app);
- assert.strictEqual(wrapper.find('.github-CoAuthorForm-name').node.value, name);
+ assert.strictEqual(wrapper.find('.github-CoAuthorForm-name').prop('value'), name);
});
});
@@ -60,7 +60,7 @@ describe('CoAuthorForm', function() {
wrapper = mount(app);
const submitButton = wrapper.find('.btn-primary');
- assert.isTrue(submitButton.node.disabled);
+ assert.isTrue(submitButton.prop('disabled'));
submitButton.simulate('click');
assert.isFalse(didSubmit.called);
});
@@ -74,7 +74,7 @@ describe('CoAuthorForm', function() {
setTextIn('.github-CoAuthorForm-email', email);
const submitButton = wrapper.find('.btn-primary');
- assert.isTrue(submitButton.node.disabled);
+ assert.isTrue(submitButton.prop('disabled'));
submitButton.simulate('click');
assert.isFalse(didSubmit.called);
});
@@ -89,7 +89,7 @@ describe('CoAuthorForm', function() {
it('calls cancel prop when `core:cancel` is triggered', function() {
wrapper = mount(app);
- atomEnv.commands.dispatch(wrapper.find('.github-CoAuthorForm').getNode(), 'core:cancel');
+ atomEnv.commands.dispatch(wrapper.find('.github-CoAuthorForm').getDOMNode(), 'core:cancel');
assert.isTrue(didCancel.called);
});
});
diff --git a/test/views/commands.test.js b/test/views/commands.test.js
index 3882cd5633..7030296d2d 100644
--- a/test/views/commands.test.js
+++ b/test/views/commands.test.js
@@ -2,6 +2,7 @@ import React from 'react';
import {mount} from 'enzyme';
import Commands, {Command} from '../../lib/views/commands';
+import RefHolder from '../../lib/models/ref-holder';
describe('Commands', function() {
let atomEnv, commandRegistry;
@@ -89,4 +90,22 @@ describe('Commands', function() {
assert.equal(callback1.callCount, 0);
assert.equal(callback2.callCount, 1);
});
+
+ it('allows the target prop to be a RefHolder to capture refs from parent components', function() {
+ const callback = sinon.spy();
+ const holder = new RefHolder();
+ mount(
+
+
+ ,
+ );
+
+ const element = document.createElement('div');
+ commandRegistry.dispatch(element, 'github:do-thing');
+ assert.isFalse(callback.called);
+
+ holder.setter(element);
+ commandRegistry.dispatch(element, 'github:do-thing');
+ assert.isTrue(callback.called);
+ });
});
diff --git a/test/views/commit-view.test.js b/test/views/commit-view.test.js
index f0dc1efc00..61474a5ea3 100644
--- a/test/views/commit-view.test.js
+++ b/test/views/commit-view.test.js
@@ -62,39 +62,38 @@ describe('CommitView', function() {
it('displays the remaining characters limit based on which line is being edited', function() {
const wrapper = mount(app);
- const remaining = wrapper.find('.github-CommitView-remaining-characters');
- assert.strictEqual(remaining.text(), '72');
+ assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '72');
wrapper.setProps({message: 'abcde fghij'});
- assert.strictEqual(remaining.text(), '61');
- assert.isFalse(remaining.hasClass('is-error'));
- assert.isFalse(remaining.hasClass('is-warning'));
+ assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '61');
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-error'));
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-warning'));
wrapper.setProps({message: '\nklmno'});
- assert.strictEqual(remaining.text(), '∞');
- assert.isFalse(remaining.hasClass('is-error'));
- assert.isFalse(remaining.hasClass('is-warning'));
+ assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '∞');
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-error'));
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-warning'));
wrapper.setProps({message: 'abcde\npqrst'});
- assert.strictEqual(remaining.text(), '∞');
- assert.isFalse(remaining.hasClass('is-error'));
- assert.isFalse(remaining.hasClass('is-warning'));
+ assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '∞');
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-error'));
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-warning'));
- wrapper.find('atom-text-editor').getNode().getModel().setCursorBufferPosition([0, 3]);
+ wrapper.find('atom-text-editor').getDOMNode().getModel().setCursorBufferPosition([0, 3]);
wrapper.update();
- assert.strictEqual(remaining.text(), '67');
- assert.isFalse(remaining.hasClass('is-error'));
- assert.isFalse(remaining.hasClass('is-warning'));
+ assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '67');
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-error'));
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-warning'));
wrapper.setProps({stagedChangesExist: true, maximumCharacterLimit: 50});
- assert.strictEqual(remaining.text(), '45');
- assert.isFalse(remaining.hasClass('is-error'));
- assert.isFalse(remaining.hasClass('is-warning'));
+ assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '45');
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-error'));
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-warning'));
wrapper.setProps({message: 'a'.repeat(41)});
- assert.strictEqual(remaining.text(), '9');
- assert.isFalse(remaining.hasClass('is-error'));
- assert.isTrue(remaining.hasClass('is-warning'));
+ assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '9');
+ assert.isFalse(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-error'));
+ assert.isTrue(wrapper.find('.github-CommitView-remaining-characters').hasClass('is-warning'));
wrapper.setProps({message: 'a'.repeat(58)});
assert.strictEqual(wrapper.find('.github-CommitView-remaining-characters').text(), '-8');
@@ -103,7 +102,7 @@ describe('CommitView', function() {
});
describe('the commit button', function() {
- let wrapper, commitButton;
+ let wrapper;
beforeEach(async function() {
const workdirPath = await cloneRepository('three-files');
@@ -116,53 +115,51 @@ describe('CommitView', function() {
message: 'something',
});
wrapper = mount(app);
-
- commitButton = wrapper.find('.github-CommitView-commit');
});
it('is disabled when no changes are staged', function() {
wrapper.setProps({stagedChangesExist: false});
- assert.isTrue(commitButton.prop('disabled'));
+ assert.isTrue(wrapper.find('.github-CommitView-commit').prop('disabled'));
wrapper.setProps({stagedChangesExist: true});
- assert.isFalse(commitButton.prop('disabled'));
+ assert.isFalse(wrapper.find('.github-CommitView-commit').prop('disabled'));
});
it('is disabled when there are merge conflicts', function() {
wrapper.setProps({mergeConflictsExist: true});
- assert.isTrue(commitButton.prop('disabled'));
+ assert.isTrue(wrapper.find('.github-CommitView-commit').prop('disabled'));
wrapper.setProps({mergeConflictsExist: false});
- assert.isFalse(commitButton.prop('disabled'));
+ assert.isFalse(wrapper.find('.github-CommitView-commit').prop('disabled'));
});
it('is disabled when the commit message is empty', function() {
wrapper.setProps({message: ''});
- assert.isTrue(commitButton.prop('disabled'));
+ assert.isTrue(wrapper.find('.github-CommitView-commit').prop('disabled'));
wrapper.setProps({message: 'Not empty'});
- assert.isFalse(commitButton.prop('disabled'));
+ assert.isFalse(wrapper.find('.github-CommitView-commit').prop('disabled'));
});
it('displays the current branch name', function() {
const currentBranch = new Branch('aw-do-the-stuff');
wrapper.setProps({currentBranch});
- assert.strictEqual(commitButton.text(), 'Commit to aw-do-the-stuff');
+ assert.strictEqual(wrapper.find('.github-CommitView-commit').text(), 'Commit to aw-do-the-stuff');
});
it('indicates when a commit will be detached', function() {
const currentBranch = Branch.createDetached('master~3');
wrapper.setProps({currentBranch});
- assert.strictEqual(commitButton.text(), 'Create detached commit');
+ assert.strictEqual(wrapper.find('.github-CommitView-commit').text(), 'Create detached commit');
});
it('displays a progress message while committing', function() {
wrapper.setState({showWorking: true});
- assert.strictEqual(commitButton.text(), 'Working...');
+ assert.strictEqual(wrapper.find('.github-CommitView-commit').text(), 'Working...');
});
it('falls back to "commit" with no current branch', function() {
- assert.strictEqual(commitButton.text(), 'Commit');
+ assert.strictEqual(wrapper.find('.github-CommitView-commit').text(), 'Commit');
});
});
@@ -177,16 +174,15 @@ describe('CommitView', function() {
app = React.cloneElement(app, {stagedChangesExist: true, prepareToCommit, commit, message: 'Something'});
wrapper = mount(app);
- editorElement = wrapper.find('atom-text-editor').getNode();
+ editorElement = wrapper.find('atom-text-editor').getDOMNode();
sinon.spy(editorElement, 'focus');
editor = editorElement.getModel();
// Perform an extra render to ensure the editor text is reflected in the commit button enablement.
// The controller accomplishes this by re-rendering on Repository update.
- wrapper.update();
+ wrapper.setProps({});
commitButton = wrapper.find('.github-CommitView-commit');
-
workspaceElement = atomEnv.views.getView(atomEnv.workspace);
});
@@ -196,6 +192,7 @@ describe('CommitView', function() {
});
it('calls props.commit(message) when the commit button is clicked', async function() {
+ wrapper.update();
commitButton.simulate('click');
await assert.async.isTrue(commit.calledWith('Something'));
diff --git a/test/views/file-patch-view.test.js b/test/views/file-patch-view.test.js
index 4292216d0e..59132587d3 100644
--- a/test/views/file-patch-view.test.js
+++ b/test/views/file-patch-view.test.js
@@ -82,6 +82,7 @@ describe('FilePatchView', function() {
getHunkView(0).prop('mousedownOnLine')({button: 0, detail: 1}, hunks[0], hunks[0].lines[2]);
getHunkView(1).prop('mousemoveOnLine')({}, hunks[1], hunks[1].lines[1]);
wrapper.instance().mouseup();
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isTrue(getHunkView(0).prop('selectedLines').has(hunks[0].lines[2]));
@@ -91,6 +92,7 @@ describe('FilePatchView', function() {
// start a new selection, drag it across an existing selection
getHunkView(1).prop('mousedownOnLine')({button: 0, detail: 1, metaKey: true}, hunks[1], hunks[1].lines[3]);
getHunkView(0).prop('mousemoveOnLine')({}, hunks[0], hunks[0].lines[0]);
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isTrue(getHunkView(0).prop('selectedLines').has(hunks[0].lines[1]));
@@ -102,6 +104,7 @@ describe('FilePatchView', function() {
// drag back down without releasing mouse; the other selection remains intact
getHunkView(1).prop('mousemoveOnLine')({}, hunks[1], hunks[1].lines[3]);
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isFalse(getHunkView(0).prop('selectedLines').has(hunks[0].lines[1]));
@@ -114,6 +117,7 @@ describe('FilePatchView', function() {
// drag back up so selections are adjacent, then release the mouse. selections should merge.
getHunkView(1).prop('mousemoveOnLine')({}, hunks[1], hunks[1].lines[2]);
wrapper.instance().mouseup();
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isTrue(getHunkView(0).prop('selectedLines').has(hunks[0].lines[2]));
@@ -124,12 +128,14 @@ describe('FilePatchView', function() {
// we detect merged selections based on the head here
wrapper.instance().selectToNext();
+ wrapper.update();
assert.isFalse(getHunkView(0).prop('isSelected'));
assert.isFalse(getHunkView(0).prop('selectedLines').has(hunks[0].lines[2]));
// double-clicking clears the existing selection and starts hunk-wise selection
getHunkView(0).prop('mousedownOnLine')({button: 0, detail: 2}, hunks[0], hunks[0].lines[2]);
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isTrue(getHunkView(0).prop('selectedLines').has(hunks[0].lines[1]));
@@ -137,6 +143,7 @@ describe('FilePatchView', function() {
assert.isFalse(getHunkView(1).prop('isSelected'));
getHunkView(1).prop('mousemoveOnLine')({}, hunks[1], hunks[1].lines[1]);
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isTrue(getHunkView(0).prop('selectedLines').has(hunks[0].lines[1]));
@@ -148,6 +155,7 @@ describe('FilePatchView', function() {
// clicking the header clears the existing selection and starts hunk-wise selection
getHunkView(0).prop('mousedownOnHeader')({button: 0, detail: 1}, hunks[0], hunks[0].lines[2]);
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isTrue(getHunkView(0).prop('selectedLines').has(hunks[0].lines[1]));
@@ -155,6 +163,7 @@ describe('FilePatchView', function() {
assert.isFalse(getHunkView(1).prop('isSelected'));
getHunkView(1).prop('mousemoveOnLine')({}, hunks[1], hunks[1].lines[1]);
+ wrapper.update();
assert.isTrue(getHunkView(0).prop('isSelected'));
assert.isTrue(getHunkView(0).prop('selectedLines').has(hunks[0].lines[1]));
@@ -470,6 +479,7 @@ describe('FilePatchView', function() {
assert.equal(wrapper.find('HunkView').prop('stageButtonLabel'), 'Unstage Hunk');
wrapper.instance().togglePatchSelectionMode();
+ wrapper.update();
assert.equal(wrapper.find('HunkView').prop('stageButtonLabel'), 'Unstage Selection');
wrapper.setProps({stagingStatus: 'unstaged'});
diff --git a/test/views/init-dialog.test.js b/test/views/init-dialog.test.js
index 3991558c49..974f7e5d8f 100644
--- a/test/views/init-dialog.test.js
+++ b/test/views/init-dialog.test.js
@@ -34,6 +34,7 @@ describe('InitDialog', function() {
const setTextIn = function(selector, text) {
wrapper.find(selector).getDOMNode().getModel().setText(text);
+ wrapper.update();
};
it('defaults to your project home path', function() {