diff --git a/apm/package-lock.json b/apm/package-lock.json index e9b15786d..614b07fa1 100644 --- a/apm/package-lock.json +++ b/apm/package-lock.json @@ -21,6 +21,7 @@ "node-gyp": "3.4.0", "npm": "6.2.0", "open": "0.0.5", + "plist": "git+https://github.com/nathansobo/node-plist.git#bd3a93387f1d4b2cff819b200870d35465796e77", "q": "~0.9.7", "read": "~1.0.5", "request": "^2.87.0", @@ -3973,7 +3974,7 @@ }, "plist": { "version": "git+https://github.com/nathansobo/node-plist.git#bd3a93387f1d4b2cff819b200870d35465796e77", - "from": "git+https://github.com/nathansobo/node-plist.git#bd3a93387f1d4b2cff819b200870d35465796e77", + "from": "git+https://github.com/nathansobo/node-plist.git", "requires": { "xmlbuilder": "0.4.x", "xmldom": "0.1.x" diff --git a/package-lock.json b/package-lock.json index 1b239486b..ddb0b422c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "atom", - "version": "1.36.0-dev", + "version": "1.37.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { "@atom/nsfw": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@atom/nsfw/-/nsfw-1.0.21.tgz", - "integrity": "sha512-ERz76RZstjDvZPM4FHwgO+9dzGkki0mJsn2r+Mu4yn29MzUuAtSmtyJDPmupVUvmz2PF7KvRbC/W2A8A6BHQhA==", + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/@atom/nsfw/-/nsfw-1.0.18.tgz", + "integrity": "sha512-YceKV9a3X62mh4Q78Nyi8aTRaoVGdpeJBHogL8gxU17iBhEpYvxGgMfTe02j1hH2taFT4barkZ5RdZkGKIsJ/w==", "requires": { - "fs-extra": "^7.0.0", + "fs-extra": "^0.26.5", "lodash.isinteger": "^4.0.4", "lodash.isundefined": "^3.0.1", - "nan": "^2.10.0", - "promisify-node": "^0.5.0" + "nan": "^2.0.0", + "promisify-node": "^0.3.0" } }, "@atom/source-map-support": { @@ -34,33 +34,30 @@ } }, "@atom/watcher": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@atom/watcher/-/watcher-1.0.8.tgz", - "integrity": "sha512-53un+vGSaY7Fsbvmg8gerYOA3ISipMWR3qiYR9hZWqSfvFsksXJfGrmFsfbBj3uqGRQ3gPTi6wpxcFSWjbWVFQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@atom/watcher/-/watcher-1.3.1.tgz", + "integrity": "sha512-UjGisruWlcRLMzddE3pwvOx6wQCFN/+gg6Z4cJZvH1kjT5QT5eA04hUDs8QXF/QH8ZxMiOtP8x9SWTw0hCNelg==", "requires": { - "event-kit": "^2.5.0", - "fs-extra": "^6.0.0", - "nan": "^2.10.0", - "node-pre-gyp": "^0.10.0" + "event-kit": "2.5.3", + "fs-extra": "7.0.1", + "nan": "2.12.1", + "prebuild-install": "5.2.4" }, "dependencies": { "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "requires": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" } } }, @@ -78,11 +75,6 @@ "resolved": "https://registry.npmjs.org/CSSwhat/-/CSSwhat-0.4.7.tgz", "integrity": "sha1-hn2g/zn3eGEyQsRM/qg/CqTr35s=" }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "about": { "version": "file:packages/about", "requires": { @@ -314,9 +306,9 @@ } }, "atom-keymap": { - "version": "8.2.12", - "resolved": "https://registry.npmjs.org/atom-keymap/-/atom-keymap-8.2.12.tgz", - "integrity": "sha512-qAXylPa/uysvYhZC1zJR7yG7QFs2RqU+2646JYGeiqgm0nMxLpdJpJ9ABIXbDNxzjhlRXNmZkr5V6N22RWjh4Q==", + "version": "8.2.13", + "resolved": "https://registry.npmjs.org/atom-keymap/-/atom-keymap-8.2.13.tgz", + "integrity": "sha512-RNf+5KbAiXpNV2KZT0+XYpTRFE8rhq7NrBryghJAOlwayY3g3z6Kp9tMfaPJ05BkPo9mChcaFO6SKUL8LTQcBg==", "requires": { "clear-cut": "^2", "emissary": "^1.1.0", @@ -1172,6 +1164,14 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, + "cached-run-in-this-context": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cached-run-in-this-context/-/cached-run-in-this-context-0.5.0.tgz", + "integrity": "sha512-FdtDP0u8WjetQ95nLz9vI06efJTFrmtmk5ZT6FECpyTKi9aakNLMHyMH21WRbGYyWlbmB/QlRoB/g1lcEpyjMw==", + "requires": { + "nan": "^2.10.0" + } + }, "caller-callsite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", @@ -1311,6 +1311,11 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" }, + "circular-json": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.7.tgz", + "integrity": "sha512-/pXoV1JA847qRKPrHbBK6YIBGFF8GOP4wzSgUOA7q0ew0vAv0iJswP+2/nZQ9uzA3Azi7eTrg9L2yzXc/7ZMIA==" + }, "classnames": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", @@ -1638,9 +1643,9 @@ } }, "date-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.0.0.tgz", - "integrity": "sha512-M6UqVvZVgFYqZL1SfHsRGIQSz3ZL+qgbsV5Lp1Vj61LZVYuEwcMXYay7DRDtYs2HQQBK5hQtQ0fD9aEJ89V0LA==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", + "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=" }, "debug": { "version": "2.6.9", @@ -2227,8 +2232,8 @@ } }, "find-and-replace": { - "version": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.0/tarball", - "integrity": "sha512-c77OpEcgce8cfPgjPQStEvK016AVmNMUfKIuCYgzYbjTik2lCSU+QAEG3q6MitIzqoFiSW6WW3FL502HFyZwKg==", + "version": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.9/tarball", + "integrity": "sha512-j1KWRa8Ki9P3G3//kP9sWUqa8vva3+HiWfZJAR8kAaOJFUqy9EGOp6SR3Xs6ChQbk8kHmQBnIcYUjCz/gDoJ/g==", "requires": { "binary-search": "^1.3.3", "element-resize-detector": "^1.1.10", @@ -2274,11 +2279,6 @@ } } }, - "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==" - }, "flatten": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", @@ -2393,13 +2393,25 @@ "integrity": "sha1-a+Dem+mYzhavivwkSXue6bfM2a0=" }, "fs-extra": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", - "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", "requires": { "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "jsonfile": { + "version": "2.4.0", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + } } }, "fs-minipass": { @@ -2411,9 +2423,9 @@ } }, "fs-plus": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.1.1.tgz", - "integrity": "sha512-Se2PJdOWXqos1qVTkvqqjb0CSnfBnwwD+pq+z4ksT+e97mEShod/hrNg0TRCCsXPbJzcIq+NuzQhigunMWMJUA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.0.2.tgz", + "integrity": "sha1-a19Sp3EolMTd6f2PgfqMYN8EHz0=", "requires": { "async": "^1.5.2", "mkdirp": "^0.5.1", @@ -2423,7 +2435,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" } } @@ -2472,8 +2484,8 @@ "integrity": "sha1-gy9kifvodnaUWVmckUpnDsIpR+4=" }, "fuzzy-finder": { - "version": "https://www.atom.io/api/packages/fuzzy-finder/versions/1.9.0/tarball", - "integrity": "sha512-YJTPPMZLQmDiUa6eoONhHF7sOvAPQqrXNFLfPGAUItDUoJxvJBwkPcxh+ryCHLlO8MuoJdDvAKHsU0mGzb/JeQ==", + "version": "https://www.atom.io/api/packages/fuzzy-finder/versions/1.9.2/tarball", + "integrity": "sha512-gpr1DXfxCEwMO48uCudocIZomhHlMJk+l1lAXJXwamtprDRsuzSqVMFjlyM49/cn4P5ToMhVHHHFngg0wklpvw==", "requires": { "async": "0.2.6", "atom-select-list": "^0.7.0", @@ -2825,14 +2837,6 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "requires": { - "minimatch": "^3.0.4" - } - }, "image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -3233,6 +3237,14 @@ "is-buffer": "^1.1.5" } }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, "language-c": { "version": "https://www.atom.io/api/packages/language-c/versions/0.60.14/tarball", "integrity": "sha512-jGFMc9vNutvBflFdoUXWJqxBFdMFG7n13PxS8z+SB8H0taZStq55JixDaQ0i/qu7ay4K5BCJ7y/PHdL13vjouQ==", @@ -3293,8 +3305,8 @@ "integrity": "sha512-kdTsc2efREnuj72WsAfcx28h0RqrVUIGF7BQcS5zy+ZibqbvnaB5DiVunRFYLhefGoaKVkAyTdRkZWMKH/yIWg==" }, "language-javascript": { - "version": "https://www.atom.io/api/packages/language-javascript/versions/0.129.19/tarball", - "integrity": "sha512-ClPU0dc41WqagaPd+qy5DQ5ahDCVcfFxC1nwppyQSsSXzrzsiE2+FuYzG33OMaSGDA6/zZ6gBkHXnXXPFzWcNA==", + "version": "https://www.atom.io/api/packages/language-javascript/versions/0.129.20/tarball", + "integrity": "sha512-zHIaVdZYxxoabugCVDnOsrhy0oZC8hLPVKOZoT8EwPIWfRRNedTshFVhABUTH+dmiOUUS++izylwng2yWggpzA==", "requires": { "tree-sitter-javascript": "^0.13.10", "tree-sitter-jsdoc": "^0.13.4", @@ -3677,21 +3689,21 @@ "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" }, "log4js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.0.0.tgz", - "integrity": "sha512-XlxZfcFAvQjnjCJBIV/EpsPmrVC12n+TxNUKgrmQh6eeA+9X+6UqvaRNV8t6dpMtXszl1LAQimB4pqyp2Gsgfw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.5.tgz", + "integrity": "sha512-IX5c3G/7fuTtdr0JjOT2OIR12aTESVhsH6cEsijloYwKgcPRlO6DgOU72v0UFhWcoV1HN6+M3dwT89qVPLXm0w==", "requires": { - "date-format": "^2.0.0", + "circular-json": "^0.5.5", + "date-format": "^1.2.0", "debug": "^3.1.0", - "flatted": "^2.0.0", "rfdc": "^1.1.2", - "streamroller": "^1.0.1" + "streamroller": "0.7.0" }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "requires": { "ms": "^2.1.1" } @@ -3884,14 +3896,6 @@ "yallist": "^3.0.0" } }, - "minizlib": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", - "integrity": "sha1-EeE2WM5GvDpwomeqxYNZ0eDCnOs=", - "requires": { - "minipass": "^2.2.1" - } - }, "mixto": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/mixto/-/mixto-1.0.0.tgz", @@ -4070,16 +4074,6 @@ "underscore": ">=1.3.1" } }, - "needle": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.2.tgz", - "integrity": "sha512-mW7W8dKuVYefCpNzE3Z7xUmPI9wSrSL/1qH31YGMxmSOAnjatS3S9Zv3cmiHrhx3Jkp1SrWWBdOFXjfF48Uq3A==", - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -4117,30 +4111,6 @@ "is-stream": "^1.0.1" } }, - "node-pre-gyp": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", - "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - }, - "dependencies": { - "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" - } - } - }, "node-uuid": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", @@ -4159,15 +4129,6 @@ "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -4192,20 +4153,6 @@ "temp": "^0.8.1" } }, - "npm-bundled": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", - "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==" - }, - "npm-packlist": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.11.tgz", - "integrity": "sha512-CxKlZ24urLkJk+9kCm48RTQ7L4hsmgSVzEk0TLGPzzyuFxD7VNgy5Sl24tOLMzQv773a/NeJ1ce1DKeacqffEA==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -4299,15 +4246,6 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "output-file-sync": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", @@ -4352,9 +4290,9 @@ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" }, "pathwatcher": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/pathwatcher/-/pathwatcher-8.0.2.tgz", - "integrity": "sha512-zuP+fLmB2IB6z89ikcehA+vG/ITx3Cmhaj3DJrBgnbdss6dwPolSq7cDBjgZ78Vl+SXmG7CHGIOM5mqdT9h7BQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/pathwatcher/-/pathwatcher-8.0.1.tgz", + "integrity": "sha1-UaLOKgHbbDLYZ/ZYXvKEvmvQo64=", "requires": { "async": "~0.2.10", "emissary": "^1.3.2", @@ -4362,19 +4300,19 @@ "fs-plus": "^3.0.0", "grim": "^2.0.1", "iconv-lite": "~0.4.4", - "nan": "^2.10.0", + "nan": "2.x", "underscore-plus": "~1.x" }, "dependencies": { "async": { "version": "0.2.10", - "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" }, "grim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", - "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "integrity": "sha1-52CinKe4NDsMH/r2ziDyGkbuiu0=", "requires": { "event-kit": "^2.0.0" } @@ -4512,12 +4450,11 @@ } }, "promisify-node": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/promisify-node/-/promisify-node-0.5.0.tgz", - "integrity": "sha512-GR2E4qgCoKFTprhULqP2OP3bl8kHo16XtnqtkHH6be7tPW1yL6rXd15nl3oV2sLTFv1+j6tqoF69VVpFtJ/j+A==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/promisify-node/-/promisify-node-0.3.0.tgz", + "integrity": "sha1-tLVaz5D6p9K4uQyjlomQhsAwYM8=", "requires": { - "nodegit-promise": "^4.0.0", - "object-assign": "^4.1.1" + "nodegit-promise": "~4.0.0" } }, "prop-types": { @@ -4920,11 +4857,6 @@ "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=" }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "scandal": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/scandal/-/scandal-3.1.0.tgz", @@ -5071,8 +5003,8 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "settings-view": { - "version": "https://www.atom.io/api/packages/settings-view/versions/0.259.0/tarball", - "integrity": "sha512-jNQuTozGf1uQtS4Y4hlUT15STnmnKlAWzzJOIe+ts2S/SdrGHZwxhNykJFzM8gC8j4N3Kjb8CkgmbCDzZ98aEw==", + "version": "https://www.atom.io/api/packages/settings-view/versions/0.260.0/tarball", + "integrity": "sha512-uZspfpknxC+47Zr9FJmkrAzR+a7yprv6hevZU6dUIQ1q2n0ws/Bxm6mgZVjyxpDmnNRwxiMrzCl2UArE05sNOg==", "requires": { "async": "~0.2.9", "dompurify": "^1.0.2", @@ -5182,8 +5114,8 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" }, "snippets": { - "version": "https://www.atom.io/api/packages/snippets/versions/1.4.0/tarball", - "integrity": "sha512-pfw/YOwYeU4xJBEe/qztZx8Lq0ODLtSy7seoaMRu4w1lGlzl0HsqBeqTwLxR6bak3nS5WLj+k3hSJO+yNJWH2w==", + "version": "https://www.atom.io/api/packages/snippets/versions/1.4.1/tarball", + "integrity": "sha512-bLQmuMmyC+Sfjm/jdPbY3j/Vml3E4ApKhMFRb4AJLTde5m7TEhdTmsQBtkf+vFqGfr76tZ144F9NWH12ryS5rw==", "requires": { "async": "~0.2.6", "atom-select-list": "^0.7.0", @@ -5256,22 +5188,22 @@ "integrity": "sha1-enzShHDMbToc/m1miG9rxDDTrIc=" }, "spell-check": { - "version": "https://www.atom.io/api/packages/spell-check/versions/0.74.3/tarball", - "integrity": "sha512-QYy0xpSKp8OSZjlvq7fOjrf/NdG4BncHvn9AmO/vcuJvWAoiwEWhqqB+BSA6uSrxOxJeJhbDJSxOrvm4YXs1xQ==", + "version": "https://www.atom.io/api/packages/spell-check/versions/0.74.2/tarball", + "integrity": "sha512-WhwhDF4nznhQuwnRemZbXODI6aqax2HlHudfLEjbhzkRGjEbfssRK82lRRvCK9LmQh3fAqCqbmELU40NEODJ8Q==", "requires": { "atom-pathspec": "^0.0.0", "atom-select-list": "^0.7.0", "multi-integer-range": "^2.0.0", "natural": "^0.4.0", - "spellchecker": "^3.5.1", + "spellchecker": "^3.4.4", "spelling-manager": "^1.1.0", "underscore-plus": "^1" } }, "spellchecker": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/spellchecker/-/spellchecker-3.5.1.tgz", - "integrity": "sha512-R1qUBsDZzio+7MFZN6/AtPUe5NGvnc0wywckuXAlp9akASaYSFqKuI5O8p3rSiA+yKP31qC7Iijjoygmzkh6xw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/spellchecker/-/spellchecker-3.5.0.tgz", + "integrity": "sha512-Xa7XnRulYhh5N/XENeL2O8/875XhLBjos7Bemv0rfcgV6ojNYMSrXscUZUGJwniX2t67eY+lNUJeptD1bMauHQ==", "requires": { "any-promise": "^1.3.0", "nan": "^2.10.0" @@ -5370,38 +5302,24 @@ } }, "streamroller": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.1.tgz", - "integrity": "sha512-FKL2mEB0A+XTIWSOlBHm2DvdsER5cGraqrUufO0lFMfsVY+Gpb3TC29Z+6/l0Urbb7vtm6m9zJOQBVl6fEkZBA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", + "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=", "requires": { - "async": "^2.6.1", - "date-format": "^2.0.0", + "date-format": "^1.2.0", "debug": "^3.1.0", - "fs-extra": "^7.0.0", - "lodash": "^4.17.10" + "mkdirp": "^0.5.1", + "readable-stream": "^2.3.0" }, "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "requires": { - "lodash": "^4.17.10" - } - }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "requires": { "ms": "^2.1.1" } }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -5473,9 +5391,9 @@ } }, "superstring": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/superstring/-/superstring-2.3.6.tgz", - "integrity": "sha512-kDTXCXArhHL1lRk2zBW7ByRJByqVwoLK3E3jlf8+LcwQLZgSMs9dwrDHDpBdoOm89kstSBSrGcW8OJqNkxjWrQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/superstring/-/superstring-2.3.4.tgz", + "integrity": "sha512-DcNkTCdB9F3FMZRdURSALsHi+7DWqFCI0cH+Eg8mwBg+kxQs6GeB3LrGUvCI5bEB6Dtlu2ox8UYN0onPN4JeZQ==", "requires": { "nan": "^2.10.0" } @@ -5518,20 +5436,6 @@ "underscore-plus": "1.x" } }, - "tar": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz", - "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==", - "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.3", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, "tar-fs": { "version": "1.16.3", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", @@ -5609,21 +5513,21 @@ } }, "text-buffer": { - "version": "13.15.3", - "resolved": "https://registry.npmjs.org/text-buffer/-/text-buffer-13.15.3.tgz", - "integrity": "sha512-H2fz/N15g0fBP7R33FUFLnIyND+Lji/xmuvHg9rKgmfCh7NAVxiFIvnZTabuBhL9InqPrtV5t4hkUy+r3dNXMg==", + "version": "13.15.1", + "resolved": "https://registry.npmjs.org/text-buffer/-/text-buffer-13.15.1.tgz", + "integrity": "sha512-LZx7EKhVwjo50Y9ZF6WDP2S4zLUcHAagHZhn6HzHjr0SxyWtm95HG7ApSzgLGvIzCZEoTzMYt62scUdesfzYuw==", "requires": { "delegato": "^1.0.0", "diff": "^2.2.1", "emissary": "^1.0.0", "event-kit": "^2.4.0", - "fs-admin": "^0.1.7", + "fs-admin": "^0.1.4", "fs-plus": "^3.0.0", "grim": "^2.0.2", "mkdirp": "^0.5.1", - "pathwatcher": "8.0.2", + "pathwatcher": "8.0.1", "serializable": "^1.0.3", - "superstring": "2.3.6", + "superstring": "2.3.4", "underscore-plus": "^1.0.0" }, "dependencies": { @@ -6066,8 +5970,8 @@ "integrity": "sha1-BNoCcKh6d4VAFzzb8KLbSZqNnik=" }, "welcome": { - "version": "https://www.atom.io/api/packages/welcome/versions/0.36.7/tarball", - "integrity": "sha512-z1EOTRYfN23fBL75Shrbe/j2VDelw2c8oKRXC2MqLLBiWUCFDkxsEo1R7OfiCaNZi7q/0ue0fqLCpENHker4FA==", + "version": "https://www.atom.io/api/packages/welcome/versions/0.36.8/tarball", + "integrity": "sha512-U2EFFqgGxqQ5eMFFP3bl5oBSSvb0kx4VZwQVyO8oXqjuveiV2gUSrnDP68C9yCqJxcMR9ZP4ZYTLsf255F7CsA==", "requires": { "etch": "0.9.0" }, diff --git a/package.json b/package.json index 6a84858c7..d19425400 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.36.0-dev", + "version": "1.37.0-dev", "description": "A hackable text editor for the 21st Century.", "main": "./src/main-process/main.js", "repository": { @@ -12,17 +12,17 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "3.0.14", + "electronVersion": "2.0.16", "dependencies": { - "@atom/nsfw": "1.0.21", + "@atom/nsfw": "1.0.18", "@atom/source-map-support": "^0.3.4", - "@atom/watcher": "1.0.8", + "@atom/watcher": "1.3.1", "about": "file:packages/about", "archive-view": "https://www.atom.io/api/packages/archive-view/versions/0.65.1/tarball", "async": "0.2.6", "atom-dark-syntax": "file:packages/atom-dark-syntax", "atom-dark-ui": "file:packages/atom-dark-ui", - "atom-keymap": "8.2.12", + "atom-keymap": "8.2.13", "atom-light-syntax": "file:packages/atom-light-syntax", "atom-light-ui": "file:packages/atom-light-ui", "atom-select-list": "^0.7.2", @@ -39,6 +39,7 @@ "base16-tomorrow-light-theme": "file:packages/base16-tomorrow-light-theme", "bookmarks": "https://www.atom.io/api/packages/bookmarks/versions/0.46.0/tarball", "bracket-matcher": "https://www.atom.io/api/packages/bracket-matcher/versions/0.90.4/tarball", + "cached-run-in-this-context": "0.5.0", "chai": "3.5.0", "chart.js": "^2.3.0", "clear-cut": "^2.0.2", @@ -54,15 +55,15 @@ "etch": "^0.12.6", "event-kit": "^2.5.3", "exception-reporting": "file:packages/exception-reporting", - "find-and-replace": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.0/tarball", + "find-and-replace": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.9/tarball", "find-parent-dir": "^0.3.0", "first-mate": "7.1.3", "focus-trap": "2.4.5", "fs-admin": "^0.1.7", - "fs-plus": "^3.1.1", + "fs-plus": "^3.0.1", "fstream": "0.1.24", "fuzzaldrin": "^2.1", - "fuzzy-finder": "https://www.atom.io/api/packages/fuzzy-finder/versions/1.9.0/tarball", + "fuzzy-finder": "https://www.atom.io/api/packages/fuzzy-finder/versions/1.9.2/tarball", "git-diff": "file:packages/git-diff", "git-utils": "5.2.1", "github": "https://www.atom.io/api/packages/github/versions/0.26.0/tarball", @@ -88,7 +89,7 @@ "language-html": "https://www.atom.io/api/packages/language-html/versions/0.52.0/tarball", "language-hyperlink": "https://www.atom.io/api/packages/language-hyperlink/versions/0.17.0/tarball", "language-java": "https://www.atom.io/api/packages/language-java/versions/0.31.1/tarball", - "language-javascript": "https://www.atom.io/api/packages/language-javascript/versions/0.129.19/tarball", + "language-javascript": "https://www.atom.io/api/packages/language-javascript/versions/0.129.20/tarball", "language-json": "https://www.atom.io/api/packages/language-json/versions/0.19.2/tarball", "language-less": "https://www.atom.io/api/packages/language-less/versions/0.34.3/tarball", "language-make": "https://www.atom.io/api/packages/language-make/versions/0.23.0/tarball", @@ -132,7 +133,7 @@ "one-light-ui": "file:packages/one-light-ui", "open-on-github": "https://www.atom.io/api/packages/open-on-github/versions/1.3.1/tarball", "package-generator": "https://www.atom.io/api/packages/package-generator/versions/1.3.0/tarball", - "pathwatcher": "8.0.2", + "pathwatcher": "8.0.1", "postcss": "5.2.4", "postcss-selector-parser": "2.2.1", "property-accessors": "^1.1.3", @@ -144,18 +145,18 @@ "season": "^6.0.2", "semver": "^4.3.3", "service-hub": "^0.7.4", - "settings-view": "https://www.atom.io/api/packages/settings-view/versions/0.259.0/tarball", + "settings-view": "https://www.atom.io/api/packages/settings-view/versions/0.260.0/tarball", "sinon": "1.17.4", - "snippets": "https://www.atom.io/api/packages/snippets/versions/1.4.0/tarball", + "snippets": "https://www.atom.io/api/packages/snippets/versions/1.4.1/tarball", "solarized-dark-syntax": "file:packages/solarized-dark-syntax", "solarized-light-syntax": "file:packages/solarized-light-syntax", - "spell-check": "https://www.atom.io/api/packages/spell-check/versions/0.74.3/tarball", + "spell-check": "https://www.atom.io/api/packages/spell-check/versions/0.74.2/tarball", "status-bar": "https://www.atom.io/api/packages/status-bar/versions/1.8.17/tarball", "styleguide": "https://www.atom.io/api/packages/styleguide/versions/0.49.12/tarball", "symbols-view": "https://www.atom.io/api/packages/symbols-view/versions/0.118.2/tarball", "tabs": "https://www.atom.io/api/packages/tabs/versions/0.110.0/tarball", "temp": "^0.8.3", - "text-buffer": "13.15.3", + "text-buffer": "13.15.1", "timecop": "https://www.atom.io/api/packages/timecop/versions/0.36.2/tarball", "tree-sitter": "0.13.23", "tree-sitter-css": "^0.13.7", @@ -163,7 +164,7 @@ "typescript-simple": "1.0.0", "underscore-plus": "^1.6.8", "update-package-dependencies": "https://www.atom.io/api/packages/update-package-dependencies/versions/0.13.1/tarball", - "welcome": "https://www.atom.io/api/packages/welcome/versions/0.36.7/tarball", + "welcome": "https://www.atom.io/api/packages/welcome/versions/0.36.8/tarball", "whitespace": "https://www.atom.io/api/packages/whitespace/versions/0.37.7/tarball", "winreg": "^1.2.1", "wrap-guide": "https://www.atom.io/api/packages/wrap-guide/versions/0.41.0/tarball", @@ -200,8 +201,8 @@ "dev-live-reload": "file:./packages/dev-live-reload", "encoding-selector": "0.23.9", "exception-reporting": "file:./packages/exception-reporting", - "find-and-replace": "0.218.0", - "fuzzy-finder": "1.9.0", + "find-and-replace": "0.218.9", + "fuzzy-finder": "1.9.2", "github": "0.26.0", "git-diff": "file:./packages/git-diff", "go-to-line": "file:./packages/go-to-line", @@ -216,9 +217,9 @@ "notifications": "0.70.6", "open-on-github": "1.3.1", "package-generator": "1.3.0", - "settings-view": "0.259.0", - "snippets": "1.4.0", - "spell-check": "0.74.3", + "settings-view": "0.260.0", + "snippets": "1.4.1", + "spell-check": "0.74.2", "status-bar": "1.8.17", "styleguide": "0.49.12", "symbols-view": "0.118.2", @@ -226,7 +227,7 @@ "timecop": "0.36.2", "tree-view": "0.224.5", "update-package-dependencies": "0.13.1", - "welcome": "0.36.7", + "welcome": "0.36.8", "whitespace": "0.37.7", "wrap-guide": "0.41.0", "language-c": "0.60.14", @@ -240,7 +241,7 @@ "language-html": "0.52.0", "language-hyperlink": "0.17.0", "language-java": "0.31.1", - "language-javascript": "0.129.19", + "language-javascript": "0.129.20", "language-json": "0.19.2", "language-less": "0.34.3", "language-make": "0.23.0", diff --git a/packages/about/lib/about.js b/packages/about/lib/about.js index 1221bda6d..1fe660e9f 100644 --- a/packages/about/lib/about.js +++ b/packages/about/lib/about.js @@ -1,4 +1,4 @@ -const {CompositeDisposable, Emitter} = require('atom') +const { CompositeDisposable, Emitter } = require('atom') const AboutView = require('./components/about-view') // Deferred requires @@ -14,16 +14,22 @@ module.exports = class About { aboutView: null } - this.subscriptions.add(atom.workspace.addOpener((uriToOpen) => { - if (uriToOpen === this.state.uri) { - return this.deserialize() - } - })) + this.subscriptions.add( + atom.workspace.addOpener(uriToOpen => { + if (uriToOpen === this.state.uri) { + return this.deserialize() + } + }) + ) - this.subscriptions.add(atom.commands.add('atom-workspace', 'about:view-release-notes', () => { - shell = shell || require('electron').shell - shell.openExternal(this.state.updateManager.getReleaseNotesURLForCurrentVersion()) - })) + this.subscriptions.add( + atom.commands.add('atom-workspace', 'about:view-release-notes', () => { + shell = shell || require('electron').shell + shell.openExternal( + this.state.updateManager.getReleaseNotesURLForCurrentVersion() + ) + }) + ) } destroy () { @@ -31,14 +37,14 @@ module.exports = class About { this.views.aboutView = null if (this.state.updateManager) this.state.updateManager.dispose() - this.setState({updateManager: null}) + this.setState({ updateManager: null }) this.subscriptions.dispose() } setState (newState) { if (newState && typeof newState === 'object') { - let {state} = this + let { state } = this this.state = Object.assign({}, state, newState) this.didChange() diff --git a/packages/about/lib/components/about-status-bar.js b/packages/about/lib/components/about-status-bar.js index d55800f5d..4529b6ae8 100644 --- a/packages/about/lib/components/about-status-bar.js +++ b/packages/about/lib/components/about-status-bar.js @@ -1,16 +1,20 @@ -const {CompositeDisposable} = require('atom') +const { CompositeDisposable } = require('atom') const etch = require('etch') const EtchComponent = require('../etch-component') const $ = etch.dom -module.exports = -class AboutStatusBar extends EtchComponent { +module.exports = class AboutStatusBar extends EtchComponent { constructor () { super() this.subscriptions = new CompositeDisposable() - this.subscriptions.add(atom.tooltips.add(this.element, {title: 'An update will be installed the next time Atom is relaunched.

Click the squirrel icon for more information.'})) + this.subscriptions.add( + atom.tooltips.add(this.element, { + title: + 'An update will be installed the next time Atom is relaunched.

Click the squirrel icon for more information.' + }) + ) } handleClick () { @@ -18,8 +22,12 @@ class AboutStatusBar extends EtchComponent { } render () { - return $.div({className: 'about-release-notes inline-block', onclick: this.handleClick.bind(this)}, - $.span({type: 'button', className: 'icon icon-squirrel'}) + return $.div( + { + className: 'about-release-notes inline-block', + onclick: this.handleClick.bind(this) + }, + $.span({ type: 'button', className: 'icon icon-squirrel' }) ) } diff --git a/packages/about/lib/components/about-view.js b/packages/about/lib/components/about-view.js index 3e370b171..34fb7cba9 100644 --- a/packages/about/lib/components/about-view.js +++ b/packages/about/lib/components/about-view.js @@ -1,4 +1,4 @@ -const {Disposable} = require('atom') +const { Disposable } = require('atom') const etch = require('etch') const shell = require('shell') const AtomLogo = require('./atom-logo') @@ -7,8 +7,7 @@ const UpdateView = require('./update-view') const $ = etch.dom -module.exports = -class AboutView extends EtchComponent { +module.exports = class AboutView extends EtchComponent { handleAtomVersionClick (e) { e.preventDefault() atom.clipboard.write(this.props.currentAtomVersion) @@ -31,12 +30,17 @@ class AboutView extends EtchComponent { handleReleaseNotesClick (e) { e.preventDefault() - shell.openExternal(this.props.updateManager.getReleaseNotesURLForAvailableVersion()) + shell.openExternal( + this.props.updateManager.getReleaseNotesURLForAvailableVersion() + ) } handleLicenseClick (e) { e.preventDefault() - atom.commands.dispatch(atom.views.getView(atom.workspace), 'application:open-license') + atom.commands.dispatch( + atom.views.getView(atom.workspace), + 'application:open-license' + ) } handleTermsOfUseClick (e) { @@ -46,7 +50,9 @@ class AboutView extends EtchComponent { handleHowToUpdateClick (e) { e.preventDefault() - shell.openExternal('https://flight-manual.atom.io/getting-started/sections/installing-atom/') + shell.openExternal( + 'https://flight-manual.atom.io/getting-started/sections/installing-atom/' + ) } handleShowMoreClick (e) { @@ -66,39 +72,87 @@ class AboutView extends EtchComponent { } render () { - return $.div({className: 'pane-item native-key-bindings about'}, - $.div({className: 'about-container'}, - $.header({className: 'about-header'}, - $.a({className: 'about-atom-io', href: 'https://atom.io'}, + return $.div( + { className: 'pane-item native-key-bindings about' }, + $.div( + { className: 'about-container' }, + $.header( + { className: 'about-header' }, + $.a( + { className: 'about-atom-io', href: 'https://atom.io' }, $(AtomLogo) ), - $.div({className: 'about-header-info'}, - $.span({className: 'about-version-container inline-block atom', onclick: this.handleAtomVersionClick.bind(this)}, - $.span({className: 'about-version'}, `${this.props.currentAtomVersion} ${process.arch}`), - $.span({className: 'icon icon-clippy about-copy-version'}) + $.div( + { className: 'about-header-info' }, + $.span( + { + className: 'about-version-container inline-block atom', + onclick: this.handleAtomVersionClick.bind(this) + }, + $.span( + { className: 'about-version' }, + `${this.props.currentAtomVersion} ${process.arch}` + ), + $.span({ className: 'icon icon-clippy about-copy-version' }) ), - $.a({className: 'about-header-release-notes', onclick: this.handleReleaseNotesClick.bind(this)}, 'Release Notes') + $.a( + { + className: 'about-header-release-notes', + onclick: this.handleReleaseNotesClick.bind(this) + }, + 'Release Notes' + ) ), - $.span({className: 'about-version-container inline-block show-more-expand', onclick: this.handleShowMoreClick.bind(this)}, - $.span({className: 'about-more-expand'}, 'Show more') + $.span( + { + className: + 'about-version-container inline-block show-more-expand', + onclick: this.handleShowMoreClick.bind(this) + }, + $.span({ className: 'about-more-expand' }, 'Show more') ), - $.div({className: 'show-more hidden about-more-info'}, - $.div({className: 'about-more-info'}, - $.span({className: 'about-version-container inline-block electron', onclick: this.handleElectronVersionClick.bind(this)}, - $.span({className: 'about-more-version'}, `Electron: ${this.props.currentElectronVersion} `), - $.span({className: 'icon icon-clippy about-copy-version'}) + $.div( + { className: 'show-more hidden about-more-info' }, + $.div( + { className: 'about-more-info' }, + $.span( + { + className: 'about-version-container inline-block electron', + onclick: this.handleElectronVersionClick.bind(this) + }, + $.span( + { className: 'about-more-version' }, + `Electron: ${this.props.currentElectronVersion} ` + ), + $.span({ className: 'icon icon-clippy about-copy-version' }) ) ), - $.div({className: 'about-more-info'}, - $.span({className: 'about-version-container inline-block chrome', onclick: this.handleChromeVersionClick.bind(this)}, - $.span({className: 'about-more-version'}, `Chrome: ${this.props.currentChromeVersion} `), - $.span({className: 'icon icon-clippy about-copy-version'}) + $.div( + { className: 'about-more-info' }, + $.span( + { + className: 'about-version-container inline-block chrome', + onclick: this.handleChromeVersionClick.bind(this) + }, + $.span( + { className: 'about-more-version' }, + `Chrome: ${this.props.currentChromeVersion} ` + ), + $.span({ className: 'icon icon-clippy about-copy-version' }) ) ), - $.div({className: 'about-more-info'}, - $.span({className: 'about-version-container inline-block node', onclick: this.handleNodeVersionClick.bind(this)}, - $.span({className: 'about-more-version'}, `Node: ${this.props.currentNodeVersion} `), - $.span({className: 'icon icon-clippy about-copy-version'}) + $.div( + { className: 'about-more-info' }, + $.span( + { + className: 'about-version-container inline-block node', + onclick: this.handleNodeVersionClick.bind(this) + }, + $.span( + { className: 'about-more-version' }, + `Node: ${this.props.currentNodeVersion} ` + ), + $.span({ className: 'icon icon-clippy about-copy-version' }) ) ) ) @@ -112,24 +166,43 @@ class AboutView extends EtchComponent { viewUpdateInstructions: this.handleHowToUpdateClick.bind(this) }), - $.div({className: 'about-actions group-item'}, - $.div({className: 'btn-group'}, - $.button({className: 'btn view-license', onclick: this.handleLicenseClick.bind(this)}, 'License'), - $.button({className: 'btn terms-of-use', onclick: this.handleTermsOfUseClick.bind(this)}, 'Terms of Use') + $.div( + { className: 'about-actions group-item' }, + $.div( + { className: 'btn-group' }, + $.button( + { + className: 'btn view-license', + onclick: this.handleLicenseClick.bind(this) + }, + 'License' + ), + $.button( + { + className: 'btn terms-of-use', + onclick: this.handleTermsOfUseClick.bind(this) + }, + 'Terms of Use' + ) ) ), - $.div({className: 'about-love group-start'}, - $.span({className: 'icon icon-code'}), - $.span({className: 'inline'}, ' with '), - $.span({className: 'icon icon-heart'}), - $.span({className: 'inline'}, ' by '), - $.a({className: 'icon icon-logo-github', href: 'https://github.com'}) + $.div( + { className: 'about-love group-start' }, + $.span({ className: 'icon icon-code' }), + $.span({ className: 'inline' }, ' with '), + $.span({ className: 'icon icon-heart' }), + $.span({ className: 'inline' }, ' by '), + $.a({ className: 'icon icon-logo-github', href: 'https://github.com' }) ), - $.div({className: 'about-credits group-item'}, - $.span({className: 'inline'}, 'And the awesome '), - $.a({href: 'https://github.com/atom/atom/contributors'}, 'Atom Community') + $.div( + { className: 'about-credits group-item' }, + $.span({ className: 'inline' }, 'And the awesome '), + $.a( + { href: 'https://github.com/atom/atom/contributors' }, + 'Atom Community' + ) ) ) } diff --git a/packages/about/lib/components/atom-logo.js b/packages/about/lib/components/atom-logo.js index f8b620ce1..bd50fc7a2 100644 --- a/packages/about/lib/components/atom-logo.js +++ b/packages/about/lib/components/atom-logo.js @@ -3,23 +3,74 @@ const EtchComponent = require('../etch-component') const $ = etch.dom -module.exports = -class AtomLogo extends EtchComponent { +module.exports = class AtomLogo extends EtchComponent { render () { - return $.svg({className: 'about-logo', width: '330px', height: '68px', viewBox: '0 0 330 68'}, - $.g({stroke: 'none', 'stroke-width': '1', fill: 'none', 'fill-rule': 'evenodd'}, - $.g({transform: 'translate(2.000000, 1.000000)'}, - $.g({transform: 'translate(96.000000, 8.000000)', fill: 'currentColor'}, - $.path({d: 'M185.498,3.399 C185.498,2.417 186.34,1.573 187.324,1.573 L187.674,1.573 C188.447,1.573 189.01,1.995 189.5,2.628 L208.676,30.862 L227.852,2.628 C228.272,1.995 228.905,1.573 229.676,1.573 L230.028,1.573 C231.01,1.573 231.854,2.417 231.854,3.399 L231.854,49.403 C231.854,50.387 231.01,51.231 230.028,51.231 C229.044,51.231 228.202,50.387 228.202,49.403 L228.202,8.246 L210.151,34.515 C209.729,35.148 209.237,35.428 208.606,35.428 C207.973,35.428 207.481,35.148 207.061,34.515 L189.01,8.246 L189.01,49.475 C189.01,50.457 188.237,51.231 187.254,51.231 C186.27,51.231 185.498,50.458 185.498,49.475 L185.498,3.399 L185.498,3.399 Z'}), - $.path({d: 'M113.086,26.507 L113.086,26.367 C113.086,12.952 122.99,0.941 137.881,0.941 C152.77,0.941 162.533,12.811 162.533,26.225 L162.533,26.367 C162.533,39.782 152.629,51.792 137.74,51.792 C122.85,51.792 113.086,39.923 113.086,26.507 M158.74,26.507 L158.74,26.367 C158.74,14.216 149.89,4.242 137.74,4.242 C125.588,4.242 116.879,14.075 116.879,26.225 L116.879,26.367 C116.879,38.518 125.729,48.491 137.881,48.491 C150.031,48.491 158.74,38.658 158.74,26.507'}), - $.path({d: 'M76.705,5.155 L60.972,5.155 C60.06,5.155 59.287,4.384 59.287,3.469 C59.287,2.556 60.059,1.783 60.972,1.783 L96.092,1.783 C97.004,1.783 97.778,2.555 97.778,3.469 C97.778,4.383 97.005,5.155 96.092,5.155 L80.358,5.155 L80.358,49.405 C80.358,50.387 79.516,51.231 78.532,51.231 C77.55,51.231 76.706,50.387 76.706,49.405 L76.706,5.155 L76.705,5.155 Z'}), - $.path({d: 'M0.291,48.562 L21.291,3.05 C21.783,1.995 22.485,1.292 23.75,1.292 L23.891,1.292 C25.155,1.292 25.858,1.995 26.348,3.05 L47.279,48.421 C47.49,48.843 47.56,49.194 47.56,49.546 C47.56,50.458 46.788,51.231 45.803,51.231 C44.961,51.231 44.329,50.599 43.978,49.826 L38.219,37.183 L9.21,37.183 L3.45,49.897 C3.099,50.739 2.538,51.231 1.694,51.231 C0.781,51.231 0.008,50.529 0.008,49.685 C0.009,49.404 0.08,48.983 0.291,48.562 L0.291,48.562 Z M36.673,33.882 L23.749,5.437 L10.755,33.882 L36.673,33.882 L36.673,33.882 Z'}) + return $.svg( + { + className: 'about-logo', + width: '330px', + height: '68px', + viewBox: '0 0 330 68' + }, + $.g( + { + stroke: 'none', + 'stroke-width': '1', + fill: 'none', + 'fill-rule': 'evenodd' + }, + $.g( + { transform: 'translate(2.000000, 1.000000)' }, + $.g( + { + transform: 'translate(96.000000, 8.000000)', + fill: 'currentColor' + }, + $.path({ + d: + 'M185.498,3.399 C185.498,2.417 186.34,1.573 187.324,1.573 L187.674,1.573 C188.447,1.573 189.01,1.995 189.5,2.628 L208.676,30.862 L227.852,2.628 C228.272,1.995 228.905,1.573 229.676,1.573 L230.028,1.573 C231.01,1.573 231.854,2.417 231.854,3.399 L231.854,49.403 C231.854,50.387 231.01,51.231 230.028,51.231 C229.044,51.231 228.202,50.387 228.202,49.403 L228.202,8.246 L210.151,34.515 C209.729,35.148 209.237,35.428 208.606,35.428 C207.973,35.428 207.481,35.148 207.061,34.515 L189.01,8.246 L189.01,49.475 C189.01,50.457 188.237,51.231 187.254,51.231 C186.27,51.231 185.498,50.458 185.498,49.475 L185.498,3.399 L185.498,3.399 Z' + }), + $.path({ + d: + 'M113.086,26.507 L113.086,26.367 C113.086,12.952 122.99,0.941 137.881,0.941 C152.77,0.941 162.533,12.811 162.533,26.225 L162.533,26.367 C162.533,39.782 152.629,51.792 137.74,51.792 C122.85,51.792 113.086,39.923 113.086,26.507 M158.74,26.507 L158.74,26.367 C158.74,14.216 149.89,4.242 137.74,4.242 C125.588,4.242 116.879,14.075 116.879,26.225 L116.879,26.367 C116.879,38.518 125.729,48.491 137.881,48.491 C150.031,48.491 158.74,38.658 158.74,26.507' + }), + $.path({ + d: + 'M76.705,5.155 L60.972,5.155 C60.06,5.155 59.287,4.384 59.287,3.469 C59.287,2.556 60.059,1.783 60.972,1.783 L96.092,1.783 C97.004,1.783 97.778,2.555 97.778,3.469 C97.778,4.383 97.005,5.155 96.092,5.155 L80.358,5.155 L80.358,49.405 C80.358,50.387 79.516,51.231 78.532,51.231 C77.55,51.231 76.706,50.387 76.706,49.405 L76.706,5.155 L76.705,5.155 Z' + }), + $.path({ + d: + 'M0.291,48.562 L21.291,3.05 C21.783,1.995 22.485,1.292 23.75,1.292 L23.891,1.292 C25.155,1.292 25.858,1.995 26.348,3.05 L47.279,48.421 C47.49,48.843 47.56,49.194 47.56,49.546 C47.56,50.458 46.788,51.231 45.803,51.231 C44.961,51.231 44.329,50.599 43.978,49.826 L38.219,37.183 L9.21,37.183 L3.45,49.897 C3.099,50.739 2.538,51.231 1.694,51.231 C0.781,51.231 0.008,50.529 0.008,49.685 C0.009,49.404 0.08,48.983 0.291,48.562 L0.291,48.562 Z M36.673,33.882 L23.749,5.437 L10.755,33.882 L36.673,33.882 L36.673,33.882 Z' + }) ), - $.g({}, - $.path({d: 'M40.363,32.075 C40.874,34.44 39.371,36.77 37.006,37.282 C34.641,37.793 32.311,36.29 31.799,33.925 C31.289,31.56 32.791,29.23 35.156,28.718 C37.521,28.207 39.851,29.71 40.363,32.075', fill: 'currentColor'}), - $.path({d: 'M48.578,28.615 C56.851,45.587 58.558,61.581 52.288,64.778 C45.822,68.076 33.326,56.521 24.375,38.969 C15.424,21.418 13.409,4.518 19.874,1.221 C22.689,-0.216 26.648,1.166 30.959,4.629', stroke: 'currentColor', 'stroke-width': '3.08', 'stroke-linecap': 'round'}), - $.path({d: 'M7.64,39.45 C2.806,36.94 -0.009,33.915 0.154,30.79 C0.531,23.542 16.787,18.497 36.462,19.52 C56.137,20.544 71.781,27.249 71.404,34.497 C71.241,37.622 68.127,40.338 63.06,42.333', stroke: 'currentColor', 'stroke-width': '3.08', 'stroke-linecap': 'round'}), - $.path({d: 'M28.828,59.354 C23.545,63.168 18.843,64.561 15.902,62.653 C9.814,58.702 13.572,42.102 24.296,25.575 C35.02,9.048 48.649,-1.149 54.736,2.803 C57.566,4.639 58.269,9.208 57.133,15.232', stroke: 'currentColor', 'stroke-width': '3.08', 'stroke-linecap': 'round'}) + $.g( + {}, + $.path({ + d: + 'M40.363,32.075 C40.874,34.44 39.371,36.77 37.006,37.282 C34.641,37.793 32.311,36.29 31.799,33.925 C31.289,31.56 32.791,29.23 35.156,28.718 C37.521,28.207 39.851,29.71 40.363,32.075', + fill: 'currentColor' + }), + $.path({ + d: + 'M48.578,28.615 C56.851,45.587 58.558,61.581 52.288,64.778 C45.822,68.076 33.326,56.521 24.375,38.969 C15.424,21.418 13.409,4.518 19.874,1.221 C22.689,-0.216 26.648,1.166 30.959,4.629', + stroke: 'currentColor', + 'stroke-width': '3.08', + 'stroke-linecap': 'round' + }), + $.path({ + d: + 'M7.64,39.45 C2.806,36.94 -0.009,33.915 0.154,30.79 C0.531,23.542 16.787,18.497 36.462,19.52 C56.137,20.544 71.781,27.249 71.404,34.497 C71.241,37.622 68.127,40.338 63.06,42.333', + stroke: 'currentColor', + 'stroke-width': '3.08', + 'stroke-linecap': 'round' + }), + $.path({ + d: + 'M28.828,59.354 C23.545,63.168 18.843,64.561 15.902,62.653 C9.814,58.702 13.572,42.102 24.296,25.575 C35.02,9.048 48.649,-1.149 54.736,2.803 C57.566,4.639 58.269,9.208 57.133,15.232', + stroke: 'currentColor', + 'stroke-width': '3.08', + 'stroke-linecap': 'round' + }) ) ) ) diff --git a/packages/about/lib/components/update-view.js b/packages/about/lib/components/update-view.js index a4e97e74d..4399b58b4 100644 --- a/packages/about/lib/components/update-view.js +++ b/packages/about/lib/components/update-view.js @@ -4,12 +4,14 @@ const UpdateManager = require('../update-manager') const $ = etch.dom -module.exports = -class UpdateView extends EtchComponent { +module.exports = class UpdateView extends EtchComponent { constructor (props) { super(props) - if (this.props.updateManager.getAutoUpdatesEnabled() && this.props.updateManager.getState() === UpdateManager.State.Idle) { + if ( + this.props.updateManager.getAutoUpdatesEnabled() && + this.props.updateManager.getState() === UpdateManager.State.Idle + ) { this.props.updateManager.checkForUpdate() } } @@ -19,12 +21,18 @@ class UpdateView extends EtchComponent { } shouldUpdateActionButtonBeDisabled () { - let {state} = this.props.updateManager - return state === UpdateManager.State.CheckingForUpdate || state === UpdateManager.State.DownloadingUpdate + let { state } = this.props.updateManager + return ( + state === UpdateManager.State.CheckingForUpdate || + state === UpdateManager.State.DownloadingUpdate + ) } executeUpdateAction () { - if (this.props.updateManager.state === UpdateManager.State.UpdateAvailableToInstall) { + if ( + this.props.updateManager.state === + UpdateManager.State.UpdateAvailableToInstall + ) { this.props.updateManager.restartAndInstallUpdate() } else { this.props.updateManager.checkForUpdate() @@ -36,44 +44,86 @@ class UpdateView extends EtchComponent { switch (this.props.updateManager.state) { case UpdateManager.State.Idle: - updateStatus = $.div({className: 'about-updates-item is-shown about-default-update-message'}, - this.props.updateManager.getAutoUpdatesEnabled() ? 'Atom will check for updates automatically' : 'Automatic updates are disabled please check manually' + updateStatus = $.div( + { + className: + 'about-updates-item is-shown about-default-update-message' + }, + this.props.updateManager.getAutoUpdatesEnabled() + ? 'Atom will check for updates automatically' + : 'Automatic updates are disabled please check manually' ) break case UpdateManager.State.CheckingForUpdate: - updateStatus = $.div({className: 'about-updates-item app-checking-for-updates'}, - $.span({className: 'about-updates-label icon icon-search'}, 'Checking for updates...') + updateStatus = $.div( + { className: 'about-updates-item app-checking-for-updates' }, + $.span( + { className: 'about-updates-label icon icon-search' }, + 'Checking for updates...' + ) ) break case UpdateManager.State.DownloadingUpdate: - updateStatus = $.div({className: 'about-updates-item app-downloading-update'}, - $.span({className: 'loading loading-spinner-tiny inline-block'}), - $.span({className: 'about-updates-label'}, 'Downloading update') + updateStatus = $.div( + { className: 'about-updates-item app-downloading-update' }, + $.span({ className: 'loading loading-spinner-tiny inline-block' }), + $.span({ className: 'about-updates-label' }, 'Downloading update') ) break case UpdateManager.State.UpdateAvailableToInstall: - updateStatus = $.div({className: 'about-updates-item app-update-available-to-install'}, - $.span({className: 'about-updates-label icon icon-squirrel'}, 'New update'), - $.span({className: 'about-updates-version'}, this.props.availableVersion), - $.a({className: 'about-updates-release-notes', onclick: this.props.viewUpdateReleaseNotes}, 'Release Notes') + updateStatus = $.div( + { className: 'about-updates-item app-update-available-to-install' }, + $.span( + { className: 'about-updates-label icon icon-squirrel' }, + 'New update' + ), + $.span( + { className: 'about-updates-version' }, + this.props.availableVersion + ), + $.a( + { + className: 'about-updates-release-notes', + onclick: this.props.viewUpdateReleaseNotes + }, + 'Release Notes' + ) ) break case UpdateManager.State.UpToDate: - updateStatus = $.div({className: 'about-updates-item app-up-to-date'}, - $.span({className: 'icon icon-check'}), - $.span({className: 'about-updates-label is-strong'}, 'Atom is up to date!') + updateStatus = $.div( + { className: 'about-updates-item app-up-to-date' }, + $.span({ className: 'icon icon-check' }), + $.span( + { className: 'about-updates-label is-strong' }, + 'Atom is up to date!' + ) ) break case UpdateManager.State.Unsupported: - updateStatus = $.div({className: 'about-updates-item app-unsupported'}, - $.span({className: 'about-updates-label is-strong'}, 'Your system does not support automatic updates'), - $.a({className: 'about-updates-instructions', onclick: this.props.viewUpdateInstructions}, 'How to update') + updateStatus = $.div( + { className: 'about-updates-item app-unsupported' }, + $.span( + { className: 'about-updates-label is-strong' }, + 'Your system does not support automatic updates' + ), + $.a( + { + className: 'about-updates-instructions', + onclick: this.props.viewUpdateInstructions + }, + 'How to update' + ) ) break case UpdateManager.State.Error: - updateStatus = $.div({className: 'about-updates-item app-update-error'}, - $.span({className: 'icon icon-x'}), - $.span({className: 'about-updates-label app-error-message is-strong'}, this.props.updateManager.getErrorMessage()) + updateStatus = $.div( + { className: 'about-updates-item app-update-error' }, + $.span({ className: 'icon icon-x' }), + $.span( + { className: 'about-updates-label app-error-message is-strong' }, + this.props.updateManager.getErrorMessage() + ) ) break } @@ -82,37 +132,47 @@ class UpdateView extends EtchComponent { } render () { - return $.div({className: 'about-updates group-start'}, - $.div({className: 'about-updates-box'}, - $.div({className: 'about-updates-status'}, this.renderUpdateStatus()), + return $.div( + { className: 'about-updates group-start' }, + $.div( + { className: 'about-updates-box' }, + $.div({ className: 'about-updates-status' }, this.renderUpdateStatus()), $.button( { className: 'btn about-update-action-button', disabled: this.shouldUpdateActionButtonBeDisabled(), onclick: this.executeUpdateAction.bind(this), style: { - display: this.props.updateManager.state === UpdateManager.State.Unsupported ? 'none' : 'block' + display: + this.props.updateManager.state === + UpdateManager.State.Unsupported + ? 'none' + : 'block' } }, - this.props.updateManager.state === 'update-available' ? 'Restart and install' : 'Check now' + this.props.updateManager.state === 'update-available' + ? 'Restart and install' + : 'Check now' ) ), $.div( { className: 'about-auto-updates', style: { - display: this.props.updateManager.state === UpdateManager.State.Unsupported ? 'none' : 'block' + display: + this.props.updateManager.state === UpdateManager.State.Unsupported + ? 'none' + : 'block' } }, - $.label({}, - $.input( - { - className: 'input-checkbox', - type: 'checkbox', - checked: this.props.updateManager.getAutoUpdatesEnabled(), - onchange: this.handleAutoUpdateCheckbox.bind(this) - } - ), + $.label( + {}, + $.input({ + className: 'input-checkbox', + type: 'checkbox', + checked: this.props.updateManager.getAutoUpdatesEnabled(), + onchange: this.handleAutoUpdateCheckbox.bind(this) + }), $.span({}, 'Automatically download updates') ) ) diff --git a/packages/about/lib/etch-component.js b/packages/about/lib/etch-component.js index f75edce94..71ea85c88 100644 --- a/packages/about/lib/etch-component.js +++ b/packages/about/lib/etch-component.js @@ -4,8 +4,7 @@ const etch = require('etch') Public: Abstract class for handling the initialization boilerplate of an Etch component. */ -module.exports = -class EtchComponent { +module.exports = class EtchComponent { constructor (props) { this.props = props diff --git a/packages/about/lib/main.js b/packages/about/lib/main.js index f0b855649..ec1420e7f 100644 --- a/packages/about/lib/main.js +++ b/packages/about/lib/main.js @@ -1,4 +1,4 @@ -const {CompositeDisposable} = require('atom') +const { CompositeDisposable } = require('atom') const semver = require('semver') const UpdateManager = require('./update-manager') const About = require('./about') @@ -16,20 +16,33 @@ module.exports = { this.createModel() let availableVersion = window.localStorage.getItem(AvailableUpdateVersion) - if (atom.getReleaseChannel() === 'dev' || (availableVersion && semver.lte(availableVersion, atom.getVersion()))) { + if ( + atom.getReleaseChannel() === 'dev' || + (availableVersion && semver.lte(availableVersion, atom.getVersion())) + ) { this.clearUpdateState() } - this.subscriptions.add(updateManager.onDidChange(() => { - if (updateManager.getState() === UpdateManager.State.UpdateAvailableToInstall) { - window.localStorage.setItem(AvailableUpdateVersion, updateManager.getAvailableVersion()) - this.showStatusBarIfNeeded() - } - })) + this.subscriptions.add( + updateManager.onDidChange(() => { + if ( + updateManager.getState() === + UpdateManager.State.UpdateAvailableToInstall + ) { + window.localStorage.setItem( + AvailableUpdateVersion, + updateManager.getAvailableVersion() + ) + this.showStatusBarIfNeeded() + } + }) + ) - this.subscriptions.add(atom.commands.add('atom-workspace', 'about:clear-update-state', () => { - this.clearUpdateState() - })) + this.subscriptions.add( + atom.commands.add('atom-workspace', 'about:clear-update-state', () => { + this.clearUpdateState() + }) + ) }, deactivate () { diff --git a/packages/about/lib/update-manager.js b/packages/about/lib/update-manager.js index 0db55f08d..bae72753d 100644 --- a/packages/about/lib/update-manager.js +++ b/packages/about/lib/update-manager.js @@ -1,4 +1,4 @@ -const {Emitter, CompositeDisposable} = require('atom') +const { Emitter, CompositeDisposable } = require('atom') const Unsupported = 'unsupported' const Idle = 'idle' @@ -27,7 +27,7 @@ let UpdateManager = class UpdateManager { atom.autoUpdater.onDidBeginDownloadingUpdate(() => { this.setState(DownloadingUpdate) }), - atom.autoUpdater.onDidCompleteDownloadingUpdate(({releaseVersion}) => { + atom.autoUpdater.onDidCompleteDownloadingUpdate(({ releaseVersion }) => { this.setAvailableVersion(releaseVersion) }), atom.autoUpdater.onUpdateNotAvailable(() => { @@ -36,7 +36,7 @@ let UpdateManager = class UpdateManager { atom.autoUpdater.onUpdateError(() => { this.setState(ErrorState) }), - atom.config.observe('core.automaticallyUpdate', (value) => { + atom.config.observe('core.automaticallyUpdate', value => { this.autoUpdatesEnabled = value this.emitDidChange() }) @@ -61,7 +61,9 @@ let UpdateManager = class UpdateManager { } getAutoUpdatesEnabled () { - return this.autoUpdatesEnabled && this.state !== UpdateManager.State.Unsupported + return ( + this.autoUpdatesEnabled && this.state !== UpdateManager.State.Unsupported + ) } setAutoUpdatesEnabled (enabled) { @@ -82,7 +84,9 @@ let UpdateManager = class UpdateManager { } resetState () { - this.state = atom.autoUpdater.platformSupportsUpdates() ? atom.autoUpdater.getState() : Unsupported + this.state = atom.autoUpdater.platformSupportsUpdates() + ? atom.autoUpdater.getState() + : Unsupported this.emitDidChange() } @@ -128,7 +132,8 @@ let UpdateManager = class UpdateManager { appVersion = `v${appVersion}` } - const releaseRepo = appVersion.indexOf('nightly') > -1 ? 'atom-nightly-releases' : 'atom' + const releaseRepo = + appVersion.indexOf('nightly') > -1 ? 'atom-nightly-releases' : 'atom' return `https://github.com/atom/${releaseRepo}/releases/tag/${appVersion}` } } diff --git a/packages/about/spec/about-spec.js b/packages/about/spec/about-spec.js index 60c4136d8..21aa4c5eb 100644 --- a/packages/about/spec/about-spec.js +++ b/packages/about/spec/about-spec.js @@ -1,5 +1,3 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./helpers/async-spec-helpers') // eslint-disable-line no-unused-vars - describe('About', () => { let workspaceElement @@ -9,7 +7,7 @@ describe('About', () => { spyOn(window.localStorage, 'setItem').andCallFake((key, value) => { storage[key] = value }) - spyOn(window.localStorage, 'getItem').andCallFake((key) => { + spyOn(window.localStorage, 'getItem').andCallFake(key => { return storage[key] }) diff --git a/packages/about/spec/about-status-bar-spec.js b/packages/about/spec/about-status-bar-spec.js index a611af67a..a67c0511c 100644 --- a/packages/about/spec/about-status-bar-spec.js +++ b/packages/about/spec/about-status-bar-spec.js @@ -1,4 +1,4 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} = require('./helpers/async-spec-helpers') // eslint-disable-line no-unused-vars +const { conditionPromise } = require('./helpers/async-spec-helpers') const MockUpdater = require('./mocks/updater') describe('the status bar', () => { @@ -11,7 +11,7 @@ describe('the status bar', () => { spyOn(window.localStorage, 'setItem').andCallFake((key, value) => { storage[key] = value }) - spyOn(window.localStorage, 'getItem').andCallFake((key) => { + spyOn(window.localStorage, 'getItem').andCallFake(key => { return storage[key] }) spyOn(atom, 'getVersion').andCallFake(() => { diff --git a/packages/about/spec/helpers/async-spec-helpers.js b/packages/about/spec/helpers/async-spec-helpers.js index 377024691..58e17d323 100644 --- a/packages/about/spec/helpers/async-spec-helpers.js +++ b/packages/about/spec/helpers/async-spec-helpers.js @@ -1,36 +1,7 @@ /** @babel */ -const {now} = Date -const {setTimeout} = global - -export function beforeEach (fn) { - global.beforeEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -export function afterEach (fn) { - global.afterEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { - module.exports[name] = function (description, fn) { - global[name](description, function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) - } -}) +const { now } = Date +const { setTimeout } = global export async function conditionPromise (condition) { const startTime = now() @@ -53,13 +24,3 @@ export function timeoutPromise (timeout) { setTimeout(resolve, timeout) }) } - -function waitsForPromise (fn) { - const promise = fn() - global.waitsFor('spec promise to resolve', function (done) { - promise.then(done, function (error) { - jasmine.getEnv().currentSpec.fail(error) - done() - }) - }) -} diff --git a/packages/about/spec/mocks/updater.js b/packages/about/spec/mocks/updater.js index c96ea4ec1..6c5e1f19d 100644 --- a/packages/about/spec/mocks/updater.js +++ b/packages/about/spec/mocks/updater.js @@ -16,6 +16,8 @@ module.exports = { }, finishDownloadingUpdate (releaseVersion) { - atom.autoUpdater.emitter.emit('did-complete-downloading-update', {releaseVersion}) + atom.autoUpdater.emitter.emit('did-complete-downloading-update', { + releaseVersion + }) } } diff --git a/packages/about/spec/update-manager-spec.js b/packages/about/spec/update-manager-spec.js index 294e33b8b..cc395949e 100644 --- a/packages/about/spec/update-manager-spec.js +++ b/packages/about/spec/update-manager-spec.js @@ -9,14 +9,24 @@ describe('UpdateManager', () => { describe('::getReleaseNotesURLForVersion', () => { it('returns atom.io releases when dev version', () => { - expect(updateManager.getReleaseNotesURLForVersion('1.7.0-dev-e44b57d')).toContain('atom.io/releases') + expect( + updateManager.getReleaseNotesURLForVersion('1.7.0-dev-e44b57d') + ).toContain('atom.io/releases') }) it('returns the page for the release when not a dev version', () => { - expect(updateManager.getReleaseNotesURLForVersion('1.7.0')).toContain('atom/atom/releases/tag/v1.7.0') - expect(updateManager.getReleaseNotesURLForVersion('v1.7.0')).toContain('atom/atom/releases/tag/v1.7.0') - expect(updateManager.getReleaseNotesURLForVersion('1.7.0-beta10')).toContain('atom/atom/releases/tag/v1.7.0-beta10') - expect(updateManager.getReleaseNotesURLForVersion('1.7.0-nightly10')).toContain('atom/atom-nightly-releases/releases/tag/v1.7.0-nightly10') + expect(updateManager.getReleaseNotesURLForVersion('1.7.0')).toContain( + 'atom/atom/releases/tag/v1.7.0' + ) + expect(updateManager.getReleaseNotesURLForVersion('v1.7.0')).toContain( + 'atom/atom/releases/tag/v1.7.0' + ) + expect( + updateManager.getReleaseNotesURLForVersion('1.7.0-beta10') + ).toContain('atom/atom/releases/tag/v1.7.0-beta10') + expect( + updateManager.getReleaseNotesURLForVersion('1.7.0-nightly10') + ).toContain('atom/atom-nightly-releases/releases/tag/v1.7.0-nightly10') }) }) }) diff --git a/packages/about/spec/update-view-spec.js b/packages/about/spec/update-view-spec.js index 83ddf5ac1..8e6587b59 100644 --- a/packages/about/spec/update-view-spec.js +++ b/packages/about/spec/update-view-spec.js @@ -1,5 +1,4 @@ -const {shell} = require('electron') -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./helpers/async-spec-helpers') // eslint-disable-line no-unused-vars +const { shell } = require('electron') const main = require('../lib/main') const AboutView = require('../lib/components/about-view') const UpdateView = require('../lib/components/update-view') @@ -17,7 +16,7 @@ describe('UpdateView', () => { spyOn(window.localStorage, 'setItem').andCallFake((key, value) => { storage[key] = value }) - spyOn(window.localStorage, 'getItem').andCallFake((key) => { + spyOn(window.localStorage, 'getItem').andCallFake(key => { return storage[key] }) @@ -45,13 +44,19 @@ describe('UpdateView', () => { }) it('hides the auto update UI and shows the update instructions link', async () => { - expect(aboutElement.querySelector('.about-update-action-button')).not.toBeVisible() - expect(aboutElement.querySelector('.about-auto-updates')).not.toBeVisible() + expect( + aboutElement.querySelector('.about-update-action-button') + ).not.toBeVisible() + expect( + aboutElement.querySelector('.about-auto-updates') + ).not.toBeVisible() }) it('opens the update instructions page when the instructions link is clicked', async () => { spyOn(shell, 'openExternal') - let link = aboutElement.querySelector('.app-unsupported .about-updates-instructions') + let link = aboutElement.querySelector( + '.app-unsupported .about-updates-instructions' + ) link.click() let args = shell.openExternal.mostRecentCall.args @@ -72,66 +77,116 @@ describe('UpdateView', () => { }) it('shows the correct panels when the app checks for updates and there is no update available', async () => { - expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + expect( + aboutElement.querySelector('.about-default-update-message') + ).toBeVisible() MockUpdater.checkForUpdate() await scheduler.getNextUpdatePromise() expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible() - expect(aboutElement.querySelector('.app-checking-for-updates')).toBeVisible() + expect( + aboutElement.querySelector('.app-checking-for-updates') + ).toBeVisible() MockUpdater.updateNotAvailable() await scheduler.getNextUpdatePromise() expect(aboutElement.querySelector('.app-up-to-date')).toBeVisible() - expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() + expect( + aboutElement.querySelector('.app-checking-for-updates') + ).not.toBeVisible() }) it('shows the correct panels when the app checks for updates and encounters an error', async () => { - expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + expect( + aboutElement.querySelector('.about-default-update-message') + ).toBeVisible() MockUpdater.checkForUpdate() await scheduler.getNextUpdatePromise() expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible() - expect(aboutElement.querySelector('.app-checking-for-updates')).toBeVisible() + expect( + aboutElement.querySelector('.app-checking-for-updates') + ).toBeVisible() spyOn(atom.autoUpdater, 'getErrorMessage').andReturn('an error message') MockUpdater.updateError() await scheduler.getNextUpdatePromise() expect(aboutElement.querySelector('.app-update-error')).toBeVisible() - expect(aboutElement.querySelector('.app-error-message').textContent).toBe('an error message') - expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() - expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) - expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + expect( + aboutElement.querySelector('.app-error-message').textContent + ).toBe('an error message') + expect( + aboutElement.querySelector('.app-checking-for-updates') + ).not.toBeVisible() + expect( + aboutElement.querySelector('.about-update-action-button').disabled + ).toBe(false) + expect( + aboutElement.querySelector('.about-update-action-button').textContent + ).toBe('Check now') }) it('shows the correct panels and button states when the app checks for updates and an update is downloaded', async () => { - expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() - expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) - expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + expect( + aboutElement.querySelector('.about-default-update-message') + ).toBeVisible() + expect( + aboutElement.querySelector('.about-update-action-button').disabled + ).toBe(false) + expect( + aboutElement.querySelector('.about-update-action-button').textContent + ).toBe('Check now') MockUpdater.checkForUpdate() await scheduler.getNextUpdatePromise() expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible() - expect(aboutElement.querySelector('.app-checking-for-updates')).toBeVisible() - expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(true) - expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + expect( + aboutElement.querySelector('.app-checking-for-updates') + ).toBeVisible() + expect( + aboutElement.querySelector('.about-update-action-button').disabled + ).toBe(true) + expect( + aboutElement.querySelector('.about-update-action-button').textContent + ).toBe('Check now') MockUpdater.downloadUpdate() await scheduler.getNextUpdatePromise() - expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() - expect(aboutElement.querySelector('.app-downloading-update')).toBeVisible() + expect( + aboutElement.querySelector('.app-checking-for-updates') + ).not.toBeVisible() + expect( + aboutElement.querySelector('.app-downloading-update') + ).toBeVisible() // TODO: at some point it would be nice to be able to cancel an update download, and then this would be a cancel button - expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(true) - expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + expect( + aboutElement.querySelector('.about-update-action-button').disabled + ).toBe(true) + expect( + aboutElement.querySelector('.about-update-action-button').textContent + ).toBe('Check now') MockUpdater.finishDownloadingUpdate('42.0.0') await scheduler.getNextUpdatePromise() - expect(aboutElement.querySelector('.app-downloading-update')).not.toBeVisible() - expect(aboutElement.querySelector('.app-update-available-to-install')).toBeVisible() + expect( + aboutElement.querySelector('.app-downloading-update') + ).not.toBeVisible() + expect( + aboutElement.querySelector('.app-update-available-to-install') + ).toBeVisible() - expect(aboutElement.querySelector('.app-update-available-to-install .about-updates-version').textContent).toBe('42.0.0') - expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) - expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Restart and install') + expect( + aboutElement.querySelector( + '.app-update-available-to-install .about-updates-version' + ).textContent + ).toBe('42.0.0') + expect( + aboutElement.querySelector('.about-update-action-button').disabled + ).toBe(false) + expect( + aboutElement.querySelector('.about-update-action-button').textContent + ).toBe('Restart and install') }) it('opens the release notes for the downloaded release when the release notes link are clicked', async () => { @@ -139,7 +194,9 @@ describe('UpdateView', () => { await scheduler.getNextUpdatePromise() spyOn(shell, 'openExternal') - let link = aboutElement.querySelector('.app-update-available-to-install .about-updates-release-notes') + let link = aboutElement.querySelector( + '.app-update-available-to-install .about-updates-release-notes' + ) link.click() let args = shell.openExternal.mostRecentCall.args @@ -168,10 +225,18 @@ describe('UpdateView', () => { updateManager.resetState() await scheduler.getNextUpdatePromise() - expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() - expect(aboutElement.querySelector('.app-downloading-update')).toBeVisible() - expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(true) - expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + expect( + aboutElement.querySelector('.app-checking-for-updates') + ).not.toBeVisible() + expect( + aboutElement.querySelector('.app-downloading-update') + ).toBeVisible() + expect( + aboutElement.querySelector('.about-update-action-button').disabled + ).toBe(true) + expect( + aboutElement.querySelector('.about-update-action-button').textContent + ).toBe('Check now') }) describe('when core.automaticallyUpdate is toggled', () => { @@ -181,36 +246,66 @@ describe('UpdateView', () => { }) it('shows the auto update UI', async () => { - expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(true) - expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() - expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Atom will check for updates automatically') + expect( + aboutElement.querySelector('.about-auto-updates input').checked + ).toBe(true) + expect( + aboutElement.querySelector('.about-default-update-message') + ).toBeVisible() + expect( + aboutElement.querySelector('.about-default-update-message') + .textContent + ).toBe('Atom will check for updates automatically') atom.config.set('core.automaticallyUpdate', false) await scheduler.getNextUpdatePromise() - expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(false) - expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() - expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Automatic updates are disabled please check manually') + expect( + aboutElement.querySelector('.about-auto-updates input').checked + ).toBe(false) + expect( + aboutElement.querySelector('.about-default-update-message') + ).toBeVisible() + expect( + aboutElement.querySelector('.about-default-update-message') + .textContent + ).toBe('Automatic updates are disabled please check manually') }) it('updates config and the UI when the checkbox is used to toggle', async () => { - expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(true) + expect( + aboutElement.querySelector('.about-auto-updates input').checked + ).toBe(true) aboutElement.querySelector('.about-auto-updates input').click() await scheduler.getNextUpdatePromise() expect(atom.config.get('core.automaticallyUpdate')).toBe(false) - expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(false) - expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() - expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Automatic updates are disabled please check manually') + expect( + aboutElement.querySelector('.about-auto-updates input').checked + ).toBe(false) + expect( + aboutElement.querySelector('.about-default-update-message') + ).toBeVisible() + expect( + aboutElement.querySelector('.about-default-update-message') + .textContent + ).toBe('Automatic updates are disabled please check manually') aboutElement.querySelector('.about-auto-updates input').click() await scheduler.getNextUpdatePromise() expect(atom.config.get('core.automaticallyUpdate')).toBe(true) - expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(true) - expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() - expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Atom will check for updates automatically') + expect( + aboutElement.querySelector('.about-auto-updates input').checked + ).toBe(true) + expect( + aboutElement.querySelector('.about-default-update-message') + ).toBeVisible() + expect( + aboutElement.querySelector('.about-default-update-message') + .textContent + ).toBe('Atom will check for updates automatically') }) describe('checking for updates', function () { @@ -271,10 +366,20 @@ describe('UpdateView', () => { updateManager = main.model.state.updateManager scheduler = AboutView.getScheduler() - expect(aboutElement.querySelector('.app-update-available-to-install')).toBeVisible() - expect(aboutElement.querySelector('.app-update-available-to-install .about-updates-version').textContent).toBe('42.0.0') - expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) - expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Restart and install') + expect( + aboutElement.querySelector('.app-update-available-to-install') + ).toBeVisible() + expect( + aboutElement.querySelector( + '.app-update-available-to-install .about-updates-version' + ).textContent + ).toBe('42.0.0') + expect( + aboutElement.querySelector('.about-update-action-button').disabled + ).toBe(false) + expect( + aboutElement.querySelector('.about-update-action-button').textContent + ).toBe('Restart and install') }) }) }) diff --git a/packages/dalek/lib/dalek.js b/packages/dalek/lib/dalek.js index 8d322cc03..3f1944a0c 100644 --- a/packages/dalek/lib/dalek.js +++ b/packages/dalek/lib/dalek.js @@ -13,7 +13,9 @@ module.exports = { const names = atom.packages.getAvailablePackageNames() for (let name of names) { if (atom.packages.isBundledPackage(name)) { - const isDuplicatedPackage = await this.isInstalledAsCommunityPackage(name) + const isDuplicatedPackage = await this.isInstalledAsCommunityPackage( + name + ) if (isDuplicatedPackage) { duplicatePackages.push(name) } diff --git a/packages/dalek/test/dalek.test.js b/packages/dalek/test/dalek.test.js index 8b84dd29d..ff1ba394c 100644 --- a/packages/dalek/test/dalek.test.js +++ b/packages/dalek/test/dalek.test.js @@ -17,20 +17,49 @@ describe('dalek', function () { beforeEach(function () { availablePackages = { - 'an-unduplicated-installed-package': path.join('Users', 'username', '.atom', 'packages', 'an-unduplicated-installed-package'), - 'duplicated-package': path.join('Users', 'username', '.atom', 'packages', 'duplicated-package'), - 'unduplicated-package': path.join(`${atom.getLoadSettings().resourcePath}`, 'node_modules', 'unduplicated-package') + 'an-unduplicated-installed-package': path.join( + 'Users', + 'username', + '.atom', + 'packages', + 'an-unduplicated-installed-package' + ), + 'duplicated-package': path.join( + 'Users', + 'username', + '.atom', + 'packages', + 'duplicated-package' + ), + 'unduplicated-package': path.join( + `${atom.getLoadSettings().resourcePath}`, + 'node_modules', + 'unduplicated-package' + ) } atom.devMode = false bundledPackages = ['duplicated-package', 'unduplicated-package'] packageDirPaths = [path.join('Users', 'username', '.atom', 'packages')] sandbox = sinon.sandbox.create() - sandbox.stub(dalek, 'realpath').callsFake((filePath) => Promise.resolve(realPaths[filePath] || filePath)) - sandbox.stub(atom.packages, 'isBundledPackage').callsFake((packageName) => { return bundledPackages.includes(packageName) }) - sandbox.stub(atom.packages, 'getAvailablePackageNames').callsFake(() => Object.keys(availablePackages)) - sandbox.stub(atom.packages, 'getPackageDirPaths').callsFake(() => { return packageDirPaths }) - sandbox.stub(fs, 'existsSync').callsFake((candidate) => { return Object.values(availablePackages).includes(candidate) && !candidate.includes(atom.getLoadSettings().resourcePath) }) + sandbox + .stub(dalek, 'realpath') + .callsFake(filePath => Promise.resolve(realPaths[filePath] || filePath)) + sandbox.stub(atom.packages, 'isBundledPackage').callsFake(packageName => { + return bundledPackages.includes(packageName) + }) + sandbox + .stub(atom.packages, 'getAvailablePackageNames') + .callsFake(() => Object.keys(availablePackages)) + sandbox.stub(atom.packages, 'getPackageDirPaths').callsFake(() => { + return packageDirPaths + }) + sandbox.stub(fs, 'existsSync').callsFake(candidate => { + return ( + Object.values(availablePackages).includes(candidate) && + !candidate.includes(atom.getLoadSettings().resourcePath) + ) + }) }) afterEach(function () { @@ -54,7 +83,13 @@ describe('dalek', function () { describe('when a package is symlinked into the package directory', async function () { beforeEach(function () { const realPath = path.join('Users', 'username', 'duplicated-package') - const packagePath = path.join('Users', 'username', '.atom', 'packages', 'duplicated-package') + const packagePath = path.join( + 'Users', + 'username', + '.atom', + 'packages', + 'duplicated-package' + ) realPaths[packagePath] = realPath }) diff --git a/packages/dalek/test/runner.js b/packages/dalek/test/runner.js index 5688fc9c0..7b155fa67 100644 --- a/packages/dalek/test/runner.js +++ b/packages/dalek/test/runner.js @@ -1,2 +1,2 @@ const createRunner = require('atom-mocha-test-runner').createRunner -module.exports = createRunner({testSuffixes: ['test.js']}) +module.exports = createRunner({ testSuffixes: ['test.js'] }) diff --git a/packages/deprecation-cop/lib/deprecation-cop-view.js b/packages/deprecation-cop/lib/deprecation-cop-view.js index 01d8ad736..0531a2631 100644 --- a/packages/deprecation-cop/lib/deprecation-cop-view.js +++ b/packages/deprecation-cop/lib/deprecation-cop-view.js @@ -2,7 +2,7 @@ /** @jsx etch.dom */ import _ from 'underscore-plus' -import {CompositeDisposable} from 'atom' +import { CompositeDisposable } from 'atom' import etch from 'etch' import fs from 'fs-plus' import Grim from 'grim' @@ -11,23 +11,45 @@ import path from 'path' import shell from 'shell' export default class DeprecationCopView { - constructor ({uri}) { + constructor ({ uri }) { this.uri = uri - this.subscriptions = new CompositeDisposable - this.subscriptions.add(Grim.on('updated', () => { etch.update(this) })) + this.subscriptions = new CompositeDisposable() + this.subscriptions.add( + Grim.on('updated', () => { + etch.update(this) + }) + ) // TODO: Remove conditional when the new StyleManager deprecation APIs reach stable. if (atom.styles.onDidUpdateDeprecations) { - this.subscriptions.add(atom.styles.onDidUpdateDeprecations(() => { etch.update(this) })) + this.subscriptions.add( + atom.styles.onDidUpdateDeprecations(() => { + etch.update(this) + }) + ) } etch.initialize(this) - this.subscriptions.add(atom.commands.add(this.element, { - 'core:move-up': () => { this.scrollUp() }, - 'core:move-down': () => { this.scrollDown() }, - 'core:page-up': () => { this.pageUp() }, - 'core:page-down': () => { this.pageDown() }, - 'core:move-to-top': () => { this.scrollToTop() }, - 'core:move-to-bottom': () => { this.scrollToBottom() } - })) + this.subscriptions.add( + atom.commands.add(this.element, { + 'core:move-up': () => { + this.scrollUp() + }, + 'core:move-down': () => { + this.scrollDown() + }, + 'core:page-up': () => { + this.pageUp() + }, + 'core:page-down': () => { + this.pageDown() + }, + 'core:move-to-top': () => { + this.scrollToTop() + }, + 'core:move-to-bottom': () => { + this.scrollToBottom() + } + }) + ) } serialize () { @@ -49,25 +71,35 @@ export default class DeprecationCopView { render () { return ( -
+
+ }} + > + Check for Updates +
-
Deprecated calls
+
+ Deprecated calls +
    {this.renderDeprecatedCalls()}
-
Deprecated selectors
+
+ Deprecated selectors +
    {this.renderDeprecatedSelectors()}
@@ -82,37 +114,57 @@ export default class DeprecationCopView { if (packageNames.length === 0) { return
  • No deprecated calls
  • } else { - return packageNames.sort().map((packageName) => ( + return packageNames.sort().map(packageName => (
  • -
    event.target.parentElement.classList.toggle('collapsed')}> +
    + event.target.parentElement.classList.toggle('collapsed') + } + > {packageName || 'atom core'} - {` (${_.pluralize(deprecationsByPackageName[packageName].length, 'deprecation')})`} + {` (${_.pluralize( + deprecationsByPackageName[packageName].length, + 'deprecation' + )})`}
      {this.renderPackageActionsIfNeeded(packageName)} - {deprecationsByPackageName[packageName].map(({deprecation, stack}) => ( -
    • - -
      - {this.renderIssueURLIfNeeded(packageName, deprecation, this.buildIssueURL(packageName, deprecation, stack))} -
      - {stack.map(({functionName, location}) => ( -
      - {functionName} - - - { - event.preventDefault() - this.openLocation(location) - }}>{location} + {deprecationsByPackageName[packageName].map( + ({ deprecation, stack }) => ( +
    • + +
      + {this.renderIssueURLIfNeeded( + packageName, + deprecation, + this.buildIssueURL(packageName, deprecation, stack) + )} +
      + {stack.map(({ functionName, location }) => ( + + ))}
      - ))} -
      -
    • - ))} + + ) + )}
  • )) @@ -123,41 +175,61 @@ export default class DeprecationCopView { const deprecationsByPackageName = this.getDeprecatedSelectorsByPackageName() const packageNames = Object.keys(deprecationsByPackageName) if (packageNames.length === 0) { - return ( -
  • No deprecated selectors
  • - ) + return
  • No deprecated selectors
  • } else { - return packageNames.map((packageName) => ( + return packageNames.map(packageName => (
  • -
    event.target.parentElement.classList.toggle('collapsed')}> +
    + event.target.parentElement.classList.toggle('collapsed') + } + > {packageName}
      {this.renderPackageActionsIfNeeded(packageName)} - {deprecationsByPackageName[packageName].map(({packagePath, sourcePath, deprecation}) => { - const relativeSourcePath = path.relative(packagePath, sourcePath) - const issueTitle = `Deprecated selector in \`${relativeSourcePath}\`` - const issueBody = `In \`${relativeSourcePath}\`: \n\n${deprecation.message}` - return ( -
    • - { - event.preventDefault() - this.openLocation(sourcePath) - }}>{relativeSourcePath} -
        -
      • - -
        - {this.renderSelectorIssueURLIfNeeded(packageName, issueTitle, issueBody)} -
      • -
      -
    • - ) - })} + {deprecationsByPackageName[packageName].map( + ({ packagePath, sourcePath, deprecation }) => { + const relativeSourcePath = path.relative( + packagePath, + sourcePath + ) + const issueTitle = `Deprecated selector in \`${relativeSourcePath}\`` + const issueBody = `In \`${relativeSourcePath}\`: \n\n${ + deprecation.message + }` + return ( +
    • + { + event.preventDefault() + this.openLocation(sourcePath) + }} + > + {relativeSourcePath} + +
        +
      • + +
        + {this.renderSelectorIssueURLIfNeeded( + packageName, + issueTitle, + issueBody + )} +
      • +
      +
    • + ) + } + )}
  • )) @@ -171,17 +243,23 @@ export default class DeprecationCopView {
    + }} + > + Check for Update + + }} + > + Disable Package +
    ) @@ -191,13 +269,18 @@ export default class DeprecationCopView { } encodeURI (str) { - return encodeURI(str).replace(/#/g, '%23').replace(/;/g, '%3B').replace(/%20/g, '+') + return encodeURI(str) + .replace(/#/g, '%23') + .replace(/;/g, '%3B') + .replace(/%20/g, '+') } renderSelectorIssueURLIfNeeded (packageName, issueTitle, issueBody) { const repoURL = this.getRepoURL(packageName) if (repoURL) { - const issueURL = `${repoURL}/issues/new?title=${this.encodeURI(issueTitle)}&body=${this.encodeURI(issueBody)}` + const issueURL = `${repoURL}/issues/new?title=${this.encodeURI( + issueTitle + )}&body=${this.encodeURI(issueBody)}` return (
    + }} + > + Report Issue +
    ) } else { @@ -227,10 +313,13 @@ export default class DeprecationCopView { data-issue-title={issueTitle} data-repo-url={repoURL} data-issue-url={issueURL} - onclick={(event) => { + onclick={event => { event.preventDefault() this.openIssueURL(repoURL, issueURL, issueTitle) - }}>Report Issue + }} + > + Report Issue +
    ) } else { @@ -242,9 +331,13 @@ export default class DeprecationCopView { const repoURL = this.getRepoURL(packageName) if (repoURL) { const title = `${deprecation.getOriginName()} is deprecated.` - const stacktrace = stack.map(({functionName, location}) => `${functionName} (${location})`).join("\n") + const stacktrace = stack + .map(({ functionName, location }) => `${functionName} (${location})`) + .join('\n') const body = `${deprecation.getMessage()}\n\`\`\`\n${stacktrace}\n\`\`\`` - return `${repoURL}/issues/new?title=${encodeURI(title)}&body=${encodeURI(body)}` + return `${repoURL}/issues/new?title=${encodeURI(title)}&body=${encodeURI( + body + )}` } else { return null } @@ -266,13 +359,16 @@ export default class DeprecationCopView { const url = 'https://api.github.com/search/issues' const repo = repoURL.replace(/http(s)?:\/\/(\d+\.)?github.com\//gi, '') const query = `${issueTitle} repo:${repo}` - const response = await window.fetch(`${url}?q=${encodeURI(query)}&sort=created`, { - method: 'GET', - headers: { - 'Accept': 'application/vnd.github.v3+json', - 'Content-Type': 'application/json' + const response = await window.fetch( + `${url}?q=${encodeURI(query)}&sort=created`, + { + method: 'GET', + headers: { + Accept: 'application/vnd.github.v3+json', + 'Content-Type': 'application/json' + } } - }) + ) if (response.ok) { const data = await response.json() @@ -284,21 +380,25 @@ export default class DeprecationCopView { } } - return (issues.open || issues.closed) + return issues.open || issues.closed } } } async shortenURL (url) { let encodedUrl = encodeURIComponent(url).substr(0, 5000) // is.gd has 5000 char limit - let incompletePercentEncoding = encodedUrl.indexOf('%', encodedUrl.length - 2) - if (incompletePercentEncoding >= 0) { // Handle an incomplete % encoding cut-off + let incompletePercentEncoding = encodedUrl.indexOf( + '%', + encodedUrl.length - 2 + ) + if (incompletePercentEncoding >= 0) { + // Handle an incomplete % encoding cut-off encodedUrl = encodedUrl.substr(0, incompletePercentEncoding) } let result = await fetch('https://is.gd/create.php?format=simple', { method: 'POST', - headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `url=${encodedUrl}` }) @@ -307,8 +407,14 @@ export default class DeprecationCopView { getRepoURL (packageName) { const loadedPackage = atom.packages.getLoadedPackage(packageName) - if (loadedPackage && loadedPackage.metadata && loadedPackage.metadata.repository) { - const url = loadedPackage.metadata.repository.url || loadedPackage.metadata.repository + if ( + loadedPackage && + loadedPackage.metadata && + loadedPackage.metadata.repository + ) { + const url = + loadedPackage.metadata.repository.url || + loadedPackage.metadata.repository return url.replace(/\.git$/, '') } else { return null @@ -330,8 +436,9 @@ export default class DeprecationCopView { packageName = (this.getPackageName(stack) || '').toLowerCase() } - deprecatedCallsByPackageName[packageName] = deprecatedCallsByPackageName[packageName] || [] - deprecatedCallsByPackageName[packageName].push({deprecation, stack}) + deprecatedCallsByPackageName[packageName] = + deprecatedCallsByPackageName[packageName] || [] + deprecatedCallsByPackageName[packageName].push({ deprecation, stack }) } } return deprecatedCallsByPackageName @@ -352,11 +459,18 @@ export default class DeprecationCopView { packagePath = '' } else { packageName = components[packagesComponentIndex + 1] - packagePath = components.slice(0, packagesComponentIndex + 1).join(path.sep) + packagePath = components + .slice(0, packagesComponentIndex + 1) + .join(path.sep) } - deprecatedSelectorsByPackageName[packageName] = deprecatedSelectorsByPackageName[packageName] || [] - deprecatedSelectorsByPackageName[packageName].push({packagePath, sourcePath, deprecation}) + deprecatedSelectorsByPackageName[packageName] = + deprecatedSelectorsByPackageName[packageName] || [] + deprecatedSelectorsByPackageName[packageName].push({ + packagePath, + sourcePath, + deprecation + }) } } @@ -366,13 +480,16 @@ export default class DeprecationCopView { getPackageName (stack) { const packagePaths = this.getPackagePathsByPackageName() for (const [packageName, packagePath] of packagePaths) { - if (packagePath.includes('.atom/dev/packages') || packagePath.includes('.atom/packages')) { + if ( + packagePath.includes('.atom/dev/packages') || + packagePath.includes('.atom/packages') + ) { packagePaths.set(packageName, fs.absolute(packagePath)) } } for (let i = 1; i < stack.length; i++) { - const {fileName} = stack[i] + const { fileName } = stack[i] // Empty when it was run from the dev console if (!fileName) { @@ -426,7 +543,7 @@ export default class DeprecationCopView { if (process.platform === 'win32') { pathToOpen = pathToOpen.replace(/^\//, '') } - atom.open({pathsToOpen: [pathToOpen]}) + atom.open({ pathsToOpen: [pathToOpen] }) } getURI () { diff --git a/packages/deprecation-cop/lib/main.js b/packages/deprecation-cop/lib/main.js index 12da158b5..3dd94da6d 100644 --- a/packages/deprecation-cop/lib/main.js +++ b/packages/deprecation-cop/lib/main.js @@ -1,4 +1,4 @@ -const {Disposable, CompositeDisposable} = require('atom') +const { Disposable, CompositeDisposable } = require('atom') const DeprecationCopView = require('./deprecation-cop-view') const DeprecationCopStatusBarView = require('./deprecation-cop-status-bar-view') const ViewURI = 'atom://deprecation-cop' @@ -6,14 +6,18 @@ const ViewURI = 'atom://deprecation-cop' class DeprecationCopPackage { activate () { this.disposables = new CompositeDisposable() - this.disposables.add(atom.workspace.addOpener((uri) => { - if (uri === ViewURI) { - return this.deserializeDeprecationCopView({uri}) - } - })) - this.disposables.add(atom.commands.add('atom-workspace', 'deprecation-cop:view', () => { - atom.workspace.open(ViewURI) - })) + this.disposables.add( + atom.workspace.addOpener(uri => { + if (uri === ViewURI) { + return this.deserializeDeprecationCopView({ uri }) + } + }) + ) + this.disposables.add( + atom.commands.add('atom-workspace', 'deprecation-cop:view', () => { + atom.workspace.open(ViewURI) + }) + ) } deactivate () { @@ -30,9 +34,20 @@ class DeprecationCopPackage { consumeStatusBar (statusBar) { const statusBarView = new DeprecationCopStatusBarView() - const statusBarTile = statusBar.addRightTile({item: statusBarView, priority: 150}) - this.disposables.add(new Disposable(() => { statusBarView.destroy() })) - this.disposables.add(new Disposable(() => { statusBarTile.destroy() })) + const statusBarTile = statusBar.addRightTile({ + item: statusBarView, + priority: 150 + }) + this.disposables.add( + new Disposable(() => { + statusBarView.destroy() + }) + ) + this.disposables.add( + new Disposable(() => { + statusBarTile.destroy() + }) + ) } } diff --git a/packages/dev-live-reload/lib/base-theme-watcher.js b/packages/dev-live-reload/lib/base-theme-watcher.js index 8d8658a8c..e18a974f6 100644 --- a/packages/dev-live-reload/lib/base-theme-watcher.js +++ b/packages/dev-live-reload/lib/base-theme-watcher.js @@ -2,16 +2,19 @@ const fs = require('fs-plus') const path = require('path') const Watcher = require('./watcher') -module.exports = -class BaseThemeWatcher extends Watcher { +module.exports = class BaseThemeWatcher extends Watcher { constructor () { super() - this.stylesheetsPath = path.dirname(atom.themes.resolveStylesheet('../static/atom.less')) + this.stylesheetsPath = path.dirname( + atom.themes.resolveStylesheet('../static/atom.less') + ) this.watch() } watch () { - const filePaths = fs.readdirSync(this.stylesheetsPath).filter(filePath => path.extname(filePath).includes('less')) + const filePaths = fs + .readdirSync(this.stylesheetsPath) + .filter(filePath => path.extname(filePath).includes('less')) for (const filePath of filePaths) { this.watchFile(path.join(this.stylesheetsPath, filePath)) diff --git a/packages/dev-live-reload/lib/main.js b/packages/dev-live-reload/lib/main.js index 8e80de68f..a486231cf 100644 --- a/packages/dev-live-reload/lib/main.js +++ b/packages/dev-live-reload/lib/main.js @@ -5,7 +5,9 @@ module.exports = { if (atom.packages.hasActivatedInitialPackages()) { this.startWatching() } else { - this.activatedDisposable = atom.packages.onDidActivateInitialPackages(() => this.startWatching()) + this.activatedDisposable = atom.packages.onDidActivateInitialPackages( + () => this.startWatching() + ) } }, @@ -17,8 +19,12 @@ module.exports = { startWatching () { const UIWatcher = require('./ui-watcher') - this.uiWatcher = new UIWatcher({themeManager: atom.themes}) - this.commandDisposable = atom.commands.add('atom-workspace', 'dev-live-reload:reload-all', () => this.uiWatcher.reloadAll()) + this.uiWatcher = new UIWatcher({ themeManager: atom.themes }) + this.commandDisposable = atom.commands.add( + 'atom-workspace', + 'dev-live-reload:reload-all', + () => this.uiWatcher.reloadAll() + ) if (this.activatedDisposable) this.activatedDisposable.dispose() } } diff --git a/packages/dev-live-reload/lib/package-watcher.js b/packages/dev-live-reload/lib/package-watcher.js index 044b59719..2cb8096dd 100644 --- a/packages/dev-live-reload/lib/package-watcher.js +++ b/packages/dev-live-reload/lib/package-watcher.js @@ -2,8 +2,7 @@ const fs = require('fs-plus') const Watcher = require('./watcher') -module.exports = -class PackageWatcher extends Watcher { +module.exports = class PackageWatcher extends Watcher { static supportsPackage (pack, type) { if (pack.getType() === type && pack.getStylesheetPaths().length) return true return false @@ -24,7 +23,9 @@ class PackageWatcher extends Watcher { const stylesheetsPath = this.pack.getStylesheetsPath() - if (fs.isDirectorySync(stylesheetsPath)) this.watchDirectory(stylesheetsPath) + if (fs.isDirectorySync(stylesheetsPath)) { + this.watchDirectory(stylesheetsPath) + } const stylesheetPaths = new Set(this.pack.getStylesheetPaths()) const onFile = stylesheetPath => stylesheetPaths.add(stylesheetPath) diff --git a/packages/dev-live-reload/lib/ui-watcher.js b/packages/dev-live-reload/lib/ui-watcher.js index 458d31a78..eb5ac2635 100644 --- a/packages/dev-live-reload/lib/ui-watcher.js +++ b/packages/dev-live-reload/lib/ui-watcher.js @@ -1,10 +1,9 @@ -const {CompositeDisposable} = require('atom') +const { CompositeDisposable } = require('atom') const BaseThemeWatcher = require('./base-theme-watcher') const PackageWatcher = require('./package-watcher') -module.exports = -class UIWatcher { +module.exports = class UIWatcher { constructor () { this.subscriptions = new CompositeDisposable() this.reloadAll = this.reloadAll.bind(this) @@ -16,39 +15,63 @@ class UIWatcher { watchPackages () { this.watchedThemes = new Map() this.watchedPackages = new Map() - for (const theme of atom.themes.getActiveThemes()) { this.watchTheme(theme) } - for (const pack of atom.packages.getActivePackages()) { this.watchPackage(pack) } + for (const theme of atom.themes.getActiveThemes()) { + this.watchTheme(theme) + } + for (const pack of atom.packages.getActivePackages()) { + this.watchPackage(pack) + } this.watchForPackageChanges() } watchForPackageChanges () { - this.subscriptions.add(atom.themes.onDidChangeActiveThemes(() => { - // We need to destroy all theme watchers as all theme packages are destroyed - // when a theme changes. - for (const theme of this.watchedThemes.values()) { theme.destroy() } + this.subscriptions.add( + atom.themes.onDidChangeActiveThemes(() => { + // We need to destroy all theme watchers as all theme packages are destroyed + // when a theme changes. + for (const theme of this.watchedThemes.values()) { + theme.destroy() + } - this.watchedThemes.clear() + this.watchedThemes.clear() - // Rewatch everything! - for (const theme of atom.themes.getActiveThemes()) { this.watchTheme(theme) } - })) + // Rewatch everything! + for (const theme of atom.themes.getActiveThemes()) { + this.watchTheme(theme) + } + }) + ) - this.subscriptions.add(atom.packages.onDidActivatePackage(pack => this.watchPackage(pack))) + this.subscriptions.add( + atom.packages.onDidActivatePackage(pack => this.watchPackage(pack)) + ) - this.subscriptions.add(atom.packages.onDidDeactivatePackage(pack => { - // This only handles packages - onDidChangeActiveThemes handles themes - const watcher = this.watchedPackages.get(pack.name) - if (watcher) watcher.destroy() - this.watchedPackages.delete(pack.name) - })) + this.subscriptions.add( + atom.packages.onDidDeactivatePackage(pack => { + // This only handles packages - onDidChangeActiveThemes handles themes + const watcher = this.watchedPackages.get(pack.name) + if (watcher) watcher.destroy() + this.watchedPackages.delete(pack.name) + }) + ) } watchTheme (theme) { - if (PackageWatcher.supportsPackage(theme, 'theme')) this.watchedThemes.set(theme.name, this.createWatcher(new PackageWatcher(theme))) + if (PackageWatcher.supportsPackage(theme, 'theme')) { + this.watchedThemes.set( + theme.name, + this.createWatcher(new PackageWatcher(theme)) + ) + } } watchPackage (pack) { - if (PackageWatcher.supportsPackage(pack, 'atom')) this.watchedPackages.set(pack.name, this.createWatcher(new PackageWatcher(pack))) + if (PackageWatcher.supportsPackage(pack, 'atom')) { + this.watchedPackages.set( + pack.name, + this.createWatcher(new PackageWatcher(pack)) + ) + } } createWatcher (watcher) { @@ -56,7 +79,9 @@ class UIWatcher { console.log('Global changed, reloading all styles') this.reloadAll() }) - watcher.onDidDestroy(() => this.watchers.splice(this.watchers.indexOf(watcher), 1)) + watcher.onDidDestroy(() => + this.watchers.splice(this.watchers.indexOf(watcher), 1) + ) this.watchers.push(watcher) return watcher } @@ -64,18 +89,26 @@ class UIWatcher { reloadAll () { this.baseTheme.loadAllStylesheets() for (const pack of atom.packages.getActivePackages()) { - if (PackageWatcher.supportsPackage(pack, 'atom')) pack.reloadStylesheets() + if (PackageWatcher.supportsPackage(pack, 'atom')) { + pack.reloadStylesheets() + } } for (const theme of atom.themes.getActiveThemes()) { - if (PackageWatcher.supportsPackage(theme, 'theme')) theme.reloadStylesheets() + if (PackageWatcher.supportsPackage(theme, 'theme')) { + theme.reloadStylesheets() + } } } destroy () { this.subscriptions.dispose() this.baseTheme.destroy() - for (const pack of this.watchedPackages.values()) { pack.destroy() } - for (const theme of this.watchedThemes.values()) { theme.destroy() } + for (const pack of this.watchedPackages.values()) { + pack.destroy() + } + for (const theme of this.watchedThemes.values()) { + theme.destroy() + } } } diff --git a/packages/dev-live-reload/lib/watcher.js b/packages/dev-live-reload/lib/watcher.js index 2ecde1eef..b1b3631a5 100644 --- a/packages/dev-live-reload/lib/watcher.js +++ b/packages/dev-live-reload/lib/watcher.js @@ -1,8 +1,7 @@ -const {CompositeDisposable, File, Directory, Emitter} = require('atom') +const { CompositeDisposable, File, Directory, Emitter } = require('atom') const path = require('path') -module.exports = -class Watcher { +module.exports = class Watcher { constructor () { this.destroy = this.destroy.bind(this) this.emitter = new Emitter() @@ -66,7 +65,10 @@ class Watcher { } isInAsarArchive (pathToCheck) { - const {resourcePath} = atom.getLoadSettings() - return pathToCheck.startsWith(`${resourcePath}${path.sep}`) && path.extname(resourcePath) === '.asar' + const { resourcePath } = atom.getLoadSettings() + return ( + pathToCheck.startsWith(`${resourcePath}${path.sep}`) && + path.extname(resourcePath) === '.asar' + ) } } diff --git a/packages/dev-live-reload/spec/async-spec-helpers.js b/packages/dev-live-reload/spec/async-spec-helpers.js index 73002c049..e78205989 100644 --- a/packages/dev-live-reload/spec/async-spec-helpers.js +++ b/packages/dev-live-reload/spec/async-spec-helpers.js @@ -1,40 +1,9 @@ /** @babel */ -export function beforeEach (fn) { - global.beforeEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -export function afterEach (fn) { - global.afterEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { - module.exports[name] = function (description, fn) { - if (fn === undefined) { - global[name](description) - return - } - - global[name](description, function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) - } -}) - -export async function conditionPromise (condition, description = 'anonymous condition') { +export async function conditionPromise ( + condition, + description = 'anonymous condition' +) { const startTime = Date.now() while (true) { @@ -55,49 +24,3 @@ export function timeoutPromise (timeout) { global.setTimeout(resolve, timeout) }) } - -function waitsForPromise (fn) { - const promise = fn() - global.waitsFor('spec promise to resolve', function (done) { - promise.then(done, function (error) { - jasmine.getEnv().currentSpec.fail(error) - done() - }) - }) -} - -export function emitterEventPromise (emitter, event, timeout = 15000) { - return new Promise((resolve, reject) => { - const timeoutHandle = setTimeout(() => { - reject(new Error(`Timed out waiting for '${event}' event`)) - }, timeout) - emitter.once(event, () => { - clearTimeout(timeoutHandle) - resolve() - }) - }) -} - -export function promisify (original) { - return function (...args) { - return new Promise((resolve, reject) => { - args.push((err, ...results) => { - if (err) { - reject(err) - } else { - resolve(...results) - } - }) - - return original(...args) - }) - } -} - -export function promisifySome (obj, fnNames) { - const result = {} - for (const fnName of fnNames) { - result[fnName] = promisify(obj[fnName]) - } - return result -} diff --git a/packages/dev-live-reload/spec/dev-live-reload-spec.js b/packages/dev-live-reload/spec/dev-live-reload-spec.js index dec828b65..7351e0f99 100644 --- a/packages/dev-live-reload/spec/dev-live-reload-spec.js +++ b/packages/dev-live-reload/spec/dev-live-reload-spec.js @@ -1,5 +1,3 @@ -const {it, fit, ffit, afterEach, beforeEach} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars - describe('Dev Live Reload', () => { describe('package activation', () => { let [pack, mainModule] = [] @@ -85,7 +83,9 @@ describe('Dev Live Reload', () => { it('stops watching all files', async () => { spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true) - const {mainModule} = await atom.packages.activatePackage('dev-live-reload') + const { mainModule } = await atom.packages.activatePackage( + 'dev-live-reload' + ) expect(mainModule.uiWatcher).not.toBeNull() spyOn(mainModule.uiWatcher, 'destroy') @@ -95,7 +95,9 @@ describe('Dev Live Reload', () => { }) it('unsubscribes from the onDidActivateInitialPackages subscription if it is disabled before all initial packages are activated', async () => { - const {mainModule} = await atom.packages.activatePackage('dev-live-reload') + const { mainModule } = await atom.packages.activatePackage( + 'dev-live-reload' + ) expect(mainModule.activatedDisposable.disposed).toBe(false) await atom.packages.deactivatePackage('dev-live-reload') @@ -109,16 +111,18 @@ describe('Dev Live Reload', () => { it('removes its commands', async () => { spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true) await atom.packages.activatePackage('dev-live-reload') - expect(atom.commands - .findCommands({target: atom.views.getView(atom.workspace)}) - .filter(command => command.name.startsWith('dev-live-reload')) - .length).toBeGreaterThan(0) + expect( + atom.commands + .findCommands({ target: atom.views.getView(atom.workspace) }) + .filter(command => command.name.startsWith('dev-live-reload')).length + ).toBeGreaterThan(0) await atom.packages.deactivatePackage('dev-live-reload') - expect(atom.commands - .findCommands({target: atom.views.getView(atom.workspace)}) - .filter(command => command.name.startsWith('dev-live-reload')) - .length).toBe(0) + expect( + atom.commands + .findCommands({ target: atom.views.getView(atom.workspace) }) + .filter(command => command.name.startsWith('dev-live-reload')).length + ).toBe(0) }) }) }) diff --git a/packages/dev-live-reload/spec/ui-watcher-spec.js b/packages/dev-live-reload/spec/ui-watcher-spec.js index c52ce35ab..0027dd5c3 100644 --- a/packages/dev-live-reload/spec/ui-watcher-spec.js +++ b/packages/dev-live-reload/spec/ui-watcher-spec.js @@ -2,25 +2,31 @@ const path = require('path') const UIWatcher = require('../lib/ui-watcher') -const {it, fit, ffit, afterEach, beforeEach, conditionPromise} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars +const { conditionPromise } = require('./async-spec-helpers') describe('UIWatcher', () => { let uiWatcher = null - beforeEach(() => atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures'))) + beforeEach(() => + atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures')) + ) afterEach(() => uiWatcher && uiWatcher.destroy()) describe("when a base theme's file changes", () => { beforeEach(() => { - spyOn(atom.themes, 'resolveStylesheet').andReturn(path.join(__dirname, 'fixtures', 'static', 'atom.less')) + spyOn(atom.themes, 'resolveStylesheet').andReturn( + path.join(__dirname, 'fixtures', 'static', 'atom.less') + ) uiWatcher = new UIWatcher() }) it('reloads all the base styles', () => { spyOn(atom.themes, 'reloadBaseStylesheets') - expect(uiWatcher.baseTheme.entities[0].getPath()).toContain(`${path.sep}static${path.sep}`) + expect(uiWatcher.baseTheme.entities[0].getPath()).toContain( + `${path.sep}static${path.sep}` + ) uiWatcher.baseTheme.entities[0].emitter.emit('did-change') expect(atom.themes.reloadBaseStylesheets).toHaveBeenCalled() @@ -28,7 +34,11 @@ describe('UIWatcher', () => { }) it("watches all the style sheets in the theme's styles folder", async () => { - const packagePath = path.join(__dirname, 'fixtures', 'package-with-styles-folder') + const packagePath = path.join( + __dirname, + 'fixtures', + 'package-with-styles-folder' + ) await atom.packages.activatePackage(packagePath) uiWatcher = new UIWatcher() @@ -36,15 +46,25 @@ describe('UIWatcher', () => { const lastWatcher = uiWatcher.watchers[uiWatcher.watchers.length - 1] expect(lastWatcher.entities.length).toBe(4) - expect(lastWatcher.entities[0].getPath()).toBe(path.join(packagePath, 'styles')) - expect(lastWatcher.entities[1].getPath()).toBe(path.join(packagePath, 'styles', '3.css')) - expect(lastWatcher.entities[2].getPath()).toBe(path.join(packagePath, 'styles', 'sub', '1.css')) - expect(lastWatcher.entities[3].getPath()).toBe(path.join(packagePath, 'styles', 'sub', '2.less')) + expect(lastWatcher.entities[0].getPath()).toBe( + path.join(packagePath, 'styles') + ) + expect(lastWatcher.entities[1].getPath()).toBe( + path.join(packagePath, 'styles', '3.css') + ) + expect(lastWatcher.entities[2].getPath()).toBe( + path.join(packagePath, 'styles', 'sub', '1.css') + ) + expect(lastWatcher.entities[3].getPath()).toBe( + path.join(packagePath, 'styles', 'sub', '2.less') + ) }) describe('when a package stylesheet file changes', async () => { beforeEach(async () => { - await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-manifest')) + await atom.packages.activatePackage( + path.join(__dirname, 'fixtures', 'package-with-styles-manifest') + ) uiWatcher = new UIWatcher() }) @@ -71,7 +91,10 @@ describe('UIWatcher', () => { describe('when a package global file changes', () => { beforeEach(async () => { - atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-multiple-imported-files']) + atom.config.set('core.themes', [ + 'theme-with-ui-variables', + 'theme-with-multiple-imported-files' + ]) await atom.themes.activateThemes() uiWatcher = new UIWatcher() @@ -85,7 +108,9 @@ describe('UIWatcher', () => { spyOn(theme, 'reloadStylesheets') } - for (const entity of uiWatcher.watchedThemes.get('theme-with-multiple-imported-files').entities) { + for (const entity of uiWatcher.watchedThemes.get( + 'theme-with-multiple-imported-files' + ).entities) { if (entity.getPath().indexOf('variables') > -1) varEntity = entity } varEntity.emitter.emit('did-change') @@ -101,21 +126,31 @@ describe('UIWatcher', () => { uiWatcher = new UIWatcher() expect(uiWatcher.watchedPackages.size).toBe(0) - await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-folder')) - expect(uiWatcher.watchedPackages.get('package-with-styles-folder')).not.toBeUndefined() + await atom.packages.activatePackage( + path.join(__dirname, 'fixtures', 'package-with-styles-folder') + ) + expect( + uiWatcher.watchedPackages.get('package-with-styles-folder') + ).not.toBeUndefined() }) it('unwatches a package after it is deactivated', async () => { - await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-folder')) + await atom.packages.activatePackage( + path.join(__dirname, 'fixtures', 'package-with-styles-folder') + ) uiWatcher = new UIWatcher() - const watcher = uiWatcher.watchedPackages.get('package-with-styles-folder') + const watcher = uiWatcher.watchedPackages.get( + 'package-with-styles-folder' + ) expect(watcher).not.toBeUndefined() const watcherDestructionSpy = jasmine.createSpy('watcher-on-did-destroy') watcher.onDidDestroy(watcherDestructionSpy) await atom.packages.deactivatePackage('package-with-styles-folder') - expect(uiWatcher.watchedPackages.get('package-with-styles-folder')).toBeUndefined() + expect( + uiWatcher.watchedPackages.get('package-with-styles-folder') + ).toBeUndefined() expect(uiWatcher.watchedPackages.size).toBe(0) expect(watcherDestructionSpy).toHaveBeenCalled() }) @@ -124,7 +159,9 @@ describe('UIWatcher', () => { uiWatcher = new UIWatcher() uiWatcher.destroy() - await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-folder')) + await atom.packages.activatePackage( + path.join(__dirname, 'fixtures', 'package-with-styles-folder') + ) expect(uiWatcher.watchedPackages.size).toBe(0) }) }) @@ -132,7 +169,10 @@ describe('UIWatcher', () => { describe('minimal theme packages', () => { let pack = null beforeEach(async () => { - atom.config.set('core.themes', ['theme-with-syntax-variables', 'theme-with-index-less']) + atom.config.set('core.themes', [ + 'theme-with-syntax-variables', + 'theme-with-index-less' + ]) await atom.themes.activateThemes() uiWatcher = new UIWatcher() pack = atom.themes.getActiveThemes()[0] @@ -157,7 +197,10 @@ describe('UIWatcher', () => { describe('theme packages', () => { let pack = null beforeEach(async () => { - atom.config.set('core.themes', ['theme-with-syntax-variables', 'theme-with-multiple-imported-files']) + atom.config.set('core.themes', [ + 'theme-with-syntax-variables', + 'theme-with-multiple-imported-files' + ]) await atom.themes.activateThemes() uiWatcher = new UIWatcher() @@ -170,7 +213,9 @@ describe('UIWatcher', () => { spyOn(pack, 'reloadStylesheets') spyOn(atom.themes, 'reloadBaseStylesheets') - const watcher = uiWatcher.watchedThemes.get('theme-with-multiple-imported-files') + const watcher = uiWatcher.watchedThemes.get( + 'theme-with-multiple-imported-files' + ) expect(watcher.entities.length).toBe(6) @@ -186,14 +231,21 @@ describe('UIWatcher', () => { jasmine.useRealClock() atom.config.set('core.themes', []) - await conditionPromise(() => !uiWatcher.watchedThemes['theme-with-multiple-imported-files']) + await conditionPromise( + () => !uiWatcher.watchedThemes['theme-with-multiple-imported-files'] + ) }) it('watches a new theme when it is deactivated', async () => { jasmine.useRealClock() - atom.config.set('core.themes', ['theme-with-syntax-variables', 'theme-with-package-file']) - await conditionPromise(() => uiWatcher.watchedThemes.get('theme-with-package-file')) + atom.config.set('core.themes', [ + 'theme-with-syntax-variables', + 'theme-with-package-file' + ]) + await conditionPromise(() => + uiWatcher.watchedThemes.get('theme-with-package-file') + ) pack = atom.themes.getActiveThemes()[0] spyOn(pack, 'reloadStylesheets') diff --git a/packages/exception-reporting/lib/main.js b/packages/exception-reporting/lib/main.js index 3909d5793..5893638f4 100644 --- a/packages/exception-reporting/lib/main.js +++ b/packages/exception-reporting/lib/main.js @@ -1,6 +1,6 @@ /** @babel */ -import {CompositeDisposable} from 'atom' +import { CompositeDisposable } from 'atom' let reporter @@ -13,36 +13,44 @@ function getReporter () { } export default { - activate() { + activate () { this.subscriptions = new CompositeDisposable() if (!atom.config.get('exception-reporting.userId')) { atom.config.set('exception-reporting.userId', require('node-uuid').v4()) } - this.subscriptions.add(atom.onDidThrowError(({message, url, line, column, originalError}) => { - try { - getReporter().reportUncaughtException(originalError) - } catch (secondaryException) { + this.subscriptions.add( + atom.onDidThrowError(({ message, url, line, column, originalError }) => { try { - console.error("Error reporting uncaught exception", secondaryException) - getReporter().reportUncaughtException(secondaryException) - } catch (error) { } - } - }) - ) - - if (atom.onDidFailAssertion != null) { - this.subscriptions.add(atom.onDidFailAssertion(error => { - try { - getReporter().reportFailedAssertion(error) + getReporter().reportUncaughtException(originalError) } catch (secondaryException) { try { - console.error("Error reporting assertion failure", secondaryException) + console.error( + 'Error reporting uncaught exception', + secondaryException + ) getReporter().reportUncaughtException(secondaryException) } catch (error) {} } }) + ) + + if (atom.onDidFailAssertion != null) { + this.subscriptions.add( + atom.onDidFailAssertion(error => { + try { + getReporter().reportFailedAssertion(error) + } catch (secondaryException) { + try { + console.error( + 'Error reporting assertion failure', + secondaryException + ) + getReporter().reportUncaughtException(secondaryException) + } catch (error) {} + } + }) ) } } diff --git a/packages/exception-reporting/lib/reporter.js b/packages/exception-reporting/lib/reporter.js index 42886c6ba..e4becff04 100644 --- a/packages/exception-reporting/lib/reporter.js +++ b/packages/exception-reporting/lib/reporter.js @@ -1,6 +1,5 @@ /** @babel */ -import _ from 'underscore-plus' import os from 'os' import stackTrace from 'stack-trace' import fs from 'fs-plus' @@ -13,9 +12,15 @@ const StackTraceCache = new WeakMap() export default class Reporter { constructor (params = {}) { this.request = params.request || window.fetch - this.alwaysReport = params.hasOwnProperty('alwaysReport') ? params.alwaysReport : false - this.reportPreviousErrors = params.hasOwnProperty('reportPreviousErrors') ? params.reportPreviousErrors : true - this.resourcePath = this.normalizePath(params.resourcePath || process.resourcesPath) + this.alwaysReport = params.hasOwnProperty('alwaysReport') + ? params.alwaysReport + : false + this.reportPreviousErrors = params.hasOwnProperty('reportPreviousErrors') + ? params.reportPreviousErrors + : true + this.resourcePath = this.normalizePath( + params.resourcePath || process.resourcesPath + ) this.reportedErrors = [] this.reportedAssertionFailures = [] } @@ -28,22 +33,24 @@ export default class Reporter { version: LIB_VERSION, url: 'https://www.atom.io' }, - events: [{ - payloadVersion: "2", - exceptions: [this.buildExceptionJSON(error, params.projectRoot)], - severity: params.severity, - user: { - id: params.userId - }, - app: { - version: params.appVersion, - releaseStage: params.releaseStage - }, - device: { - osVersion: params.osVersion - }, - metaData: error.metadata - }] + events: [ + { + payloadVersion: '2', + exceptions: [this.buildExceptionJSON(error, params.projectRoot)], + severity: params.severity, + user: { + id: params.userId + }, + app: { + version: params.appVersion, + releaseStage: params.releaseStage + }, + device: { + osVersion: params.osVersion + }, + metaData: error.metadata + } + ] } } @@ -59,7 +66,8 @@ export default class Reporter { return this.parseStackTrace(error).map(callSite => { return { file: this.scrubPath(callSite.getFileName()), - method: callSite.getMethodName() || callSite.getFunctionName() || "none", + method: + callSite.getMethodName() || callSite.getFunctionName() || 'none', lineNumber: callSite.getLineNumber(), columnNumber: callSite.getColumnNumber(), inProject: !/node_modules/.test(callSite.getFileName()) @@ -69,8 +77,8 @@ export default class Reporter { normalizePath (pathToNormalize) { return pathToNormalize - .replace('file:///', '') // Sometimes it's a uri - .replace(/\\/g, '/') // Unify path separators across Win/macOS/Linux + .replace('file:///', '') // Sometimes it's a uri + .replace(/\\/g, '/') // Unify path separators across Win/macOS/Linux } scrubPath (pathToScrub) { @@ -96,17 +104,17 @@ export default class Reporter { } getReleaseChannel (version) { - return (version.indexOf('beta') > -1) + return version.indexOf('beta') > -1 ? 'beta' - : (version.indexOf('dev') > -1) - ? 'dev' - : 'stable' + : version.indexOf('dev') > -1 + ? 'dev' + : 'stable' } performRequest (json) { this.request.call(null, 'https://notify.bugsnag.com', { method: 'POST', - headers: new Headers({'Content-Type': 'application/json'}), + headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify(json) }) } @@ -118,7 +126,10 @@ export default class Reporter { const topFrame = this.parseStackTrace(error)[0] const fileName = topFrame ? topFrame.getFileName() : null - return fileName && (this.isBundledFile(fileName) || this.isTeletypeFile(fileName)) + return ( + fileName && + (this.isBundledFile(fileName) || this.isTeletypeFile(fileName)) + ) } parseStackTrace (error) { @@ -169,21 +180,24 @@ export default class Reporter { notification = atom.notifications.addInfo(message, { detail: error.privateMetadataDescription, - description: "Are you willing to submit this information to a private server for debugging purposes?", + description: + 'Are you willing to submit this information to a private server for debugging purposes?', dismissable: true, buttons: [ { - text: "No", + text: 'No', onDidClick: reportWithoutPrivateMetadata }, { - text: "Yes, Submit for Debugging", + text: 'Yes, Submit for Debugging', onDidClick: reportWithPrivateMetadata } ] }) - dismissSubscription = notification.onDidDismiss(reportWithoutPrivateMetadata) + dismissSubscription = notification.onDidDismiss( + reportWithoutPrivateMetadata + ) } addPackageMetadata (error) { @@ -200,7 +214,9 @@ export default class Reporter { } } - if (error.metadata == null) { error.metadata = {} } + if (error.metadata == null) { + error.metadata = {} + } error.metadata.bundledPackages = bundledPackages error.metadata.userPackages = userPackages } @@ -209,8 +225,12 @@ export default class Reporter { addPreviousErrorsMetadata (error) { if (!this.reportPreviousErrors) return if (!error.metadata) error.metadata = {} - error.metadata.previousErrors = this.reportedErrors.map(error => error.message) - error.metadata.previousAssertionFailures = this.reportedAssertionFailures.map(error => error.message) + error.metadata.previousErrors = this.reportedErrors.map( + error => error.message + ) + error.metadata.previousAssertionFailures = this.reportedAssertionFailures.map( + error => error.message + ) } reportUncaughtException (error) { @@ -219,13 +239,20 @@ export default class Reporter { this.addPackageMetadata(error) this.addPreviousErrorsMetadata(error) - if ((error.privateMetadata != null) && (error.privateMetadataDescription != null)) { - this.requestPrivateMetadataConsent(error, "The Atom team would like to collect the following information to resolve this error:", error => this.reportUncaughtException(error)) + if ( + error.privateMetadata != null && + error.privateMetadataDescription != null + ) { + this.requestPrivateMetadataConsent( + error, + 'The Atom team would like to collect the following information to resolve this error:', + error => this.reportUncaughtException(error) + ) return } let params = this.getDefaultNotificationParams() - params.severity = "error" + params.severity = 'error' this.performRequest(this.buildNotificationJSON(error, params)) this.reportedErrors.push(error) } @@ -236,13 +263,20 @@ export default class Reporter { this.addPackageMetadata(error) this.addPreviousErrorsMetadata(error) - if ((error.privateMetadata != null) && (error.privateMetadataDescription != null)) { - this.requestPrivateMetadataConsent(error, "The Atom team would like to collect some information to resolve an unexpected condition:", error => this.reportFailedAssertion(error)) + if ( + error.privateMetadata != null && + error.privateMetadataDescription != null + ) { + this.requestPrivateMetadataConsent( + error, + 'The Atom team would like to collect some information to resolve an unexpected condition:', + error => this.reportFailedAssertion(error) + ) return } let params = this.getDefaultNotificationParams() - params.severity = "warning" + params.severity = 'warning' this.performRequest(this.buildNotificationJSON(error, params)) this.reportedAssertionFailures.push(error) } @@ -258,10 +292,11 @@ export default class Reporter { isTeletypeFile (fileName) { const teletypePath = atom.packages.resolvePackagePath('teletype') - return teletypePath && this.normalizePath(fileName).indexOf(teletypePath) === 0 + return ( + teletypePath && this.normalizePath(fileName).indexOf(teletypePath) === 0 + ) } } - Reporter.API_KEY = API_KEY Reporter.LIB_VERSION = LIB_VERSION diff --git a/packages/exception-reporting/spec/reporter-spec.js b/packages/exception-reporting/spec/reporter-spec.js index bcfdf8925..615868f3b 100644 --- a/packages/exception-reporting/spec/reporter-spec.js +++ b/packages/exception-reporting/spec/reporter-spec.js @@ -6,25 +6,31 @@ const fs = require('fs-plus') let osVersion = `${os.platform()}-${os.arch()}-${os.release()}` let getReleaseChannel = version => { - return (version.indexOf('beta') > -1) + return version.indexOf('beta') > -1 ? 'beta' - : (version.indexOf('dev') > -1) + : version.indexOf('dev') > -1 ? 'dev' : 'stable' } -describe("Reporter", () => { - let reporter, requests, initialStackTraceLimit, initialFsGetHomeDirectory, mockActivePackages +describe('Reporter', () => { + let reporter, + requests, + initialStackTraceLimit, + initialFsGetHomeDirectory, + mockActivePackages beforeEach(() => { reporter = new Reporter({ - request: (url, options) => requests.push(Object.assign({url}, options)), + request: (url, options) => requests.push(Object.assign({ url }, options)), alwaysReport: true, reportPreviousErrors: false }) requests = [] mockActivePackages = [] - spyOn(atom.packages, 'getActivePackages').andCallFake(() => mockActivePackages) + spyOn(atom.packages, 'getActivePackages').andCallFake( + () => mockActivePackages + ) initialStackTraceLimit = Error.stackTraceLimit Error.stackTraceLimit = 1 @@ -37,11 +43,12 @@ describe("Reporter", () => { Error.stackTraceLimit = initialStackTraceLimit }) - describe(".reportUncaughtException(error)", () => { - it("posts errors originated inside Atom Core to BugSnag", () => { + describe('.reportUncaughtException(error)', () => { + it('posts errors originated inside Atom Core to BugSnag', () => { const repositoryRootPath = path.join(__dirname, '..') reporter = new Reporter({ - request: (url, options) => requests.push(Object.assign({url}, options)), + request: (url, options) => + requests.push(Object.assign({ url }, options)), alwaysReport: true, reportPreviousErrors: false, resourcePath: repositoryRootPath @@ -50,111 +57,123 @@ describe("Reporter", () => { let error = new Error() Error.captureStackTrace(error) reporter.reportUncaughtException(error) - let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s)) + let [lineNumber, columnNumber] = error.stack + .match(/.js:(\d+):(\d+)/) + .slice(1) + .map(s => parseInt(s)) expect(requests.length).toBe(1) let [request] = requests - expect(request.method).toBe("POST") - expect(request.url).toBe("https://notify.bugsnag.com") - expect(request.headers.get("Content-Type")).toBe("application/json") + expect(request.method).toBe('POST') + expect(request.url).toBe('https://notify.bugsnag.com') + expect(request.headers.get('Content-Type')).toBe('application/json') let body = JSON.parse(request.body) // Delete `inProject` field because tests may fail when run as part of Atom core // (i.e. when this test file will be located under `node_modules/exception-reporting/spec`) delete body.events[0].exceptions[0].stacktrace[0].inProject expect(body).toEqual({ - "apiKey": Reporter.API_KEY, - "notifier": { - "name": "Atom", - "version": Reporter.LIB_VERSION, - "url": "https://www.atom.io" + apiKey: Reporter.API_KEY, + notifier: { + name: 'Atom', + version: Reporter.LIB_VERSION, + url: 'https://www.atom.io' }, - "events": [ + events: [ { - "payloadVersion": "2", - "exceptions": [ + payloadVersion: '2', + exceptions: [ { - "errorClass": "Error", - "message": "", - "stacktrace": [ + errorClass: 'Error', + message: '', + stacktrace: [ { - "method": semver.gt(process.versions.electron, '1.6.0') ? 'Spec.it' : 'it', - "file": "spec/reporter-spec.js", - "lineNumber": lineNumber, - "columnNumber": columnNumber + method: semver.gt(process.versions.electron, '1.6.0') + ? 'Spec.it' + : 'it', + file: 'spec/reporter-spec.js', + lineNumber: lineNumber, + columnNumber: columnNumber } ] } ], - "severity": "error", - "user": {}, - "app": { - "version": atom.getVersion(), - "releaseStage": getReleaseChannel(atom.getVersion()) + severity: 'error', + user: {}, + app: { + version: atom.getVersion(), + releaseStage: getReleaseChannel(atom.getVersion()) }, - "device": { - "osVersion": osVersion + device: { + osVersion: osVersion } } ] - });}) + }) + }) - it("posts errors originated outside Atom Core to BugSnag", () => { + it('posts errors originated outside Atom Core to BugSnag', () => { fs.getHomeDirectory = () => path.join(__dirname, '..', '..') let error = new Error() Error.captureStackTrace(error) reporter.reportUncaughtException(error) - let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s)) + let [lineNumber, columnNumber] = error.stack + .match(/.js:(\d+):(\d+)/) + .slice(1) + .map(s => parseInt(s)) expect(requests.length).toBe(1) let [request] = requests - expect(request.method).toBe("POST") - expect(request.url).toBe("https://notify.bugsnag.com") - expect(request.headers.get("Content-Type")).toBe("application/json") + expect(request.method).toBe('POST') + expect(request.url).toBe('https://notify.bugsnag.com') + expect(request.headers.get('Content-Type')).toBe('application/json') let body = JSON.parse(request.body) // Delete `inProject` field because tests may fail when run as part of Atom core // (i.e. when this test file will be located under `node_modules/exception-reporting/spec`) delete body.events[0].exceptions[0].stacktrace[0].inProject expect(body).toEqual({ - "apiKey": Reporter.API_KEY, - "notifier": { - "name": "Atom", - "version": Reporter.LIB_VERSION, - "url": "https://www.atom.io" + apiKey: Reporter.API_KEY, + notifier: { + name: 'Atom', + version: Reporter.LIB_VERSION, + url: 'https://www.atom.io' }, - "events": [ + events: [ { - "payloadVersion": "2", - "exceptions": [ + payloadVersion: '2', + exceptions: [ { - "errorClass": "Error", - "message": "", - "stacktrace": [ + errorClass: 'Error', + message: '', + stacktrace: [ { - "method": semver.gt(process.versions.electron, '1.6.0') ? 'Spec.it' : 'it', - "file": '~/exception-reporting/spec/reporter-spec.js', - "lineNumber": lineNumber, - "columnNumber": columnNumber + method: semver.gt(process.versions.electron, '1.6.0') + ? 'Spec.it' + : 'it', + file: '~/exception-reporting/spec/reporter-spec.js', + lineNumber: lineNumber, + columnNumber: columnNumber } ] } ], - "severity": "error", - "user": {}, - "app": { - "version": atom.getVersion(), - "releaseStage": getReleaseChannel(atom.getVersion()) + severity: 'error', + user: {}, + app: { + version: atom.getVersion(), + releaseStage: getReleaseChannel(atom.getVersion()) }, - "device": { - "osVersion": osVersion + device: { + osVersion: osVersion } } ] - });}) + }) + }) - describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => { + describe('when the error object has `privateMetadata` and `privateMetadataDescription` fields', () => { let [error, notification] = [] beforeEach(() => { @@ -164,17 +183,17 @@ describe("Reporter", () => { error = new Error() Error.captureStackTrace(error) - error.metadata = {foo: "bar"} - error.privateMetadata = {baz: "quux"} - error.privateMetadataDescription = "The contents of baz" + error.metadata = { foo: 'bar' } + error.privateMetadata = { baz: 'quux' } + error.privateMetadataDescription = 'The contents of baz' }) - it("posts a notification asking for consent", () => { + it('posts a notification asking for consent', () => { reporter.reportUncaughtException(error) expect(atom.notifications.addInfo).toHaveBeenCalled() }) - it("submits the error with the private metadata if the user consents", () => { + it('submits the error with the private metadata if the user consents', () => { spyOn(reporter, 'reportUncaughtException').andCallThrough() reporter.reportUncaughtException(error) reporter.reportUncaughtException.reset() @@ -189,12 +208,12 @@ describe("Reporter", () => { expect(reporter.reportUncaughtException.callCount).toBe(1) expect(error.privateMetadata).toBeUndefined() expect(error.privateMetadataDescription).toBeUndefined() - expect(error.metadata).toEqual({foo: "bar", baz: "quux"}) + expect(error.metadata).toEqual({ foo: 'bar', baz: 'quux' }) expect(notification.isDismissed()).toBe(true) }) - it("submits the error without the private metadata if the user does not consent", () => { + it('submits the error without the private metadata if the user does not consent', () => { spyOn(reporter, 'reportUncaughtException').andCallThrough() reporter.reportUncaughtException(error) reporter.reportUncaughtException.reset() @@ -209,12 +228,12 @@ describe("Reporter", () => { expect(reporter.reportUncaughtException.callCount).toBe(1) expect(error.privateMetadata).toBeUndefined() expect(error.privateMetadataDescription).toBeUndefined() - expect(error.metadata).toEqual({foo: "bar"}) + expect(error.metadata).toEqual({ foo: 'bar' }) expect(notification.isDismissed()).toBe(true) }) - it("submits the error without the private metadata if the user dismisses the notification", () => { + it('submits the error without the private metadata if the user dismisses the notification', () => { spyOn(reporter, 'reportUncaughtException').andCallThrough() reporter.reportUncaughtException(error) reporter.reportUncaughtException.reset() @@ -226,14 +245,34 @@ describe("Reporter", () => { expect(reporter.reportUncaughtException.callCount).toBe(1) expect(error.privateMetadata).toBeUndefined() expect(error.privateMetadataDescription).toBeUndefined() - expect(error.metadata).toEqual({foo: "bar"});});}) + expect(error.metadata).toEqual({ foo: 'bar' }) + }) + }) it('treats packages located in atom.packages.getPackageDirPaths as user packages', () => { mockActivePackages = [ - {name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}}, - {name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}}, - {name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}}, - {name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}}, + { + name: 'user-1', + path: '/Users/user/.atom/packages/user-1', + metadata: { version: '1.0.0' } + }, + { + name: 'user-2', + path: '/Users/user/.atom/packages/user-2', + metadata: { version: '1.2.0' } + }, + { + name: 'bundled-1', + path: + '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', + metadata: { version: '1.0.0' } + }, + { + name: 'bundled-2', + path: + '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', + metadata: { version: '1.2.0' } + } ] const packageDirPaths = ['/Users/user/.atom/packages'] @@ -269,69 +308,78 @@ describe("Reporter", () => { const lastRequest = requests[requests.length - 1] const body = JSON.parse(lastRequest.body) - console.log(body); + console.log(body) expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B']) - expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y']) + expect(body.events[0].metaData.previousAssertionFailures).toEqual([ + 'X', + 'Y' + ]) }) }) - describe(".reportFailedAssertion(error)", () => { - it("posts warnings to bugsnag", () => { + describe('.reportFailedAssertion(error)', () => { + it('posts warnings to bugsnag', () => { fs.getHomeDirectory = () => path.join(__dirname, '..', '..') let error = new Error() Error.captureStackTrace(error) reporter.reportFailedAssertion(error) - let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s)) + let [lineNumber, columnNumber] = error.stack + .match(/.js:(\d+):(\d+)/) + .slice(1) + .map(s => parseInt(s)) expect(requests.length).toBe(1) let [request] = requests - expect(request.method).toBe("POST") - expect(request.url).toBe("https://notify.bugsnag.com") - expect(request.headers.get("Content-Type")).toBe("application/json") + expect(request.method).toBe('POST') + expect(request.url).toBe('https://notify.bugsnag.com') + expect(request.headers.get('Content-Type')).toBe('application/json') let body = JSON.parse(request.body) // Delete `inProject` field because tests may fail when run as part of Atom core // (i.e. when this test file will be located under `node_modules/exception-reporting/spec`) delete body.events[0].exceptions[0].stacktrace[0].inProject expect(body).toEqual({ - "apiKey": Reporter.API_KEY, - "notifier": { - "name": "Atom", - "version": Reporter.LIB_VERSION, - "url": "https://www.atom.io" + apiKey: Reporter.API_KEY, + notifier: { + name: 'Atom', + version: Reporter.LIB_VERSION, + url: 'https://www.atom.io' }, - "events": [ + events: [ { - "payloadVersion": "2", - "exceptions": [ + payloadVersion: '2', + exceptions: [ { - "errorClass": "Error", - "message": "", - "stacktrace": [ + errorClass: 'Error', + message: '', + stacktrace: [ { - "method": semver.gt(process.versions.electron, '1.6.0') ? 'Spec.it' : 'it', - "file": '~/exception-reporting/spec/reporter-spec.js', - "lineNumber": lineNumber, - "columnNumber": columnNumber + method: semver.gt(process.versions.electron, '1.6.0') + ? 'Spec.it' + : 'it', + file: '~/exception-reporting/spec/reporter-spec.js', + lineNumber: lineNumber, + columnNumber: columnNumber } ] } ], - "severity": "warning", - "user": {}, - "app": { - "version": atom.getVersion(), - "releaseStage": getReleaseChannel(atom.getVersion()) + severity: 'warning', + user: {}, + app: { + version: atom.getVersion(), + releaseStage: getReleaseChannel(atom.getVersion()) }, - "device": { - "osVersion": osVersion + device: { + osVersion: osVersion } } ] - });}) + }) + }) - describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => { + describe('when the error object has `privateMetadata` and `privateMetadataDescription` fields', () => { let [error, notification] = [] beforeEach(() => { @@ -341,17 +389,17 @@ describe("Reporter", () => { error = new Error() Error.captureStackTrace(error) - error.metadata = {foo: "bar"} - error.privateMetadata = {baz: "quux"} - error.privateMetadataDescription = "The contents of baz" + error.metadata = { foo: 'bar' } + error.privateMetadata = { baz: 'quux' } + error.privateMetadataDescription = 'The contents of baz' }) - it("posts a notification asking for consent", () => { + it('posts a notification asking for consent', () => { reporter.reportFailedAssertion(error) expect(atom.notifications.addInfo).toHaveBeenCalled() }) - it("submits the error with the private metadata if the user consents", () => { + it('submits the error with the private metadata if the user consents', () => { spyOn(reporter, 'reportFailedAssertion').andCallThrough() reporter.reportFailedAssertion(error) reporter.reportFailedAssertion.reset() @@ -366,12 +414,12 @@ describe("Reporter", () => { expect(reporter.reportFailedAssertion.callCount).toBe(1) expect(error.privateMetadata).toBeUndefined() expect(error.privateMetadataDescription).toBeUndefined() - expect(error.metadata).toEqual({foo: "bar", baz: "quux"}) + expect(error.metadata).toEqual({ foo: 'bar', baz: 'quux' }) expect(notification.isDismissed()).toBe(true) }) - it("submits the error without the private metadata if the user does not consent", () => { + it('submits the error without the private metadata if the user does not consent', () => { spyOn(reporter, 'reportFailedAssertion').andCallThrough() reporter.reportFailedAssertion(error) reporter.reportFailedAssertion.reset() @@ -386,12 +434,12 @@ describe("Reporter", () => { expect(reporter.reportFailedAssertion.callCount).toBe(1) expect(error.privateMetadata).toBeUndefined() expect(error.privateMetadataDescription).toBeUndefined() - expect(error.metadata).toEqual({foo: "bar"}) + expect(error.metadata).toEqual({ foo: 'bar' }) expect(notification.isDismissed()).toBe(true) }) - it("submits the error without the private metadata if the user dismisses the notification", () => { + it('submits the error without the private metadata if the user dismisses the notification', () => { spyOn(reporter, 'reportFailedAssertion').andCallThrough() reporter.reportFailedAssertion(error) reporter.reportFailedAssertion.reset() @@ -403,13 +451,17 @@ describe("Reporter", () => { expect(reporter.reportFailedAssertion.callCount).toBe(1) expect(error.privateMetadata).toBeUndefined() expect(error.privateMetadataDescription).toBeUndefined() - expect(error.metadata).toEqual({foo: "bar"}) + expect(error.metadata).toEqual({ foo: 'bar' }) }) it("only notifies the user once for a given 'privateMetadataRequestName'", () => { let fakeStorage = {} - spyOn(global.localStorage, 'setItem').andCallFake((key, value) => fakeStorage[key] = value) - spyOn(global.localStorage, 'getItem').andCallFake(key => fakeStorage[key]) + spyOn(global.localStorage, 'setItem').andCallFake( + (key, value) => (fakeStorage[key] = value) + ) + spyOn(global.localStorage, 'getItem').andCallFake( + key => fakeStorage[key] + ) error.privateMetadataRequestName = 'foo' @@ -423,7 +475,7 @@ describe("Reporter", () => { let error2 = new Error() Error.captureStackTrace(error2) error2.privateMetadataDescription = 'Something about you' - error2.privateMetadata = {baz: 'quux'} + error2.privateMetadata = { baz: 'quux' } error2.privateMetadataRequestName = 'bar' reporter.reportFailedAssertion(error2) @@ -433,10 +485,28 @@ describe("Reporter", () => { it('treats packages located in atom.packages.getPackageDirPaths as user packages', () => { mockActivePackages = [ - {name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}}, - {name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}}, - {name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}}, - {name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}}, + { + name: 'user-1', + path: '/Users/user/.atom/packages/user-1', + metadata: { version: '1.0.0' } + }, + { + name: 'user-2', + path: '/Users/user/.atom/packages/user-2', + metadata: { version: '1.2.0' } + }, + { + name: 'bundled-1', + path: + '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', + metadata: { version: '1.0.0' } + }, + { + name: 'bundled-2', + path: + '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', + metadata: { version: '1.2.0' } + } ] const packageDirPaths = ['/Users/user/.atom/packages'] @@ -473,7 +543,10 @@ describe("Reporter", () => { const body = JSON.parse(lastRequest.body) expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B']) - expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y']) + expect(body.events[0].metaData.previousAssertionFailures).toEqual([ + 'X', + 'Y' + ]) }) }) }) diff --git a/packages/git-diff/lib/diff-list-view.js b/packages/git-diff/lib/diff-list-view.js index 38e50880f..22f7c7d23 100644 --- a/packages/git-diff/lib/diff-list-view.js +++ b/packages/git-diff/lib/diff-list-view.js @@ -1,14 +1,13 @@ const SelectListView = require('atom-select-list') -const {repositoryForPath} = require('./helpers') +const { repositoryForPath } = require('./helpers') -module.exports = -class DiffListView { +module.exports = class DiffListView { constructor () { this.selectListView = new SelectListView({ emptyMessage: 'No diffs in file', items: [], - filterKeyForItem: (diff) => diff.lineText, - elementForItem: (diff) => { + filterKeyForItem: diff => diff.lineText, + elementForItem: diff => { const li = document.createElement('li') li.classList.add('two-lines') @@ -19,15 +18,19 @@ class DiffListView { const secondaryLine = document.createElement('div') secondaryLine.classList.add('secondary-line') - secondaryLine.textContent = `-${diff.oldStart},${diff.oldLines} +${diff.newStart},${diff.newLines}` + secondaryLine.textContent = `-${diff.oldStart},${diff.oldLines} +${ + diff.newStart + },${diff.newLines}` li.appendChild(secondaryLine) return li }, - didConfirmSelection: (diff) => { + didConfirmSelection: diff => { this.cancel() const bufferRow = diff.newStart > 0 ? diff.newStart - 1 : diff.newStart - this.editor.setCursorBufferPosition([bufferRow, 0], {autoscroll: true}) + this.editor.setCursorBufferPosition([bufferRow, 0], { + autoscroll: true + }) this.editor.moveToFirstCharacterOfLine() }, didCancelSelection: () => { @@ -35,7 +38,10 @@ class DiffListView { } }) this.selectListView.element.classList.add('diff-list-view') - this.panel = atom.workspace.addModalPanel({item: this.selectListView, visible: false}) + this.panel = atom.workspace.addModalPanel({ + item: this.selectListView, + visible: false + }) } attach () { @@ -66,7 +72,9 @@ class DiffListView { } else if (editor) { this.editor = editor const repository = repositoryForPath(this.editor.getPath()) - let diffs = repository ? repository.getLineDiffs(this.editor.getPath(), this.editor.getText()) : [] + let diffs = repository + ? repository.getLineDiffs(this.editor.getPath(), this.editor.getText()) + : [] if (!diffs) diffs = [] for (let diff of diffs) { const bufferRow = diff.newStart > 0 ? diff.newStart - 1 : diff.newStart @@ -74,7 +82,7 @@ class DiffListView { diff.lineText = lineText ? lineText.trim() : '' } - await this.selectListView.update({items: diffs}) + await this.selectListView.update({ items: diffs }) this.attach() } } diff --git a/packages/git-diff/lib/git-diff-view.js b/packages/git-diff/lib/git-diff-view.js index 1f1913eab..99d57b483 100644 --- a/packages/git-diff/lib/git-diff-view.js +++ b/packages/git-diff/lib/git-diff-view.js @@ -1,10 +1,9 @@ -const {CompositeDisposable} = require('atom') -const {repositoryForPath} = require('./helpers') +const { CompositeDisposable } = require('atom') +const { repositoryForPath } = require('./helpers') const MAX_BUFFER_LENGTH_TO_DIFF = 2 * 1024 * 1024 -module.exports = -class GitDiffView { +module.exports = class GitDiffView { constructor (editor) { this.updateDiffs = this.updateDiffs.bind(this) this.editor = editor @@ -22,10 +21,18 @@ class GitDiffView { this.editor.onDidStopChanging(this.updateDiffs), this.editor.onDidChangePath(this.updateDiffs), atom.project.onDidChangePaths(() => this.subscribeToRepository()), - atom.commands.add(editorElement, 'git-diff:move-to-next-diff', () => this.moveToNextDiff()), - atom.commands.add(editorElement, 'git-diff:move-to-previous-diff', () => this.moveToPreviousDiff()), - atom.config.onDidChange('git-diff.showIconsInEditorGutter', () => this.updateIconDecoration()), - atom.config.onDidChange('editor.showLineNumbers', () => this.updateIconDecoration()), + atom.commands.add(editorElement, 'git-diff:move-to-next-diff', () => + this.moveToNextDiff() + ), + atom.commands.add(editorElement, 'git-diff:move-to-previous-diff', () => + this.moveToPreviousDiff() + ), + atom.config.onDidChange('git-diff.showIconsInEditorGutter', () => + this.updateIconDecoration() + ), + atom.config.onDidChange('editor.showLineNumbers', () => + this.updateIconDecoration() + ), editorElement.onDidAttach(() => this.updateIconDecoration()), this.editor.onDidDestroy(() => { this.cancelUpdate() @@ -43,7 +50,7 @@ class GitDiffView { let nextDiffLineNumber = null let firstDiffLineNumber = null if (this.diffs) { - for (const {newStart} of this.diffs) { + for (const { newStart } of this.diffs) { if (newStart > cursorLineNumber) { if (nextDiffLineNumber == null) nextDiffLineNumber = newStart - 1 nextDiffLineNumber = Math.min(newStart - 1, nextDiffLineNumber) @@ -55,7 +62,10 @@ class GitDiffView { } // Wrap around to the first diff in the file - if (atom.config.get('git-diff.wrapAroundOnMoveToDiff') && nextDiffLineNumber == null) { + if ( + atom.config.get('git-diff.wrapAroundOnMoveToDiff') && + nextDiffLineNumber == null + ) { nextDiffLineNumber = firstDiffLineNumber } @@ -65,7 +75,10 @@ class GitDiffView { updateIconDecoration () { const gutter = this.editor.getElement().querySelector('.gutter') if (gutter) { - if (atom.config.get('editor.showLineNumbers') && atom.config.get('git-diff.showIconsInEditorGutter')) { + if ( + atom.config.get('editor.showLineNumbers') && + atom.config.get('git-diff.showIconsInEditorGutter') + ) { gutter.classList.add('git-diff-icon') } else { gutter.classList.remove('git-diff-icon') @@ -78,16 +91,22 @@ class GitDiffView { let previousDiffLineNumber = -1 let lastDiffLineNumber = -1 if (this.diffs) { - for (const {newStart} of this.diffs) { + for (const { newStart } of this.diffs) { if (newStart < cursorLineNumber) { - previousDiffLineNumber = Math.max(newStart - 1, previousDiffLineNumber) + previousDiffLineNumber = Math.max( + newStart - 1, + previousDiffLineNumber + ) } lastDiffLineNumber = Math.max(newStart - 1, lastDiffLineNumber) } } // Wrap around to the last diff in the file - if (atom.config.get('git-diff.wrapAroundOnMoveToDiff') && previousDiffLineNumber === -1) { + if ( + atom.config.get('git-diff.wrapAroundOnMoveToDiff') && + previousDiffLineNumber === -1 + ) { previousDiffLineNumber = lastDiffLineNumber } @@ -104,12 +123,16 @@ class GitDiffView { subscribeToRepository () { this.repository = repositoryForPath(this.editor.getPath()) if (this.repository) { - this.subscriptions.add(this.repository.onDidChangeStatuses(() => { - this.scheduleUpdate() - })) - this.subscriptions.add(this.repository.onDidChangeStatus(changedPath => { - if (changedPath === this.editor.getPath()) this.scheduleUpdate() - })) + this.subscriptions.add( + this.repository.onDidChangeStatuses(() => { + this.scheduleUpdate() + }) + ) + this.subscriptions.add( + this.repository.onDidChangeStatus(changedPath => { + if (changedPath === this.editor.getPath()) this.scheduleUpdate() + }) + ) } } @@ -126,16 +149,21 @@ class GitDiffView { if (this.editor.isDestroyed()) return this.removeDecorations() const path = this.editor && this.editor.getPath() - if (path && this.editor.getBuffer().getLength() < MAX_BUFFER_LENGTH_TO_DIFF) { - this.diffs = this.repository && this.repository.getLineDiffs(path, this.editor.getText()) + if ( + path && + this.editor.getBuffer().getLength() < MAX_BUFFER_LENGTH_TO_DIFF + ) { + this.diffs = + this.repository && + this.repository.getLineDiffs(path, this.editor.getText()) if (this.diffs) this.addDecorations(this.diffs) } } addDecorations (diffs) { - for (const {newStart, oldLines, newLines} of diffs) { + for (const { newStart, oldLines, newLines } of diffs) { const startRow = newStart - 1 - const endRow = (newStart + newLines) - 1 + const endRow = newStart + newLines - 1 if (oldLines === 0 && newLines > 0) { this.markRange(startRow, endRow, 'git-line-added') } else if (newLines === 0 && oldLines > 0) { @@ -156,8 +184,10 @@ class GitDiffView { } markRange (startRow, endRow, klass) { - const marker = this.editor.markBufferRange([[startRow, 0], [endRow, 0]], {invalidate: 'never'}) - this.editor.decorateMarker(marker, {type: 'line-number', class: klass}) + const marker = this.editor.markBufferRange([[startRow, 0], [endRow, 0]], { + invalidate: 'never' + }) + this.editor.decorateMarker(marker, { type: 'line-number', class: klass }) this.markers.push(marker) } } diff --git a/packages/git-diff/lib/main.js b/packages/git-diff/lib/main.js index d5162139b..72597b93c 100644 --- a/packages/git-diff/lib/main.js +++ b/packages/git-diff/lib/main.js @@ -11,10 +11,14 @@ module.exports = { if (watchedEditors.has(editor)) return new GitDiffView(editor).start() - atom.commands.add(atom.views.getView(editor), 'git-diff:toggle-diff-list', () => { - if (diffListView == null) diffListView = new DiffListView() - diffListView.toggle() - }) + atom.commands.add( + atom.views.getView(editor), + 'git-diff:toggle-diff-list', + () => { + if (diffListView == null) diffListView = new DiffListView() + diffListView.toggle() + } + ) watchedEditors.add(editor) editor.onDidDestroy(() => watchedEditors.delete(editor)) diff --git a/packages/git-diff/spec/diff-list-view-spec.js b/packages/git-diff/spec/diff-list-view-spec.js index 36ca97b05..8f98e68c0 100644 --- a/packages/git-diff/spec/diff-list-view-spec.js +++ b/packages/git-diff/spec/diff-list-view-spec.js @@ -8,7 +8,10 @@ describe('git-diff:toggle-diff-list', () => { beforeEach(() => { const projectPath = temp.mkdirSync('git-diff-spec-') fs.copySync(path.join(__dirname, 'fixtures', 'working-dir'), projectPath) - fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git')) + fs.moveSync( + path.join(projectPath, 'git.git'), + path.join(projectPath, '.git') + ) atom.project.setPaths([projectPath]) jasmine.attachToDOM(atom.workspace.getElement()) @@ -19,7 +22,7 @@ describe('git-diff:toggle-diff-list', () => { runs(() => { editor = atom.workspace.getActiveTextEditor() - editor.setCursorBufferPosition([4, 29]) + editor.setCursorBufferPosition([8, 30]) editor.insertText('a') atom.commands.dispatch(editor.getElement(), 'git-diff:toggle-diff-list') }) @@ -32,12 +35,15 @@ describe('git-diff:toggle-diff-list', () => { it('shows a list of all diff hunks', () => { diffListView = document.querySelector('.diff-list-view ol') - expect(diffListView.textContent).toBe('while(items.length > 0) {a-5,1 +5,1') + expect(diffListView.textContent).toBe('while (items.length > 0) {a-9,1 +9,1') }) it('moves the cursor to the selected hunk', () => { editor.setCursorBufferPosition([0, 0]) - atom.commands.dispatch(document.querySelector('.diff-list-view'), 'core:confirm') - expect(editor.getCursorBufferPosition()).toEqual([4, 4]) + atom.commands.dispatch( + document.querySelector('.diff-list-view'), + 'core:confirm' + ) + expect(editor.getCursorBufferPosition()).toEqual([8, 4]) }) }) diff --git a/packages/git-diff/spec/fixtures/working-dir/git.git/index b/packages/git-diff/spec/fixtures/working-dir/git.git/index index 7b94a9797..a15f269aa 100644 Binary files a/packages/git-diff/spec/fixtures/working-dir/git.git/index and b/packages/git-diff/spec/fixtures/working-dir/git.git/index differ diff --git a/packages/git-diff/spec/fixtures/working-dir/git.git/objects/06/5a272b55ec2ee84530dffd60b6869f7bf5d99c b/packages/git-diff/spec/fixtures/working-dir/git.git/objects/06/5a272b55ec2ee84530dffd60b6869f7bf5d99c new file mode 100644 index 000000000..03673f8db Binary files /dev/null and b/packages/git-diff/spec/fixtures/working-dir/git.git/objects/06/5a272b55ec2ee84530dffd60b6869f7bf5d99c differ diff --git a/packages/git-diff/spec/fixtures/working-dir/git.git/objects/8e/ab2e81eb8dea81ad08694c7b30ae165af89c8e b/packages/git-diff/spec/fixtures/working-dir/git.git/objects/8e/ab2e81eb8dea81ad08694c7b30ae165af89c8e new file mode 100644 index 000000000..edb8b87fa Binary files /dev/null and b/packages/git-diff/spec/fixtures/working-dir/git.git/objects/8e/ab2e81eb8dea81ad08694c7b30ae165af89c8e differ diff --git a/packages/git-diff/spec/fixtures/working-dir/git.git/objects/e7/fd5b055dcdaa93ad8f9d63ca8db5330537105f b/packages/git-diff/spec/fixtures/working-dir/git.git/objects/e7/fd5b055dcdaa93ad8f9d63ca8db5330537105f new file mode 100644 index 000000000..05ab75421 Binary files /dev/null and b/packages/git-diff/spec/fixtures/working-dir/git.git/objects/e7/fd5b055dcdaa93ad8f9d63ca8db5330537105f differ diff --git a/packages/git-diff/spec/fixtures/working-dir/git.git/refs/heads/master b/packages/git-diff/spec/fixtures/working-dir/git.git/refs/heads/master index 40efdd18b..83fe7393f 100644 --- a/packages/git-diff/spec/fixtures/working-dir/git.git/refs/heads/master +++ b/packages/git-diff/spec/fixtures/working-dir/git.git/refs/heads/master @@ -1 +1 @@ -90820108a054b6f49c0d21031313244b6f7d69dc +065a272b55ec2ee84530dffd60b6869f7bf5d99c diff --git a/packages/git-diff/spec/fixtures/working-dir/sample.js b/packages/git-diff/spec/fixtures/working-dir/sample.js index fb33b0b43..e7fd5b055 100644 --- a/packages/git-diff/spec/fixtures/working-dir/sample.js +++ b/packages/git-diff/spec/fixtures/working-dir/sample.js @@ -1,13 +1,19 @@ -var quicksort = function () { - var sort = function(items) { - if (items.length <= 1) return items; - var pivot = items.shift(), current, left = [], right = []; - while(items.length > 0) { - current = items.shift(); - current < pivot ? left.push(current) : right.push(current); - } - return sort(left).concat(pivot).concat(sort(right)); - }; +module.exports.quicksort = function () { + var sort = function (items) { + if (items.length <= 1) return items + var pivot = items.shift() + var current + var left = [] + var right = [] - return sort(Array.apply(this, arguments)); -}; \ No newline at end of file + while (items.length > 0) { + current = items.shift() + current < pivot ? left.push(current) : right.push(current) + } + return sort(left) + .concat(pivot) + .concat(sort(right)) + } + + return sort(Array.apply(this, arguments)) +} diff --git a/packages/git-diff/spec/git-diff-spec.js b/packages/git-diff/spec/git-diff-spec.js index 397693c89..badb15f76 100644 --- a/packages/git-diff/spec/git-diff-spec.js +++ b/packages/git-diff/spec/git-diff-spec.js @@ -12,12 +12,17 @@ describe('GitDiff package', () => { const otherPath = temp.mkdirSync('some-other-path-') fs.copySync(path.join(__dirname, 'fixtures', 'working-dir'), projectPath) - fs.moveSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git')) + fs.moveSync( + path.join(projectPath, 'git.git'), + path.join(projectPath, '.git') + ) atom.project.setPaths([otherPath, projectPath]) jasmine.attachToDOM(atom.workspace.getElement()) - waitsForPromise(() => atom.workspace.open(path.join(projectPath, 'sample.js'))) + waitsForPromise(() => + atom.workspace.open(path.join(projectPath, 'sample.js')) + ) runs(() => { editor = atom.workspace.getActiveTextEditor() @@ -29,11 +34,18 @@ describe('GitDiff package', () => { describe('when the editor has modified lines', () => { it('highlights the modified lines', () => { - expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(0) + expect(editorElement.querySelectorAll('.git-line-modified').length).toBe( + 0 + ) editor.insertText('a') advanceClock(editor.getBuffer().stoppedChangingDelay) - expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(1) - expect(editorElement.querySelector('.git-line-modified')).toHaveData('buffer-row', 0) + expect(editorElement.querySelectorAll('.git-line-modified').length).toBe( + 1 + ) + expect(editorElement.querySelector('.git-line-modified')).toHaveData( + 'buffer-row', + 0 + ) }) }) @@ -45,7 +57,10 @@ describe('GitDiff package', () => { editor.insertText('a') advanceClock(editor.getBuffer().stoppedChangingDelay) expect(editorElement.querySelectorAll('.git-line-added').length).toBe(1) - expect(editorElement.querySelector('.git-line-added')).toHaveData('buffer-row', 1) + expect(editorElement.querySelector('.git-line-added')).toHaveData( + 'buffer-row', + 1 + ) }) }) @@ -56,7 +71,10 @@ describe('GitDiff package', () => { editor.deleteLine() advanceClock(editor.getBuffer().stoppedChangingDelay) expect(editorElement.querySelectorAll('.git-line-removed').length).toBe(1) - expect(editorElement.querySelector('.git-line-removed')).toHaveData('buffer-row', 4) + expect(editorElement.querySelector('.git-line-removed')).toHaveData( + 'buffer-row', + 4 + ) }) }) @@ -66,29 +84,44 @@ describe('GitDiff package', () => { editor.setCursorBufferPosition([0, 0]) editor.deleteLine() advanceClock(editor.getBuffer().stoppedChangingDelay) - expect(editorElement.querySelectorAll('.git-previous-line-removed').length).toBe(1) - expect(editorElement.querySelector('.git-previous-line-removed')).toHaveData('buffer-row', 0) + expect( + editorElement.querySelectorAll('.git-previous-line-removed').length + ).toBe(1) + expect( + editorElement.querySelector('.git-previous-line-removed') + ).toHaveData('buffer-row', 0) }) }) describe('when a modified line is restored to the HEAD version contents', () => { it('removes the diff highlight', () => { - expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(0) + expect(editorElement.querySelectorAll('.git-line-modified').length).toBe( + 0 + ) editor.insertText('a') advanceClock(editor.getBuffer().stoppedChangingDelay) - expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(1) + expect(editorElement.querySelectorAll('.git-line-modified').length).toBe( + 1 + ) editor.backspace() advanceClock(editor.getBuffer().stoppedChangingDelay) - expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(0) + expect(editorElement.querySelectorAll('.git-line-modified').length).toBe( + 0 + ) }) }) describe('when a modified file is opened', () => { it('highlights the changed lines', () => { - fs.writeFileSync(path.join(projectPath, 'sample.txt'), 'Some different text.') + fs.writeFileSync( + path.join(projectPath, 'sample.txt'), + 'Some different text.' + ) let nextTick = false - waitsForPromise(() => atom.workspace.open(path.join(projectPath, 'sample.txt'))) + waitsForPromise(() => + atom.workspace.open(path.join(projectPath, 'sample.txt')) + ) runs(() => { editorElement = atom.workspace.getActiveTextEditor().getElement() @@ -101,8 +134,13 @@ describe('GitDiff package', () => { waitsFor(() => nextTick) runs(() => { - expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(1) - expect(editorElement.querySelector('.git-line-modified')).toHaveData('buffer-row', 0) + expect( + editorElement.querySelectorAll('.git-line-modified').length + ).toBe(1) + expect(editorElement.querySelector('.git-line-modified')).toHaveData( + 'buffer-row', + 0 + ) }) }) }) @@ -148,7 +186,9 @@ describe('GitDiff package', () => { }) describe('when the wrapAroundOnMoveToDiff config option is false', () => { - beforeEach(() => atom.config.set('git-diff.wrapAroundOnMoveToDiff', false)) + beforeEach(() => + atom.config.set('git-diff.wrapAroundOnMoveToDiff', false) + ) it('does not wraps around to the first/last diff in the file', () => { editor.insertText('a') @@ -177,19 +217,28 @@ describe('GitDiff package', () => { atom.config.set('git-diff.showIconsInEditorGutter', true) }) - it('the gutter has a git-diff-icon class', () => expect(editorElement.querySelector('.gutter')).toHaveClass('git-diff-icon')) + it('the gutter has a git-diff-icon class', () => + expect(editorElement.querySelector('.gutter')).toHaveClass( + 'git-diff-icon' + )) it('keeps the git-diff-icon class when editor.showLineNumbers is toggled', () => { atom.config.set('editor.showLineNumbers', false) - expect(editorElement.querySelector('.gutter')).not.toHaveClass('git-diff-icon') + expect(editorElement.querySelector('.gutter')).not.toHaveClass( + 'git-diff-icon' + ) atom.config.set('editor.showLineNumbers', true) - expect(editorElement.querySelector('.gutter')).toHaveClass('git-diff-icon') + expect(editorElement.querySelector('.gutter')).toHaveClass( + 'git-diff-icon' + ) }) it('removes the git-diff-icon class when the showIconsInEditorGutter config option set to false', () => { atom.config.set('git-diff.showIconsInEditorGutter', false) - expect(editorElement.querySelector('.gutter')).not.toHaveClass('git-diff-icon') + expect(editorElement.querySelector('.gutter')).not.toHaveClass( + 'git-diff-icon' + ) }) }) }) diff --git a/packages/git-diff/styles/git-diff.less b/packages/git-diff/styles/git-diff.less index cbff36467..0a725be22 100644 --- a/packages/git-diff/styles/git-diff.less +++ b/packages/git-diff/styles/git-diff.less @@ -21,7 +21,7 @@ atom-text-editor { left: 0; height: 0; width: 0; - content: " "; + content: ""; border: solid transparent; border-left-color: @syntax-color-removed; border-width: @size; @@ -37,22 +37,19 @@ atom-text-editor { } .gutter.git-diff-icon .line-number { - width: 100%; - border-left: none; - padding-left: 0.4em; + border-left-width: 0; + padding-left: 1.4em; // space for diff icon &:before { - .octicon-font(); + .octicon-font(); + content: ""; display: inline-block; - position: relative; - top: -.05em; - - // make sure it doesnt affect the gutter line height. - height: 0px; + position: absolute; + top: .2em; + left: .4em; + height: 0px; // make sure it doesnt affect the gutter line height. width: 1em; - content: " "; - padding-right: 0.4em; - font-size: .95em; + font-size: .75em; } &.git-line-modified:before { @@ -70,13 +67,12 @@ atom-text-editor { border: none; // reset triangle content: @dash; color: @syntax-color-removed; - position: relative; } &.git-line-removed:before { - top: .6em; + top: 1em; } &.git-previous-line-removed:before { - top: -.6em; + top: 0; } } } diff --git a/packages/go-to-line/lib/go-to-line-view.js b/packages/go-to-line/lib/go-to-line-view.js index 66759f9d9..93d174dbc 100644 --- a/packages/go-to-line/lib/go-to-line-view.js +++ b/packages/go-to-line/lib/go-to-line-view.js @@ -29,13 +29,13 @@ class GoToLineView { atom.commands.add(this.miniEditor.element, 'core:cancel', () => { this.close() }) - this.miniEditor.onWillInsertText((arg) => { + this.miniEditor.onWillInsertText(arg => { if (arg.text.match(/[^0-9:]/)) { arg.cancel() } }) this.miniEditor.onDidChange(() => { - this.navigate({keepOpen: true}) + this.navigate({ keepOpen: true }) }) } @@ -62,9 +62,11 @@ class GoToLineView { const currentRow = editor.getCursorBufferPosition().row const rowLineNumber = lineNumber.split(/:+/)[0] || '' - const row = rowLineNumber.length > 0 ? parseInt(rowLineNumber) - 1 : currentRow + const row = + rowLineNumber.length > 0 ? parseInt(rowLineNumber) - 1 : currentRow const columnLineNumber = lineNumber.split(/:+/)[1] || '' - const column = columnLineNumber.length > 0 ? parseInt(columnLineNumber) - 1 : -1 + const column = + columnLineNumber.length > 0 ? parseInt(columnLineNumber) - 1 : -1 const position = new Point(row, column) editor.setCursorBufferPosition(position) @@ -83,7 +85,10 @@ class GoToLineView { } restoreFocus () { - if (this.previouslyFocusedElement && this.previouslyFocusedElement.parentElement) { + if ( + this.previouslyFocusedElement && + this.previouslyFocusedElement.parentElement + ) { return this.previouslyFocusedElement.focus() } atom.views.getView(atom.workspace).focus() @@ -93,7 +98,8 @@ class GoToLineView { if (this.panel.isVisible() || !atom.workspace.getActiveTextEditor()) return this.storeFocusedElement() this.panel.show() - this.message.textContent = 'Enter a or : to go there. Examples: "3" for row 3 or "2:7" for row 2 and column 7' + this.message.textContent = + 'Enter a or : to go there. Examples: "3" for row 3 or "2:7" for row 2 and column 7' this.miniEditor.element.focus() } } diff --git a/packages/go-to-line/spec/fixtures/sample.js b/packages/go-to-line/spec/fixtures/sample.js index cb53d4078..17845689a 100644 --- a/packages/go-to-line/spec/fixtures/sample.js +++ b/packages/go-to-line/spec/fixtures/sample.js @@ -1,70 +1,84 @@ var quicksort = function () { - var sort = function(items) { - if (items.length <= 1) return items; - var pivot = items.shift(), current, left = [], right = []; - while(items.length > 0) { - current = items.shift(); - current < pivot ? left.push(current) : right.push(current); - } - return sort(left).concat(pivot).concat(sort(right)); - }; + var sort = function (items) { + if (items.length <= 1) return items + var pivot = items.shift() + var current + var left = [] + var right = [] - return sort(Array.apply(this, arguments)); -}; + while (items.length > 0) { + current = items.shift() + current < pivot ? left.push(current) : right.push(current) + } + return sort(left) + .concat(pivot) + .concat(sort(right)) + } + + return sort(Array.apply(this, arguments)) +} // adapted from: // https://github.com/nzakas/computer-science-in-javascript/tree/master/algorithms/sorting/merge-sort-recursive -var mergeSort function (items){ - var merge = function (left, right){ - var result = []; - var il = 0; - var ir = 0; +var mergeSort = function (items) { + var merge = function (left, right) { + var result = [] + var il = 0 + var ir = 0 - while (il < left.length && ir < right.length){ - if (left[il] < right[ir]){ - result.push(left[il++]); + while (il < left.length && ir < right.length) { + if (left[il] < right[ir]) { + result.push(left[il++]) } else { - result.push(right[ir++]); + result.push(right[ir++]) } } - return result.concat(left.slice(il)).concat(right.slice(ir)); - }; - - if (items.length < 2) { - return items; + return result.concat(left.slice(il)).concat(right.slice(ir)) } - var middle = Math.floor(items.length / 2), - left = items.slice(0, middle), - right = items.slice(middle), - params = merge(mergeSort(left), mergeSort(right)); + if (items.length < 2) { + return items + } + + var middle = Math.floor(items.length / 2) + var left = items.slice(0, middle) + var right = items.slice(middle) + var params = merge(mergeSort(left), mergeSort(right)) // Add the arguments to replace everything between 0 and last item in the array - params.unshift(0, items.length); - items.splice.apply(items, params); - return items; -}; + params.unshift(0, items.length) + items.splice.apply(items, params) + return items +} // adapted from: // https://github.com/nzakas/computer-science-in-javascript/blob/master/algorithms/sorting/bubble-sort/bubble-sort.js -var bubbleSort = function (items){ - var swap = function (items, firstIndex, secondIndex){ - var temp = items[firstIndex]; - items[firstIndex] = items[secondIndex]; - items[secondIndex] = temp; - }; +var bubbleSort = function (items) { + var swap = function (items, firstIndex, secondIndex) { + var temp = items[firstIndex] + items[firstIndex] = items[secondIndex] + items[secondIndex] = temp + } - var len = items.length, - i, j, stop; + var len = items.length + var i + var j + var stop - for (i=0; i < len; i++){ - for (j=0, stop=len-i; j < stop; j++){ - if (items[j] > items[j+1]){ - swap(items, j, j+1); + for (i = 0; i < len; i++) { + for (j = 0, stop = len - i; j < stop; j++) { + if (items[j] > items[j + 1]) { + swap(items, j, j + 1) } } } - return items; -}; + return items +} + +module.exports = { + bubbleSort, + mergeSort, + quicksort +} diff --git a/packages/go-to-line/spec/go-to-line-spec.js b/packages/go-to-line/spec/go-to-line-spec.js index 8b9f3ba92..b3440b00f 100644 --- a/packages/go-to-line/spec/go-to-line-spec.js +++ b/packages/go-to-line/spec/go-to-line-spec.js @@ -51,8 +51,8 @@ describe('GoToLine', () => { describe('when typing line numbers (auto-navigation)', () => { it('automatically scrolls to the desired line', () => { - goToLine.miniEditor.insertText('13') - expect(editor.getCursorBufferPosition()).toEqual([12, 0]) + goToLine.miniEditor.insertText('19') + expect(editor.getCursorBufferPosition()).toEqual([18, 0]) }) }) @@ -76,8 +76,12 @@ describe('GoToLine', () => { atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm') const rowsPerPage = editor.getRowsPerPage() const currentRow = editor.getCursorBufferPosition().row - 1 - expect(editor.getFirstVisibleScreenRow()).toBe(currentRow - Math.ceil(rowsPerPage / 2)) - expect(editor.getLastVisibleScreenRow()).toBe(currentRow + Math.floor(rowsPerPage / 2)) + expect(editor.getFirstVisibleScreenRow()).toBe( + currentRow - Math.ceil(rowsPerPage / 2) + ) + expect(editor.getLastVisibleScreenRow()).toBe( + currentRow + Math.floor(rowsPerPage / 2) + ) }) }) @@ -86,10 +90,10 @@ describe('GoToLine', () => { atom.commands.dispatch(editorView, 'go-to-line:toggle') expect(goToLine.panel.isVisible()).toBeTruthy() expect(goToLine.miniEditor.getText()).toBe('') - goToLine.miniEditor.insertText('71') + goToLine.miniEditor.insertText('78') atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm') expect(goToLine.panel.isVisible()).toBeFalsy() - expect(editor.getCursorBufferPosition()).toEqual([70, 0]) + expect(editor.getCursorBufferPosition()).toEqual([77, 0]) }) }) @@ -101,7 +105,7 @@ describe('GoToLine', () => { goToLine.miniEditor.insertText('3:43') atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm') expect(goToLine.panel.isVisible()).toBeFalsy() - expect(editor.getCursorBufferPosition()).toEqual([2, 40]) + expect(editor.getCursorBufferPosition()).toEqual([2, 39]) }) }) @@ -116,12 +120,12 @@ describe('GoToLine', () => { describe('when the line number entered is nested within foldes', () => { it('unfolds all folds containing the given row', () => { - expect(editor.indentationForBufferRow(6)).toEqual(3) + expect(editor.indentationForBufferRow(9)).toEqual(3) editor.foldAll() - expect(editor.screenRowForBufferRow(6)).toEqual(0) - goToLine.miniEditor.insertText('7') + expect(editor.screenRowForBufferRow(9)).toEqual(0) + goToLine.miniEditor.insertText('10') atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm') - expect(editor.getCursorBufferPosition()).toEqual([6, 6]) + expect(editor.getCursorBufferPosition()).toEqual([9, 6]) }) }) }) diff --git a/packages/grammar-selector/lib/grammar-list-view.js b/packages/grammar-selector/lib/grammar-list-view.js index 557036af5..d6812fcfc 100644 --- a/packages/grammar-selector/lib/grammar-list-view.js +++ b/packages/grammar-selector/lib/grammar-list-view.js @@ -1,14 +1,13 @@ const SelectListView = require('atom-select-list') -module.exports = -class GrammarListView { +module.exports = class GrammarListView { constructor () { - this.autoDetect = {name: 'Auto Detect'} + this.autoDetect = { name: 'Auto Detect' } this.selectListView = new SelectListView({ itemsClassList: ['mark-active'], items: [], - filterKeyForItem: (grammar) => grammar.name, - elementForItem: (grammar) => { + filterKeyForItem: grammar => grammar.name, + elementForItem: grammar => { const grammarName = grammar.name || grammar.scopeName const element = document.createElement('li') if (grammar === this.currentGrammar) { @@ -29,7 +28,7 @@ class GrammarListView { return element }, - didConfirmSelection: (grammar) => { + didConfirmSelection: grammar => { this.cancel() if (grammar === this.autoDetect) { atom.textEditors.clearGrammarOverride(this.editor) @@ -64,7 +63,7 @@ class GrammarListView { attach () { this.previouslyFocusedElement = document.activeElement if (this.panel == null) { - this.panel = atom.workspace.addModalPanel({item: this.selectListView}) + this.panel = atom.workspace.addModalPanel({ item: this.selectListView }) } this.selectListView.focus() this.selectListView.reset() @@ -80,7 +79,7 @@ class GrammarListView { this.currentGrammar = this.autoDetect } - const grammars = atom.grammars.getGrammars().filter((grammar) => { + const grammars = atom.grammars.getGrammars().filter(grammar => { return grammar !== atom.grammars.nullGrammar && grammar.name }) grammars.sort((a, b) => { @@ -97,7 +96,7 @@ class GrammarListView { } }) grammars.unshift(this.autoDetect) - await this.selectListView.update({items: grammars}) + await this.selectListView.update({ items: grammars }) this.attach() } } diff --git a/packages/grammar-selector/lib/grammar-status-view.js b/packages/grammar-selector/lib/grammar-status-view.js index 45dab6cd1..d7f13930b 100644 --- a/packages/grammar-selector/lib/grammar-status-view.js +++ b/packages/grammar-selector/lib/grammar-status-view.js @@ -1,7 +1,6 @@ -const {Disposable} = require('atom') +const { Disposable } = require('atom') -module.exports = -class GrammarStatusView { +module.exports = class GrammarStatusView { constructor (statusBar) { this.statusBar = statusBar this.element = document.createElement('grammar-selector-status') @@ -10,15 +9,25 @@ class GrammarStatusView { this.grammarLink.classList.add('inline-block') this.element.appendChild(this.grammarLink) - this.activeItemSubscription = atom.workspace.observeActiveTextEditor(this.subscribeToActiveTextEditor.bind(this)) + this.activeItemSubscription = atom.workspace.observeActiveTextEditor( + this.subscribeToActiveTextEditor.bind(this) + ) - this.configSubscription = atom.config.observe('grammar-selector.showOnRightSideOfStatusBar', this.attach.bind(this)) - const clickHandler = (event) => { + this.configSubscription = atom.config.observe( + 'grammar-selector.showOnRightSideOfStatusBar', + this.attach.bind(this) + ) + const clickHandler = event => { event.preventDefault() - atom.commands.dispatch(atom.views.getView(atom.workspace.getActiveTextEditor()), 'grammar-selector:show') + atom.commands.dispatch( + atom.views.getView(atom.workspace.getActiveTextEditor()), + 'grammar-selector:show' + ) } this.element.addEventListener('click', clickHandler) - this.clickSubscription = new Disposable(() => { this.element.removeEventListener('click', clickHandler) }) + this.clickSubscription = new Disposable(() => { + this.element.removeEventListener('click', clickHandler) + }) } attach () { @@ -27,8 +36,8 @@ class GrammarStatusView { } this.tile = atom.config.get('grammar-selector.showOnRightSideOfStatusBar') - ? this.statusBar.addRightTile({item: this.element, priority: 10}) - : this.statusBar.addLeftTile({item: this.element, priority: 10}) + ? this.statusBar.addRightTile({ item: this.element, priority: 10 }) + : this.statusBar.addLeftTile({ item: this.element, priority: 10 }) } destroy () { @@ -65,7 +74,9 @@ class GrammarStatusView { const editor = atom.workspace.getActiveTextEditor() if (editor) { - this.grammarSubscription = editor.onDidChangeGrammar(this.updateGrammarText.bind(this)) + this.grammarSubscription = editor.onDidChangeGrammar( + this.updateGrammarText.bind(this) + ) } this.updateGrammarText() } @@ -92,7 +103,9 @@ class GrammarStatusView { this.grammarLink.dataset.grammar = grammarName this.element.style.display = '' - this.tooltip = atom.tooltips.add(this.element, {title: `File uses the ${grammarName} grammar`}) + this.tooltip = atom.tooltips.add(this.element, { + title: `File uses the ${grammarName} grammar` + }) } else { this.element.style.display = 'none' } diff --git a/packages/grammar-selector/lib/main.js b/packages/grammar-selector/lib/main.js index 6f95a44e6..428108dd4 100644 --- a/packages/grammar-selector/lib/main.js +++ b/packages/grammar-selector/lib/main.js @@ -7,10 +7,14 @@ let grammarStatusView = null module.exports = { activate () { - commandDisposable = atom.commands.add('atom-text-editor', 'grammar-selector:show', () => { - if (!grammarListView) grammarListView = new GrammarListView() - grammarListView.toggle() - }) + commandDisposable = atom.commands.add( + 'atom-text-editor', + 'grammar-selector:show', + () => { + if (!grammarListView) grammarListView = new GrammarListView() + grammarListView.toggle() + } + ) }, deactivate () { diff --git a/packages/grammar-selector/spec/async-spec-helpers.js b/packages/grammar-selector/spec/async-spec-helpers.js deleted file mode 100644 index 61eeaab39..000000000 --- a/packages/grammar-selector/spec/async-spec-helpers.js +++ /dev/null @@ -1,42 +0,0 @@ -exports.beforeEach = function beforeEach (fn) { - global.beforeEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise('beforeEach promise', result) - } - }) -} - -exports.afterEach = function afterEach (fn) { - global.afterEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise('afterEach promise', result) - } - }) -} - -;['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { - exports[name] = function (description, fn) { - if (fn === undefined) { - global[name](description) - return - } - - global[name](description, function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise('test promise', result) - } - }) - } -}) - -function waitsForPromise (message, promise) { - global.waitsFor(message, (done) => { - promise.then(done, (error) => { - jasmine.getEnv().currentSpec.fail(error) - done() - }) - }) -} diff --git a/packages/grammar-selector/spec/grammar-selector-spec.js b/packages/grammar-selector/spec/grammar-selector-spec.js index 21ea63d38..400fd6241 100644 --- a/packages/grammar-selector/spec/grammar-selector-spec.js +++ b/packages/grammar-selector/spec/grammar-selector-spec.js @@ -1,6 +1,5 @@ const path = require('path') const SelectListView = require('atom-select-list') -const {it, fit, ffit, beforeEach, afterEach} = require('./async-spec-helpers') // eslint-disable-line describe('GrammarSelector', () => { let [editor, textGrammar, jsGrammar] = [] @@ -13,7 +12,9 @@ describe('GrammarSelector', () => { await atom.packages.activatePackage('grammar-selector') await atom.packages.activatePackage('language-text') await atom.packages.activatePackage('language-javascript') - await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'language-with-no-name')) + await atom.packages.activatePackage( + path.join(__dirname, 'fixtures', 'language-with-no-name') + ) editor = await atom.workspace.open('sample.js') @@ -33,15 +34,24 @@ describe('GrammarSelector', () => { // TODO: Remove once Atom 1.23 reaches stable if (parseFloat(atom.getVersion()) >= 1.23) { // Do not take into account the two JS regex grammars or language-with-no-name - expect(grammarView.querySelectorAll('li').length).toBe(atom.grammars.grammars.length - 3) + expect(grammarView.querySelectorAll('li').length).toBe( + atom.grammars.grammars.length - 3 + ) } else { - expect(grammarView.querySelectorAll('li').length).toBe(atom.grammars.grammars.length - 1) + expect(grammarView.querySelectorAll('li').length).toBe( + atom.grammars.grammars.length - 1 + ) } - expect(grammarView.querySelectorAll('li')[0].textContent).toBe('Auto Detect') + expect(grammarView.querySelectorAll('li')[0].textContent).toBe( + 'Auto Detect' + ) expect(grammarView.textContent.includes('source.a')).toBe(false) - grammarView.querySelectorAll('li').forEach(li => expect(li.textContent).not.toBe(atom.grammars.nullGrammar.name)) - }) - ) + grammarView + .querySelectorAll('li') + .forEach(li => + expect(li.textContent).not.toBe(atom.grammars.nullGrammar.name) + ) + })) describe('when a grammar is selected', () => it('sets the new grammar on the editor', async () => { @@ -51,8 +61,7 @@ describe('GrammarSelector', () => { const grammarView = atom.workspace.getModalPanels()[0].getItem() grammarView.props.didConfirmSelection(textGrammar) expect(editor.getGrammar()).toBe(textGrammar) - }) - ) + })) describe('when auto-detect is selected', () => it('restores the auto-detected grammar on the editor', async () => { @@ -69,8 +78,7 @@ describe('GrammarSelector', () => { grammarView = atom.workspace.getModalPanels()[0].getItem() grammarView.props.didConfirmSelection(grammarView.items[0]) expect(editor.getGrammar()).toBe(jsGrammar) - }) - ) + })) describe("when the editor's current grammar is the null grammar", () => it('displays Auto Detect as the selected grammar', async () => { @@ -79,9 +87,10 @@ describe('GrammarSelector', () => { await SelectListView.getScheduler().getNextUpdatePromise() const grammarView = atom.workspace.getModalPanels()[0].getItem().element - expect(grammarView.querySelector('li.active').textContent).toBe('Auto Detect') - }) - ) + expect(grammarView.querySelector('li.active').textContent).toBe( + 'Auto Detect' + ) + })) describe('when editor is untitled', () => it('sets the new grammar on the editor', async () => { @@ -94,8 +103,7 @@ describe('GrammarSelector', () => { const grammarView = atom.workspace.getModalPanels()[0].getItem() grammarView.props.didConfirmSelection(jsGrammar) expect(editor.getGrammar()).toBe(jsGrammar) - }) - ) + })) describe('Status bar grammar label', () => { let [grammarStatus, grammarTile, statusBar] = [] @@ -114,7 +122,9 @@ describe('GrammarSelector', () => { it('displays the name of the current grammar', () => { expect(grammarStatus.querySelector('a').textContent).toBe('JavaScript') - expect(getTooltipText(grammarStatus)).toBe('File uses the JavaScript grammar') + expect(getTooltipText(grammarStatus)).toBe( + 'File uses the JavaScript grammar' + ) }) it('displays Plain Text when the current grammar is the null grammar', async () => { @@ -123,7 +133,9 @@ describe('GrammarSelector', () => { expect(grammarStatus.querySelector('a').textContent).toBe('Plain Text') expect(grammarStatus).toBeVisible() - expect(getTooltipText(grammarStatus)).toBe('File uses the Plain Text grammar') + expect(getTooltipText(grammarStatus)).toBe( + 'File uses the Plain Text grammar' + ) editor.setGrammar(atom.grammars.grammarForScopeName('source.js')) await atom.views.getNextUpdatePromise() @@ -142,20 +154,31 @@ describe('GrammarSelector', () => { describe('when the grammar-selector.showOnRightSideOfStatusBar setting changes', () => it('moves the item to the preferred side of the status bar', () => { - expect(statusBar.getLeftTiles().map(tile => tile.getItem())).toContain(grammarStatus) - expect(statusBar.getRightTiles().map(tile => tile.getItem())).not.toContain(grammarStatus) + expect(statusBar.getLeftTiles().map(tile => tile.getItem())).toContain( + grammarStatus + ) + expect( + statusBar.getRightTiles().map(tile => tile.getItem()) + ).not.toContain(grammarStatus) atom.config.set('grammar-selector.showOnRightSideOfStatusBar', true) - expect(statusBar.getLeftTiles().map(tile => tile.getItem())).not.toContain(grammarStatus) - expect(statusBar.getRightTiles().map(tile => tile.getItem())).toContain(grammarStatus) + expect( + statusBar.getLeftTiles().map(tile => tile.getItem()) + ).not.toContain(grammarStatus) + expect(statusBar.getRightTiles().map(tile => tile.getItem())).toContain( + grammarStatus + ) atom.config.set('grammar-selector.showOnRightSideOfStatusBar', false) - expect(statusBar.getLeftTiles().map(tile => tile.getItem())).toContain(grammarStatus) - expect(statusBar.getRightTiles().map(tile => tile.getItem())).not.toContain(grammarStatus) - }) - ) + expect(statusBar.getLeftTiles().map(tile => tile.getItem())).toContain( + grammarStatus + ) + expect( + statusBar.getRightTiles().map(tile => tile.getItem()) + ).not.toContain(grammarStatus) + })) describe("when the editor's grammar changes", () => it('displays the new grammar of the editor', async () => { @@ -163,32 +186,37 @@ describe('GrammarSelector', () => { await atom.views.getNextUpdatePromise() expect(grammarStatus.querySelector('a').textContent).toBe('Plain Text') - expect(getTooltipText(grammarStatus)).toBe('File uses the Plain Text grammar') + expect(getTooltipText(grammarStatus)).toBe( + 'File uses the Plain Text grammar' + ) editor.setGrammar(atom.grammars.grammarForScopeName('source.a')) await atom.views.getNextUpdatePromise() expect(grammarStatus.querySelector('a').textContent).toBe('source.a') - expect(getTooltipText(grammarStatus)).toBe('File uses the source.a grammar') - }) - ) + expect(getTooltipText(grammarStatus)).toBe( + 'File uses the source.a grammar' + ) + })) describe('when clicked', () => it('shows the grammar selector modal', () => { const eventHandler = jasmine.createSpy('eventHandler') - atom.commands.add(editor.getElement(), 'grammar-selector:show', eventHandler) + atom.commands.add( + editor.getElement(), + 'grammar-selector:show', + eventHandler + ) grammarStatus.click() expect(eventHandler).toHaveBeenCalled() - }) - ) + })) describe('when the package is deactivated', () => it('removes the view', () => { spyOn(grammarTile, 'destroy') atom.packages.deactivatePackage('grammar-selector') expect(grammarTile.destroy).toHaveBeenCalled() - }) - ) + })) }) }) diff --git a/packages/incompatible-packages/lib/incompatible-packages-component.js b/packages/incompatible-packages/lib/incompatible-packages-component.js index 8eb6c62c6..478e9682d 100644 --- a/packages/incompatible-packages/lib/incompatible-packages-component.js +++ b/packages/incompatible-packages/lib/incompatible-packages-component.js @@ -1,7 +1,6 @@ /** @babel */ /** @jsx etch.dom */ -import {BufferedProcess} from 'atom' import etch from 'etch' import VIEW_URI from './view-uri' @@ -11,8 +10,8 @@ const REBUILD_SUCCEEDED = 'rebuild-succeeded' export default class IncompatiblePackagesComponent { constructor (packageManager) { - this.rebuildStatuses = new Map - this.rebuildFailureOutputs = new Map + this.rebuildStatuses = new Map() + this.rebuildFailureOutputs = new Map() this.rebuildInProgress = false this.rebuiltPackageCount = 0 this.packageManager = packageManager @@ -25,13 +24,15 @@ export default class IncompatiblePackagesComponent { global.setImmediate(this.populateIncompatiblePackages.bind(this)) } - this.element.addEventListener('click', (event) => { + this.element.addEventListener('click', event => { if (event.target === this.refs.rebuildButton) { this.rebuildIncompatiblePackages() } else if (event.target === this.refs.reloadButton) { atom.reload() } else if (event.target.classList.contains('view-settings')) { - atom.workspace.open(`atom://config/packages/${event.target.package.name}`) + atom.workspace.open( + `atom://config/packages/${event.target.package.name}` + ) } }) } @@ -44,7 +45,10 @@ export default class IncompatiblePackagesComponent { } return ( -
    +
    {this.renderHeading()} {this.renderIncompatiblePackageList()}
    @@ -55,15 +59,14 @@ export default class IncompatiblePackagesComponent { if (this.incompatiblePackages.length > 0) { if (this.rebuiltPackageCount > 0) { let alertClass = - (this.rebuiltPackageCount === this.incompatiblePackages.length) + this.rebuiltPackageCount === this.incompatiblePackages.length ? 'alert-success icon-check' : 'alert-warning icon-bug' return (
    - {this.rebuiltPackageCount} of {this.incompatiblePackages.length} packages - were rebuilt successfully. Reload Atom to activate them. - + {this.rebuiltPackageCount} of {this.incompatiblePackages.length}{' '} + packages were rebuilt successfully. Reload Atom to activate them. @@ -72,10 +75,13 @@ export default class IncompatiblePackagesComponent { } else { return (
    - Some installed packages could not be loaded because they contain native - modules that were compiled for an earlier version of Atom. - -
    @@ -92,9 +98,11 @@ export default class IncompatiblePackagesComponent { renderIncompatiblePackageList () { return ( -
    { - this.incompatiblePackages.map(this.renderIncompatiblePackage.bind(this)) - }
    +
    + {this.incompatiblePackages.map( + this.renderIncompatiblePackage.bind(this) + )} +
    ) } @@ -104,15 +112,18 @@ export default class IncompatiblePackagesComponent { return (
    {this.renderRebuildStatusIndicator(rebuildStatus)} - +

    {pack.name} {pack.metadata.version}

    - { - rebuildStatus + {rebuildStatus ? this.renderRebuildOutput(pack) - : this.renderIncompatibleModules(pack) - } + : this.renderIncompatibleModules(pack)}
    ) } @@ -151,23 +162,23 @@ export default class IncompatiblePackagesComponent { renderIncompatibleModules (pack) { return ( -
      { - pack.incompatibleModules.map((nativeModule) => +
        + {pack.incompatibleModules.map(nativeModule => (
      • - {nativeModule.name}@{nativeModule.version || 'unknown'} – {nativeModule.error} + {nativeModule.name}@{nativeModule.version || 'unknown'} –{' '} + {nativeModule.error}
      • - ) - }
      + ))} +
    ) } populateIncompatiblePackages () { - this.incompatiblePackages = - this.packageManager - .getLoadedPackages() - .filter(pack => !pack.isCompatible()) + this.incompatiblePackages = this.packageManager + .getLoadedPackages() + .filter(pack => !pack.isCompatible()) for (let pack of this.incompatiblePackages) { let buildFailureOutput = pack.getBuildFailureOutput() @@ -186,7 +197,7 @@ export default class IncompatiblePackagesComponent { let rebuiltPackageCount = 0 for (let pack of this.incompatiblePackages) { this.setPackageStatus(pack, REBUILDING) - let {code, stderr} = await pack.rebuild() + let { code, stderr } = await pack.rebuild() if (code === 0) { this.setPackageStatus(pack, REBUILD_SUCCEEDED) rebuiltPackageCount++ @@ -223,6 +234,6 @@ export default class IncompatiblePackagesComponent { } serialize () { - return {deserializer: 'IncompatiblePackagesComponent'} + return { deserializer: 'IncompatiblePackagesComponent' } } } diff --git a/packages/incompatible-packages/lib/main.js b/packages/incompatible-packages/lib/main.js index b936cb880..f66549a7c 100644 --- a/packages/incompatible-packages/lib/main.js +++ b/packages/incompatible-packages/lib/main.js @@ -1,6 +1,6 @@ /** @babel */ -import {Disposable, CompositeDisposable} from 'atom' +import { Disposable, CompositeDisposable } from 'atom' import VIEW_URI from './view-uri' let disposables = null @@ -8,17 +8,21 @@ let disposables = null export function activate () { disposables = new CompositeDisposable() - disposables.add(atom.workspace.addOpener((uri) => { - if (uri === VIEW_URI) { - return deserializeIncompatiblePackagesComponent() - } - })) + disposables.add( + atom.workspace.addOpener(uri => { + if (uri === VIEW_URI) { + return deserializeIncompatiblePackagesComponent() + } + }) + ) - disposables.add(atom.commands.add('atom-workspace', { - 'incompatible-packages:view': () => { - atom.workspace.open(VIEW_URI) - } - })) + disposables.add( + atom.commands.add('atom-workspace', { + 'incompatible-packages:view': () => { + atom.workspace.open(VIEW_URI) + } + }) + ) } export function deactivate () { @@ -33,7 +37,7 @@ export function consumeStatusBar (statusBar) { if (incompatibleCount > 0) { let icon = createIcon(incompatibleCount) - let tile = statusBar.addRightTile({item: icon, priority: 200}) + let tile = statusBar.addRightTile({ item: icon, priority: 200 }) icon.element.addEventListener('click', () => { atom.commands.dispatch(icon.element, 'incompatible-packages:view') }) @@ -48,5 +52,5 @@ export function deserializeIncompatiblePackagesComponent () { function createIcon (count) { const StatusIconComponent = require('./status-icon-component') - return new StatusIconComponent({count}) + return new StatusIconComponent({ count }) } diff --git a/packages/incompatible-packages/lib/status-icon-component.js b/packages/incompatible-packages/lib/status-icon-component.js index 72653ca9b..21cc2a742 100644 --- a/packages/incompatible-packages/lib/status-icon-component.js +++ b/packages/incompatible-packages/lib/status-icon-component.js @@ -4,7 +4,7 @@ import etch from 'etch' export default class StatusIconComponent { - constructor ({count}) { + constructor ({ count }) { this.count = count etch.initialize(this) } @@ -14,7 +14,7 @@ export default class StatusIconComponent { render () { return (
    - + {this.count}
    ) diff --git a/packages/incompatible-packages/spec/incompatible-packages-component-spec.js b/packages/incompatible-packages/spec/incompatible-packages-component-spec.js index 88d46677e..1039cabe5 100644 --- a/packages/incompatible-packages/spec/incompatible-packages-component-spec.js +++ b/packages/incompatible-packages/spec/incompatible-packages-component-spec.js @@ -16,7 +16,7 @@ describe('IncompatiblePackagesComponent', () => { return false }, rebuild: function () { - return new Promise((resolve) => this.resolveRebuild = resolve) + return new Promise(resolve => (this.resolveRebuild = resolve)) }, getBuildFailureOutput () { return null @@ -27,8 +27,8 @@ describe('IncompatiblePackagesComponent', () => { version: '1.0.0' }, incompatibleModules: [ - {name: 'x', version: '1.0.0', error: 'Expected version X, got Y'}, - {name: 'y', version: '1.0.0', error: 'Expected version X, got Z'} + { name: 'x', version: '1.0.0', error: 'Expected version X, got Y' }, + { name: 'y', version: '1.0.0', error: 'Expected version X, got Z' } ] }, { @@ -37,7 +37,7 @@ describe('IncompatiblePackagesComponent', () => { return false }, rebuild () { - return new Promise((resolve) => this.resolveRebuild = resolve) + return new Promise(resolve => (this.resolveRebuild = resolve)) }, getBuildFailureOutput () { return null @@ -48,7 +48,7 @@ describe('IncompatiblePackagesComponent', () => { version: '1.0.0' }, incompatibleModules: [ - {name: 'z', version: '1.0.0', error: 'Expected version X, got Y'} + { name: 'z', version: '1.0.0', error: 'Expected version X, got Y' } ] }, { @@ -67,7 +67,7 @@ describe('IncompatiblePackagesComponent', () => { repository: 'https://github.com/atom/b', version: '1.0.0' }, - incompatibleModules: [], + incompatibleModules: [] } ] }) @@ -75,18 +75,21 @@ describe('IncompatiblePackagesComponent', () => { describe('when packages have not finished loading', () => { it('delays rendering incompatible packages until the end of the tick', () => { waitsForPromise(async () => { - let component = - new IncompatiblePackagesComponent({ - getActivePackages: () => [], - getLoadedPackages: () => packages - }) - let {element} = component + let component = new IncompatiblePackagesComponent({ + getActivePackages: () => [], + getLoadedPackages: () => packages + }) + let { element } = component - expect(element.querySelectorAll('.incompatible-package').length).toEqual(0) + expect( + element.querySelectorAll('.incompatible-package').length + ).toEqual(0) await etchScheduler.getNextUpdatePromise() - expect(element.querySelectorAll('.incompatible-package').length).toBeGreaterThan(0) + expect( + element.querySelectorAll('.incompatible-package').length + ).toBeGreaterThan(0) }) }) }) @@ -97,12 +100,11 @@ describe('IncompatiblePackagesComponent', () => { expect(packages[2].isCompatible()).toBe(true) let compatiblePackages = [packages[2]] - let component = - new IncompatiblePackagesComponent({ - getActivePackages: () => compatiblePackages, - getLoadedPackages: () => compatiblePackages - }) - let {element} = component + let component = new IncompatiblePackagesComponent({ + getActivePackages: () => compatiblePackages, + getLoadedPackages: () => compatiblePackages + }) + let { element } = component await etchScheduler.getNextUpdatePromise() @@ -119,18 +121,23 @@ describe('IncompatiblePackagesComponent', () => { return 'The build failed' } - let component = - new IncompatiblePackagesComponent({ - getActivePackages: () => packages, - getLoadedPackages: () => packages - }) - let {element} = component + let component = new IncompatiblePackagesComponent({ + getActivePackages: () => packages, + getLoadedPackages: () => packages + }) + let { element } = component await etchScheduler.getNextUpdatePromise() - let packageElement = element.querySelector('.incompatible-package:nth-child(2)') + let packageElement = element.querySelector( + '.incompatible-package:nth-child(2)' + ) - expect(packageElement.querySelector('.badge').textContent).toBe('Rebuild Failed') - expect(packageElement.querySelector('pre').textContent).toBe('The build failed') + expect(packageElement.querySelector('.badge').textContent).toBe( + 'Rebuild Failed' + ) + expect(packageElement.querySelector('pre').textContent).toBe( + 'The build failed' + ) }) }) }) @@ -138,75 +145,101 @@ describe('IncompatiblePackagesComponent', () => { describe('when there are incompatible packages', () => { it('renders incompatible packages and the rebuild button', () => { waitsForPromise(async () => { - let component = - new IncompatiblePackagesComponent({ - getActivePackages: () => packages, - getLoadedPackages: () => packages - }) - let {element} = component + let component = new IncompatiblePackagesComponent({ + getActivePackages: () => packages, + getLoadedPackages: () => packages + }) + let { element } = component await etchScheduler.getNextUpdatePromise() - expect(element.querySelectorAll('.incompatible-package').length).toEqual(2) + expect( + element.querySelectorAll('.incompatible-package').length + ).toEqual(2) expect(element.querySelector('button')).not.toBeNull() }) }) describe('when the "Rebuild All" button is clicked', () => { - it('rebuilds every incompatible package, updating each package\'s view with status', () => { + it("rebuilds every incompatible package, updating each package's view with status", () => { waitsForPromise(async () => { - let component = - new IncompatiblePackagesComponent({ - getActivePackages: () => packages, - getLoadedPackages: () => packages - }) - let {element} = component + let component = new IncompatiblePackagesComponent({ + getActivePackages: () => packages, + getLoadedPackages: () => packages + }) + let { element } = component jasmine.attachToDOM(element) await etchScheduler.getNextUpdatePromise() - component.refs.rebuildButton.dispatchEvent(new CustomEvent('click', {bubbles: true})) + component.refs.rebuildButton.dispatchEvent( + new CustomEvent('click', { bubbles: true }) + ) await etchScheduler.getNextUpdatePromise() // view update expect(component.refs.rebuildButton.disabled).toBe(true) expect(packages[0].resolveRebuild).toBeDefined() - expect(element.querySelector('.incompatible-package:nth-child(1) .badge').textContent).toBe('Rebuilding') - expect(element.querySelector('.incompatible-package:nth-child(2) .badge')).toBeNull() + expect( + element.querySelector('.incompatible-package:nth-child(1) .badge') + .textContent + ).toBe('Rebuilding') + expect( + element.querySelector('.incompatible-package:nth-child(2) .badge') + ).toBeNull() - packages[0].resolveRebuild({code: 0}) // simulate rebuild success + packages[0].resolveRebuild({ code: 0 }) // simulate rebuild success await etchScheduler.getNextUpdatePromise() // view update expect(packages[1].resolveRebuild).toBeDefined() - expect(element.querySelector('.incompatible-package:nth-child(1) .badge').textContent).toBe('Rebuild Succeeded') - expect(element.querySelector('.incompatible-package:nth-child(2) .badge').textContent).toBe('Rebuilding') + expect( + element.querySelector('.incompatible-package:nth-child(1) .badge') + .textContent + ).toBe('Rebuild Succeeded') + expect( + element.querySelector('.incompatible-package:nth-child(2) .badge') + .textContent + ).toBe('Rebuilding') - packages[1].resolveRebuild({code: 12, stderr: 'This is an error from the test!'}) // simulate rebuild failure + packages[1].resolveRebuild({ + code: 12, + stderr: 'This is an error from the test!' + }) // simulate rebuild failure await etchScheduler.getNextUpdatePromise() // view update - expect(element.querySelector('.incompatible-package:nth-child(1) .badge').textContent).toBe('Rebuild Succeeded') - expect(element.querySelector('.incompatible-package:nth-child(2) .badge').textContent).toBe('Rebuild Failed') - expect(element.querySelector('.incompatible-package:nth-child(2) pre').textContent).toBe('This is an error from the test!') + expect( + element.querySelector('.incompatible-package:nth-child(1) .badge') + .textContent + ).toBe('Rebuild Succeeded') + expect( + element.querySelector('.incompatible-package:nth-child(2) .badge') + .textContent + ).toBe('Rebuild Failed') + expect( + element.querySelector('.incompatible-package:nth-child(2) pre') + .textContent + ).toBe('This is an error from the test!') }) }) it('displays a prompt to reload Atom when the packages finish rebuilding', () => { waitsForPromise(async () => { - let component = - new IncompatiblePackagesComponent({ - getActivePackages: () => packages, - getLoadedPackages: () => packages - }) - let {element} = component + let component = new IncompatiblePackagesComponent({ + getActivePackages: () => packages, + getLoadedPackages: () => packages + }) + let { element } = component jasmine.attachToDOM(element) await etchScheduler.getNextUpdatePromise() // view update - component.refs.rebuildButton.dispatchEvent(new CustomEvent('click', {bubbles: true})) - expect(packages[0].resolveRebuild({code: 0})) + component.refs.rebuildButton.dispatchEvent( + new CustomEvent('click', { bubbles: true }) + ) + expect(packages[0].resolveRebuild({ code: 0 })) await new Promise(global.setImmediate) - expect(packages[1].resolveRebuild({code: 0})) + expect(packages[1].resolveRebuild({ code: 0 })) await etchScheduler.getNextUpdatePromise() // view update @@ -214,7 +247,9 @@ describe('IncompatiblePackagesComponent', () => { expect(element.querySelector('.alert').textContent).toMatch(/2 of 2/) spyOn(atom, 'reload') - component.refs.reloadButton.dispatchEvent(new CustomEvent('click', {bubbles: true})) + component.refs.reloadButton.dispatchEvent( + new CustomEvent('click', { bubbles: true }) + ) expect(atom.reload).toHaveBeenCalled() }) }) @@ -223,19 +258,22 @@ describe('IncompatiblePackagesComponent', () => { describe('when the "Package Settings" button is clicked', () => { it('opens the settings panel for the package', () => { waitsForPromise(async () => { - let component = - new IncompatiblePackagesComponent({ - getActivePackages: () => packages, - getLoadedPackages: () => packages - }) - let {element} = component + let component = new IncompatiblePackagesComponent({ + getActivePackages: () => packages, + getLoadedPackages: () => packages + }) + let { element } = component jasmine.attachToDOM(element) await etchScheduler.getNextUpdatePromise() spyOn(atom.workspace, 'open') - element.querySelector('.incompatible-package:nth-child(2) button').dispatchEvent(new CustomEvent('click', {bubbles: true})) - expect(atom.workspace.open).toHaveBeenCalledWith('atom://config/packages/incompatible-2') + element + .querySelector('.incompatible-package:nth-child(2) button') + .dispatchEvent(new CustomEvent('click', { bubbles: true })) + expect(atom.workspace.open).toHaveBeenCalledWith( + 'atom://config/packages/incompatible-2' + ) }) }) }) diff --git a/packages/incompatible-packages/spec/incompatible-packages-spec.js b/packages/incompatible-packages/spec/incompatible-packages-spec.js index fa6e7e76a..c4e3587d8 100644 --- a/packages/incompatible-packages/spec/incompatible-packages-spec.js +++ b/packages/incompatible-packages/spec/incompatible-packages-spec.js @@ -36,7 +36,9 @@ describe('Incompatible packages', () => { ) spyOn(incompatiblePackage, 'isCompatible').andReturn(false) incompatiblePackage.incompatibleModules = [] - waitsForPromise(() => atom.packages.activatePackage("incompatible-packages")) + waitsForPromise(() => + atom.packages.activatePackage('incompatible-packages') + ) waits(1) }) @@ -63,13 +65,15 @@ describe('Incompatible packages', () => { describe('when there are no packages with incompatible native modules', () => { beforeEach(() => { - waitsForPromise(() => atom.packages.activatePackage("incompatible-packages")) + waitsForPromise(() => + atom.packages.activatePackage('incompatible-packages') + ) }) it('does not add an icon to the status bar', () => { let statusBarItemClasses = statusBar .getRightTiles() - .map((tile) => tile.getItem().className) + .map(tile => tile.getItem().className) expect(statusBarItemClasses).not.toContain('incompatible-packages') }) diff --git a/packages/line-ending-selector/lib/main.js b/packages/line-ending-selector/lib/main.js index 5945ed24f..c3e07ff05 100644 --- a/packages/line-ending-selector/lib/main.js +++ b/packages/line-ending-selector/lib/main.js @@ -1,12 +1,17 @@ 'use babel' import _ from 'underscore-plus' -import {CompositeDisposable, Disposable} from 'atom' +import { CompositeDisposable, Disposable } from 'atom' import SelectListView from 'atom-select-list' import StatusBarItem from './status-bar-item' import helpers from './helpers' const LineEndingRegExp = /\r\n|\n/g + +// the following regular expression is executed natively via the `substring` package, +// where `\A` corresponds to the beginning of the string. +// More info: https://github.com/atom/line-ending-selector/pull/56 +// eslint-disable-next-line no-useless-escape const LFRegExp = /(\A|[^\r])\n/g const CRLFRegExp = /\r\n/g @@ -17,48 +22,60 @@ let lineEndingListView = null export function activate () { disposables = new CompositeDisposable() - disposables.add(atom.commands.add('atom-text-editor', { - 'line-ending-selector:show': (event) => { - if (!modalPanel) { - lineEndingListView = new SelectListView({ - items: [{name: 'LF', value: '\n'}, {name: 'CRLF', value: '\r\n'}], - filterKeyForItem: (lineEnding) => lineEnding.name, - didConfirmSelection: (lineEnding) => { - setLineEnding(atom.workspace.getActiveTextEditor(), lineEnding.value) - modalPanel.hide() - }, - didCancelSelection: () => { - modalPanel.hide() - }, - elementForItem: (lineEnding) => { - const element = document.createElement('li') - element.textContent = lineEnding.name - return element - } - }) - modalPanel = atom.workspace.addModalPanel({item: lineEndingListView}) - disposables.add(new Disposable(() => { - lineEndingListView.destroy() - modalPanel.destroy() - modalPanel = null - })) + disposables.add( + atom.commands.add('atom-text-editor', { + 'line-ending-selector:show': event => { + if (!modalPanel) { + lineEndingListView = new SelectListView({ + items: [ + { name: 'LF', value: '\n' }, + { name: 'CRLF', value: '\r\n' } + ], + filterKeyForItem: lineEnding => lineEnding.name, + didConfirmSelection: lineEnding => { + setLineEnding( + atom.workspace.getActiveTextEditor(), + lineEnding.value + ) + modalPanel.hide() + }, + didCancelSelection: () => { + modalPanel.hide() + }, + elementForItem: lineEnding => { + const element = document.createElement('li') + element.textContent = lineEnding.name + return element + } + }) + modalPanel = atom.workspace.addModalPanel({ + item: lineEndingListView + }) + disposables.add( + new Disposable(() => { + lineEndingListView.destroy() + modalPanel.destroy() + modalPanel = null + }) + ) + } + + lineEndingListView.reset() + modalPanel.show() + lineEndingListView.focus() + }, + + 'line-ending-selector:convert-to-LF': event => { + const editorElement = event.target.closest('atom-text-editor') + setLineEnding(editorElement.getModel(), '\n') + }, + + 'line-ending-selector:convert-to-CRLF': event => { + const editorElement = event.target.closest('atom-text-editor') + setLineEnding(editorElement.getModel(), '\r\n') } - - lineEndingListView.reset() - modalPanel.show() - lineEndingListView.focus() - }, - - 'line-ending-selector:convert-to-LF': (event) => { - const editorElement = event.target.closest('atom-text-editor') - setLineEnding(editorElement.getModel(), '\n') - }, - - 'line-ending-selector:convert-to-CRLF': (event) => { - const editorElement = event.target.closest('atom-text-editor') - setLineEnding(editorElement.getModel(), '\r\n') - } - })) + }) + ) } export function deactivate () { @@ -70,8 +87,8 @@ export function consumeStatusBar (statusBar) { let currentBufferDisposable = null let tooltipDisposable = null - const updateTile = _.debounce((buffer) => { - getLineEndings(buffer).then((lineEndings) => { + const updateTile = _.debounce(buffer => { + getLineEndings(buffer).then(lineEndings => { if (lineEndings.size === 0) { let defaultLineEnding = getDefaultLineEnding() buffer.setPreferredLineEnding(defaultLineEnding) @@ -81,45 +98,49 @@ export function consumeStatusBar (statusBar) { }) }, 0) - disposables.add(atom.workspace.observeActiveTextEditor((editor) => { - if (currentBufferDisposable) currentBufferDisposable.dispose() + disposables.add( + atom.workspace.observeActiveTextEditor(editor => { + if (currentBufferDisposable) currentBufferDisposable.dispose() - if (editor && editor.getBuffer) { - let buffer = editor.getBuffer() - updateTile(buffer) - currentBufferDisposable = buffer.onDidChange(({oldText, newText}) => { - if (!statusBarItem.hasLineEnding('\n')) { - if (newText.indexOf('\n') >= 0) { + if (editor && editor.getBuffer) { + let buffer = editor.getBuffer() + updateTile(buffer) + currentBufferDisposable = buffer.onDidChange(({ oldText, newText }) => { + if (!statusBarItem.hasLineEnding('\n')) { + if (newText.indexOf('\n') >= 0) { + updateTile(buffer) + } + } else if (!statusBarItem.hasLineEnding('\r\n')) { + if (newText.indexOf('\r\n') >= 0) { + updateTile(buffer) + } + } else if (oldText.indexOf('\n')) { updateTile(buffer) } - } else if (!statusBarItem.hasLineEnding('\r\n')) { - if (newText.indexOf('\r\n') >= 0) { - updateTile(buffer) - } - } else if (oldText.indexOf('\n')) { - updateTile(buffer) + }) + } else { + statusBarItem.setLineEndings(new Set()) + currentBufferDisposable = null + } + + if (tooltipDisposable) { + disposables.remove(tooltipDisposable) + tooltipDisposable.dispose() + } + tooltipDisposable = atom.tooltips.add(statusBarItem.element, { + title () { + return `File uses ${statusBarItem.description()} line endings` } }) - } else { - statusBarItem.setLineEndings(new Set()) - currentBufferDisposable = null - } - - if (tooltipDisposable) { - disposables.remove(tooltipDisposable) - tooltipDisposable.dispose() - } - tooltipDisposable = atom.tooltips.add(statusBarItem.element, { - title () { - return `File uses ${statusBarItem.description()} line endings` - } + disposables.add(tooltipDisposable) }) - disposables.add(tooltipDisposable) - })) + ) - disposables.add(new Disposable(() => { - if (currentBufferDisposable) currentBufferDisposable.dispose() - })) + disposables.add( + new Disposable(() => { + if (currentBufferDisposable) currentBufferDisposable.dispose() + }) + ) statusBarItem.onClick(() => { const editor = atom.workspace.getActiveTextEditor() @@ -129,7 +150,7 @@ export function consumeStatusBar (statusBar) { ) }) - let tile = statusBar.addRightTile({item: statusBarItem, priority: 200}) + let tile = statusBar.addRightTile({ item: statusBarItem, priority: 200 }) disposables.add(new Disposable(() => tile.destroy())) } @@ -141,23 +162,22 @@ function getDefaultLineEnding () { return '\r\n' case 'OS Default': default: - return (helpers.getProcessPlatform() === 'win32') ? '\r\n' : '\n' + return helpers.getProcessPlatform() === 'win32' ? '\r\n' : '\n' } } function getLineEndings (buffer) { if (typeof buffer.find === 'function') { - return Promise.all([ - buffer.find(LFRegExp), - buffer.find(CRLFRegExp) - ]).then(([hasLF, hasCRLF]) => { - const result = new Set() - if (hasLF) result.add('\n') - if (hasCRLF) result.add('\r\n') - return result - }) + return Promise.all([buffer.find(LFRegExp), buffer.find(CRLFRegExp)]).then( + ([hasLF, hasCRLF]) => { + const result = new Set() + if (hasLF) result.add('\n') + if (hasCRLF) result.add('\r\n') + return result + } + ) } else { - return new Promise((resolve) => { + return new Promise(resolve => { const result = new Set() for (let i = 0; i < buffer.getLineCount() - 1; i++) { result.add(buffer.lineEndingForRow(i)) diff --git a/packages/line-ending-selector/lib/status-bar-item.js b/packages/line-ending-selector/lib/status-bar-item.js index da2f033c9..1684bc370 100644 --- a/packages/line-ending-selector/lib/status-bar-item.js +++ b/packages/line-ending-selector/lib/status-bar-item.js @@ -1,7 +1,6 @@ -const {Emitter} = require('atom') +const { Emitter } = require('atom') -module.exports = -class StatusBarItem { +module.exports = class StatusBarItem { constructor () { this.element = document.createElement('a') this.element.className = 'line-ending-tile inline-block' @@ -46,9 +45,13 @@ function lineEndingName (lineEndings) { function lineEndingDescription (lineEndings) { switch (lineEndingName(lineEndings)) { - case 'Mixed': return 'mixed' - case 'LF': return 'LF (Unix)' - case 'CRLF': return 'CRLF (Windows)' - default: return 'unknown' + case 'Mixed': + return 'mixed' + case 'LF': + return 'LF (Unix)' + case 'CRLF': + return 'CRLF (Windows)' + default: + return 'unknown' } } diff --git a/packages/line-ending-selector/spec/line-ending-selector-spec.js b/packages/line-ending-selector/spec/line-ending-selector-spec.js index 263f43c62..afc8f9db0 100644 --- a/packages/line-ending-selector/spec/line-ending-selector-spec.js +++ b/packages/line-ending-selector/spec/line-ending-selector-spec.js @@ -1,5 +1,5 @@ const helpers = require('../lib/helpers') -const {TextEditor} = require('atom') +const { TextEditor } = require('atom') describe('line ending selector', () => { let lineEndingTile @@ -30,7 +30,7 @@ describe('line ending selector', () => { beforeEach(() => { waitsForPromise(() => { - return atom.workspace.open('mixed-endings.md').then((e) => { + return atom.workspace.open('mixed-endings.md').then(e => { editor = e editorElement = atom.views.getView(editor) jasmine.attachToDOM(editorElement) @@ -41,7 +41,10 @@ describe('line ending selector', () => { describe('When "line-ending-selector:convert-to-LF" is run', () => { it('converts the file to LF line endings', () => { editorElement.focus() - atom.commands.dispatch(document.activeElement, 'line-ending-selector:convert-to-LF') + atom.commands.dispatch( + document.activeElement, + 'line-ending-selector:convert-to-LF' + ) expect(editor.getText()).toBe('Hello\nGoodbye\nMixed\n') }) }) @@ -49,7 +52,10 @@ describe('line ending selector', () => { describe('When "line-ending-selector:convert-to-LF" is run', () => { it('converts the file to CRLF line endings', () => { editorElement.focus() - atom.commands.dispatch(document.activeElement, 'line-ending-selector:convert-to-CRLF') + atom.commands.dispatch( + document.activeElement, + 'line-ending-selector:convert-to-CRLF' + ) expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nMixed\r\n') }) }) @@ -58,30 +64,34 @@ describe('line ending selector', () => { describe('Status bar tile', () => { describe('when an empty file is opened', () => { it('uses the default line endings for the platform', () => { - waitsFor((done) => { + waitsFor(done => { spyOn(helpers, 'getProcessPlatform').andReturn('win32') - atom.workspace.open('').then((editor) => { + atom.workspace.open('').then(editor => { const subscription = lineEndingTile.onDidChange(() => { subscription.dispose() expect(lineEndingTile.element.textContent).toBe('CRLF') expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n') - expect(getTooltipText(lineEndingTile.element)).toBe('File uses CRLF (Windows) line endings') + expect(getTooltipText(lineEndingTile.element)).toBe( + 'File uses CRLF (Windows) line endings' + ) done() }) }) }) - waitsFor((done) => { + waitsFor(done => { helpers.getProcessPlatform.andReturn('darwin') - atom.workspace.open('').then((editor) => { + atom.workspace.open('').then(editor => { const subscription = lineEndingTile.onDidChange(() => { subscription.dispose() expect(lineEndingTile.element.textContent).toBe('LF') expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n') - expect(getTooltipText(lineEndingTile.element)).toBe('File uses LF (Unix) line endings') + expect(getTooltipText(lineEndingTile.element)).toBe( + 'File uses LF (Unix) line endings' + ) done() }) @@ -95,10 +105,10 @@ describe('line ending selector', () => { }) it('uses LF line endings, regardless of the platform', () => { - waitsFor((done) => { + waitsFor(done => { spyOn(helpers, 'getProcessPlatform').andReturn('win32') - atom.workspace.open('').then((editor) => { + atom.workspace.open('').then(editor => { lineEndingTile.onDidChange(() => { expect(lineEndingTile.element.textContent).toBe('LF') expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n') @@ -115,8 +125,8 @@ describe('line ending selector', () => { }) it('uses CRLF line endings, regardless of the platform', () => { - waitsFor((done) => { - atom.workspace.open('').then((editor) => { + waitsFor(done => { + atom.workspace.open('').then(editor => { lineEndingTile.onDidChange(() => { expect(lineEndingTile.element.textContent).toBe('CRLF') expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n') @@ -130,7 +140,7 @@ describe('line ending selector', () => { describe('when a file is opened that contains only CRLF line endings', () => { it('displays "CRLF" as the line ending', () => { - waitsFor((done) => { + waitsFor(done => { atom.workspace.open('windows-endings.md').then(() => { lineEndingTile.onDidChange(() => { expect(lineEndingTile.element.textContent).toBe('CRLF') @@ -143,8 +153,8 @@ describe('line ending selector', () => { describe('when a file is opened that contains only LF line endings', () => { it('displays "LF" as the line ending', () => { - waitsFor((done) => { - atom.workspace.open('unix-endings.md').then((editor) => { + waitsFor(done => { + atom.workspace.open('unix-endings.md').then(editor => { lineEndingTile.onDidChange(() => { expect(lineEndingTile.element.textContent).toBe('LF') expect(editor.getBuffer().getPreferredLineEnding()).toBe(null) @@ -157,7 +167,7 @@ describe('line ending selector', () => { describe('when a file is opened that contains mixed line endings', () => { it('displays "Mixed" as the line ending', () => { - waitsFor((done) => { + waitsFor(done => { atom.workspace.open('mixed-endings.md').then(() => { lineEndingTile.onDidChange(() => { expect(lineEndingTile.element.textContent).toBe('Mixed') @@ -174,10 +184,10 @@ describe('line ending selector', () => { beforeEach(() => { jasmine.attachToDOM(atom.views.getView(atom.workspace)) - waitsFor((done) => - atom.workspace.open('unix-endings.md').then(() => - lineEndingTile.onDidChange(done) - ) + waitsFor(done => + atom.workspace + .open('unix-endings.md') + .then(() => lineEndingTile.onDidChange(done)) ) }) @@ -192,7 +202,9 @@ describe('line ending selector', () => { lineEndingSelector = lineEndingModal.getItem() expect(lineEndingModal.isVisible()).toBe(true) - expect(lineEndingSelector.element.contains(document.activeElement)).toBe(true) + expect( + lineEndingSelector.element.contains(document.activeElement) + ).toBe(true) let listItems = lineEndingSelector.element.querySelectorAll('li') expect(listItems[0].textContent).toBe('LF') expect(listItems[1].textContent).toBe('CRLF') @@ -210,7 +222,9 @@ describe('line ending selector', () => { lineEndingSelector = lineEndingModal.getItem() expect(lineEndingModal.isVisible()).toBe(true) - expect(lineEndingSelector.element.contains(document.activeElement)).toBe(true) + expect( + lineEndingSelector.element.contains(document.activeElement) + ).toBe(true) let listItems = lineEndingSelector.element.querySelectorAll('li') expect(listItems[0].textContent).toBe('LF') expect(listItems[1].textContent).toBe('CRLF') @@ -264,12 +278,12 @@ describe('line ending selector', () => { }) }) - describe('when the buffer\'s line endings change', () => { + describe("when the buffer's line endings change", () => { let editor beforeEach(() => { - waitsFor((done) => { - atom.workspace.open('unix-endings.md').then((e) => { + waitsFor(done => { + atom.workspace.open('unix-endings.md').then(e => { editor = e lineEndingTile.onDidChange(done) }) @@ -291,33 +305,47 @@ describe('line ending selector', () => { }) expect(lineEndingTile.element.textContent).toBe('LF') - expect(getTooltipText(lineEndingTile.element)).toBe('File uses LF (Unix) line endings') + expect(getTooltipText(lineEndingTile.element)).toBe( + 'File uses LF (Unix) line endings' + ) - waitsFor((done) => { + waitsFor(done => { editor.setTextInBufferRange([[0, 0], [0, 0]], '... ') - editor.setTextInBufferRange([[0, Infinity], [1, 0]], '\r\n', {normalizeLineEndings: false}) + editor.setTextInBufferRange([[0, Infinity], [1, 0]], '\r\n', { + normalizeLineEndings: false + }) lineEndingTile.onDidChange(done) }) runs(() => { expect(tileUpdateCount).toBe(1) expect(lineEndingTile.element.textContent).toBe('Mixed') - expect(getTooltipText(lineEndingTile.element)).toBe('File uses mixed line endings') + expect(getTooltipText(lineEndingTile.element)).toBe( + 'File uses mixed line endings' + ) }) - waitsFor((done) => { - atom.commands.dispatch(editor.getElement(), 'line-ending-selector:convert-to-CRLF') + waitsFor(done => { + atom.commands.dispatch( + editor.getElement(), + 'line-ending-selector:convert-to-CRLF' + ) lineEndingTile.onDidChange(done) }) runs(() => { expect(tileUpdateCount).toBe(2) expect(lineEndingTile.element.textContent).toBe('CRLF') - expect(getTooltipText(lineEndingTile.element)).toBe('File uses CRLF (Windows) line endings') + expect(getTooltipText(lineEndingTile.element)).toBe( + 'File uses CRLF (Windows) line endings' + ) }) - waitsFor((done) => { - atom.commands.dispatch(editor.getElement(), 'line-ending-selector:convert-to-LF') + waitsFor(done => { + atom.commands.dispatch( + editor.getElement(), + 'line-ending-selector:convert-to-LF' + ) lineEndingTile.onDidChange(done) }) diff --git a/packages/link/lib/link.js b/packages/link/lib/link.js index 4b2443cfb..005c9e614 100644 --- a/packages/link/lib/link.js +++ b/packages/link/lib/link.js @@ -1,12 +1,16 @@ const url = require('url') -const {shell} = require('electron') +const { shell } = require('electron') const _ = require('underscore-plus') const LINK_SCOPE_REGEX = /markup\.underline\.link/ module.exports = { activate () { - this.commandDisposable = atom.commands.add('atom-text-editor', 'link:open', () => this.openLink()) + this.commandDisposable = atom.commands.add( + 'atom-text-editor', + 'link:open', + () => this.openLink() + ) }, deactivate () { @@ -24,8 +28,10 @@ module.exports = { link = this.linkForName(editor, link) } - const {protocol} = url.parse(link) - if (protocol === 'http:' || protocol === 'https:' || protocol === 'atom:') shell.openExternal(link) + const { protocol } = url.parse(link) + if (protocol === 'http:' || protocol === 'https:' || protocol === 'atom:') { + shell.openExternal(link) + } }, // Get the link under the cursor in the editor @@ -47,7 +53,11 @@ module.exports = { // Returns a {String} link or undefined if no link found. linkAtPosition (editor, bufferPosition) { const token = editor.tokenForBufferPosition(bufferPosition) - if (token && token.value && token.scopes.some(scope => LINK_SCOPE_REGEX.test(scope))) { + if ( + token && + token.value && + token.scopes.some(scope => LINK_SCOPE_REGEX.test(scope)) + ) { return token.value } }, @@ -65,11 +75,18 @@ module.exports = { // Returns a {String} link linkForName (editor, linkName) { let link = linkName - const regex = new RegExp(`^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`, 'g') - editor.backwardsScanInBufferRange(regex, [[0, 0], [Infinity, Infinity]], ({match, stop}) => { - link = match[1] - stop() - }) + const regex = new RegExp( + `^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`, + 'g' + ) + editor.backwardsScanInBufferRange( + regex, + [[0, 0], [Infinity, Infinity]], + ({ match, stop }) => { + link = match[1] + stop() + } + ) return link } } diff --git a/packages/link/spec/async-spec-helpers.js b/packages/link/spec/async-spec-helpers.js deleted file mode 100644 index 73002c049..000000000 --- a/packages/link/spec/async-spec-helpers.js +++ /dev/null @@ -1,103 +0,0 @@ -/** @babel */ - -export function beforeEach (fn) { - global.beforeEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -export function afterEach (fn) { - global.afterEach(function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { - module.exports[name] = function (description, fn) { - if (fn === undefined) { - global[name](description) - return - } - - global[name](description, function () { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) - } -}) - -export async function conditionPromise (condition, description = 'anonymous condition') { - const startTime = Date.now() - - while (true) { - await timeoutPromise(100) - - if (await condition()) { - return - } - - if (Date.now() - startTime > 5000) { - throw new Error('Timed out waiting on ' + description) - } - } -} - -export function timeoutPromise (timeout) { - return new Promise(function (resolve) { - global.setTimeout(resolve, timeout) - }) -} - -function waitsForPromise (fn) { - const promise = fn() - global.waitsFor('spec promise to resolve', function (done) { - promise.then(done, function (error) { - jasmine.getEnv().currentSpec.fail(error) - done() - }) - }) -} - -export function emitterEventPromise (emitter, event, timeout = 15000) { - return new Promise((resolve, reject) => { - const timeoutHandle = setTimeout(() => { - reject(new Error(`Timed out waiting for '${event}' event`)) - }, timeout) - emitter.once(event, () => { - clearTimeout(timeoutHandle) - resolve() - }) - }) -} - -export function promisify (original) { - return function (...args) { - return new Promise((resolve, reject) => { - args.push((err, ...results) => { - if (err) { - reject(err) - } else { - resolve(...results) - } - }) - - return original(...args) - }) - } -} - -export function promisifySome (obj, fnNames) { - const result = {} - for (const fnName of fnNames) { - result[fnName] = promisify(obj[fnName]) - } - return result -} diff --git a/packages/link/spec/link-spec.js b/packages/link/spec/link-spec.js index 976261cd4..e31d3f6ca 100644 --- a/packages/link/spec/link-spec.js +++ b/packages/link/spec/link-spec.js @@ -1,6 +1,4 @@ -const {shell} = require('electron') - -const {it, fit, ffit, afterEach, beforeEach} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars +const { shell } = require('electron') describe('link package', () => { beforeEach(async () => { @@ -47,13 +45,15 @@ describe('link package', () => { // only works in Atom >= 1.33.0 // https://github.com/atom/link/pull/33#issuecomment-419643655 const atomVersion = atom.getVersion().split('.') - console.error("atomVersion", atomVersion) + console.error('atomVersion', atomVersion) if (+atomVersion[0] > 1 || +atomVersion[1] >= 33) { it("opens an 'atom:' link", async () => { await atom.workspace.open('sample.md') const editor = atom.workspace.getActiveTextEditor() - editor.setText('// "atom://core/open/file?filename=sample.js&line=1&column=2"') + editor.setText( + '// "atom://core/open/file?filename=sample.js&line=1&column=2"' + ) spyOn(shell, 'openExternal') atom.commands.dispatch(atom.views.getView(editor), 'link:open') @@ -63,21 +63,27 @@ describe('link package', () => { atom.commands.dispatch(atom.views.getView(editor), 'link:open') expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe('atom://core/open/file?filename=sample.js&line=1&column=2') + expect(shell.openExternal.argsForCall[0][0]).toBe( + 'atom://core/open/file?filename=sample.js&line=1&column=2' + ) shell.openExternal.reset() editor.setCursorBufferPosition([0, 8]) atom.commands.dispatch(atom.views.getView(editor), 'link:open') expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe('atom://core/open/file?filename=sample.js&line=1&column=2') + expect(shell.openExternal.argsForCall[0][0]).toBe( + 'atom://core/open/file?filename=sample.js&line=1&column=2' + ) shell.openExternal.reset() editor.setCursorBufferPosition([0, 60]) atom.commands.dispatch(atom.views.getView(editor), 'link:open') expect(shell.openExternal).toHaveBeenCalled() - expect(shell.openExternal.argsForCall[0][0]).toBe('atom://core/open/file?filename=sample.js&line=1&column=2') + expect(shell.openExternal.argsForCall[0][0]).toBe( + 'atom://core/open/file?filename=sample.js&line=1&column=2' + ) }) } @@ -91,8 +97,7 @@ you should [click][here] you should not [click][her] [here]: http://github.com\ -` - ) +`) spyOn(shell, 'openExternal') editor.setCursorBufferPosition([0, 0]) @@ -110,8 +115,7 @@ you should not [click][her] atom.commands.dispatch(atom.views.getView(editor), 'link:open') expect(shell.openExternal).not.toHaveBeenCalled() - }) - ) + })) it('does not open non http/https/atom links', async () => { await atom.workspace.open('sample.md') diff --git a/packages/one-dark-ui/spec/theme-spec.js b/packages/one-dark-ui/spec/theme-spec.js index 5ff5aa3db..a43ea4f7c 100644 --- a/packages/one-dark-ui/spec/theme-spec.js +++ b/packages/one-dark-ui/spec/theme-spec.js @@ -14,31 +14,45 @@ describe(`${themeName} theme`, () => { it('allows the tab sizing to be set via config', () => { atom.config.set(`${themeName}.tabSizing`, 'Maximum') - expect(document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)).toBe('maximum') + expect( + document.documentElement.getAttribute(`theme-${themeName}-tabsizing`) + ).toBe('maximum') }) it('allows the tab sizing to be set via config', () => { atom.config.set(`${themeName}.tabSizing`, 'Minimum') - expect(document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)).toBe('minimum') + expect( + document.documentElement.getAttribute(`theme-${themeName}-tabsizing`) + ).toBe('minimum') }) it('allows the tab close button to be shown on the left via config', () => { atom.config.set(`${themeName}.tabCloseButton`, 'Left') - expect(document.documentElement.getAttribute(`theme-${themeName}-tab-close-button`)).toBe('left') + expect( + document.documentElement.getAttribute( + `theme-${themeName}-tab-close-button` + ) + ).toBe('left') }) it('allows the dock toggle buttons to be hidden via config', () => { atom.config.set(`${themeName}.hideDockButtons`, true) - expect(document.documentElement.getAttribute(`theme-${themeName}-dock-buttons`)).toBe('hidden') + expect( + document.documentElement.getAttribute(`theme-${themeName}-dock-buttons`) + ).toBe('hidden') }) it('allows the tree-view headers to be sticky via config', () => { atom.config.set(`${themeName}.stickyHeaders`, true) - expect(document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)).toBe('sticky') + expect( + document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`) + ).toBe('sticky') }) it('allows the tree-view headers to not be sticky via config', () => { atom.config.set(`${themeName}.stickyHeaders`, false) - expect(document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)).toBe(null) + expect( + document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`) + ).toBe(null) }) }) diff --git a/packages/one-light-ui/spec/theme-spec.js b/packages/one-light-ui/spec/theme-spec.js index a0e72875b..862d2ef5e 100644 --- a/packages/one-light-ui/spec/theme-spec.js +++ b/packages/one-light-ui/spec/theme-spec.js @@ -14,31 +14,45 @@ describe(`${themeName} theme`, () => { it('allows the tab sizing to be set via config', () => { atom.config.set(`${themeName}.tabSizing`, 'Maximum') - expect(document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)).toBe('maximum') + expect( + document.documentElement.getAttribute(`theme-${themeName}-tabsizing`) + ).toBe('maximum') }) it('allows the tab sizing to be set via config', () => { atom.config.set(`${themeName}.tabSizing`, 'Minimum') - expect(document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)).toBe('minimum') + expect( + document.documentElement.getAttribute(`theme-${themeName}-tabsizing`) + ).toBe('minimum') }) it('allows the tab close button to be shown on the left via config', () => { atom.config.set(`${themeName}.tabCloseButton`, 'Left') - expect(document.documentElement.getAttribute(`theme-${themeName}-tab-close-button`)).toBe('left') + expect( + document.documentElement.getAttribute( + `theme-${themeName}-tab-close-button` + ) + ).toBe('left') }) it('allows the dock toggle buttons to be hidden via config', () => { atom.config.set(`${themeName}.hideDockButtons`, true) - expect(document.documentElement.getAttribute(`theme-${themeName}-dock-buttons`)).toBe('hidden') + expect( + document.documentElement.getAttribute(`theme-${themeName}-dock-buttons`) + ).toBe('hidden') }) it('allows the tree-view headers to be sticky via config', () => { atom.config.set(`${themeName}.stickyHeaders`, true) - expect(document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)).toBe('sticky') + expect( + document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`) + ).toBe('sticky') }) it('allows the tree-view headers to not be sticky via config', () => { atom.config.set(`${themeName}.stickyHeaders`, false) - expect(document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)).toBe(null) + expect( + document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`) + ).toBe(null) }) }) diff --git a/script/lib/dump-symbols.js b/script/lib/dump-symbols.js index 6b0b10bef..9b7492ed7 100644 --- a/script/lib/dump-symbols.js +++ b/script/lib/dump-symbols.js @@ -35,7 +35,7 @@ function dumpSymbol (binaryPath) { const symbolDirPath = path.join(CONFIG.symbolsPath, filename, moduleLine[1]) const symbolFilePath = path.join(symbolDirPath, `${filename}.sym`) fs.mkdirpSync(symbolDirPath) - fs.writeFileSync(symbolFilePath) + fs.writeFileSync(symbolFilePath, content) resolve() } } diff --git a/script/lib/generate-startup-snapshot.js b/script/lib/generate-startup-snapshot.js index f6921d77a..553df33a4 100644 --- a/script/lib/generate-startup-snapshot.js +++ b/script/lib/generate-startup-snapshot.js @@ -42,6 +42,7 @@ module.exports = function (packagedAppPath) { requiredModuleRelativePath === path.join('..', 'src', 'electron-shims.js') || requiredModuleRelativePath === path.join('..', 'node_modules', 'atom-keymap', 'lib', 'command-event.js') || requiredModuleRelativePath === path.join('..', 'node_modules', 'babel-core', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'cached-run-in-this-context', 'lib', 'main.js') || requiredModuleRelativePath === path.join('..', 'node_modules', 'debug', 'node.js') || requiredModuleRelativePath === path.join('..', 'node_modules', 'git-utils', 'src', 'git.js') || requiredModuleRelativePath === path.join('..', 'node_modules', 'glob', 'glob.js') || @@ -96,36 +97,22 @@ module.exports = function (packagedAppPath) { {env: Object.assign({}, process.env, {ELECTRON_RUN_AS_NODE: 1})} ) - console.log('Generating startup blob with mksnapshot') - childProcess.spawnSync( - process.execPath, [ - path.join(CONFIG.repositoryRootPath, 'script', 'node_modules', 'electron-mksnapshot', 'mksnapshot.js'), - snapshotScriptPath, - '--output_dir', - CONFIG.buildOutputPath - ] + const generatedStartupBlobPath = path.join(CONFIG.buildOutputPath, 'snapshot_blob.bin') + console.log(`Generating startup blob at "${generatedStartupBlobPath}"`) + childProcess.execFileSync( + path.join(CONFIG.repositoryRootPath, 'script', 'node_modules', 'electron-mksnapshot', 'bin', 'mksnapshot'), + ['--no-use_ic', snapshotScriptPath, '--startup_blob', generatedStartupBlobPath] ) let startupBlobDestinationPath if (process.platform === 'darwin') { - startupBlobDestinationPath = `${packagedAppPath}/Contents/Frameworks/Electron Framework.framework/Resources` + startupBlobDestinationPath = `${packagedAppPath}/Contents/Frameworks/Electron Framework.framework/Resources/snapshot_blob.bin` } else { - startupBlobDestinationPath = packagedAppPath + startupBlobDestinationPath = path.join(packagedAppPath, 'snapshot_blob.bin') } - const snapshotBinaries = ['v8_context_snapshot.bin', 'snapshot_blob.bin'] - for (let snapshotBinary of snapshotBinaries) { - const destinationPath = path.join(startupBlobDestinationPath, snapshotBinary) - console.log(`Moving generated startup blob into "${destinationPath}"`) - try { - fs.unlinkSync(destinationPath) - } catch (err) { - // Doesn't matter if the file doesn't exist already - if (!err.code || err.code !== 'ENOENT') { - throw err - } - } - fs.renameSync(path.join(CONFIG.buildOutputPath, snapshotBinary), destinationPath) - } + console.log(`Moving generated startup blob into "${startupBlobDestinationPath}"`) + fs.unlinkSync(startupBlobDestinationPath) + fs.renameSync(generatedStartupBlobPath, startupBlobDestinationPath) }) } diff --git a/script/lib/lint-java-script-paths.js b/script/lib/lint-java-script-paths.js index 8086488dc..f237820ff 100644 --- a/script/lib/lint-java-script-paths.js +++ b/script/lib/lint-java-script-paths.js @@ -6,28 +6,46 @@ const path = require('path') const CONFIG = require('../config') -module.exports = function () { +module.exports = async function () { const globPathsToLint = [ path.join(CONFIG.repositoryRootPath, 'exports', '**', '*.js'), + path.join(CONFIG.repositoryRootPath, 'packages', '**', '*.js'), path.join(CONFIG.repositoryRootPath, 'script', '**', '*.js'), + path.join(CONFIG.repositoryRootPath, 'spec', '**', '*.js'), path.join(CONFIG.repositoryRootPath, 'src', '**', '*.js'), path.join(CONFIG.repositoryRootPath, 'static', '*.js') ] - return expandGlobPaths(globPathsToLint).then((paths) => { - return new Promise((resolve, reject) => { - standard.lintFiles(paths, (error, lintOutput) => { - if (error) { - reject(error) - } else { - const errors = [] - for (let result of lintOutput.results) { - for (let message of result.messages) { - errors.push({path: result.filePath, lineNumber: message.line, message: message.message, rule: message.ruleId}) - } + const globPathsToIgnore = [ + path.join(CONFIG.repositoryRootPath, 'spec', 'fixtures', '**', '*.js') + ] + + const [includePaths, excludePaths] = await Promise.all([ + expandGlobPaths(globPathsToLint), + expandGlobPaths(globPathsToIgnore) + ]) + + const paths = includePaths.filter( + myPath => !excludePaths.includes(myPath) + ) + + return new Promise((resolve, reject) => { + standard.lintFiles(paths, (error, lintOutput) => { + if (error) { + reject(error) + } else { + const errors = [] + for (let result of lintOutput.results) { + for (let message of result.messages) { + errors.push({ + path: result.filePath, + lineNumber: message.line, + message: message.message, + rule: message.ruleId + }) } - resolve(errors) } - }) + resolve(errors) + } }) }) } diff --git a/script/package-lock.json b/script/package-lock.json index bc5efb6b2..6bda42fdf 100644 --- a/script/package-lock.json +++ b/script/package-lock.json @@ -22,6 +22,11 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz", "integrity": "sha512-LAQ1d4OPfSJ/BMbI2DuizmYrrkD9JMaTdi2hQTlI53lQ4kRQPyZQRS4CYQ7O66bnBBnP/oYdRxbk++X0xuFU6A==" }, + "@types/node": { + "version": "11.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz", + "integrity": "sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -376,22 +381,6 @@ "postcss-value-parser": "^3.2.3" } }, - "aws-sdk": { - "version": "2.275.1", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.275.1.tgz", - "integrity": "sha512-lcpgoiHLhdcolUT7aJdg/CmlYO5ecf+3A+4dIceO72mFovCWZde1Rvr07QNbQ8gT0paqr5j2rs2b6c23Y/K0RQ==", - "requires": { - "buffer": "4.9.1", - "events": "1.1.1", - "ieee754": "1.1.8", - "jmespath": "0.15.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "3.1.0", - "xml2js": "0.4.19" - } - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -625,11 +614,6 @@ } } }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" - }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -691,6 +675,11 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, "boom": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", @@ -737,16 +726,6 @@ "electron-to-chromium": "^1.3.47" } }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", @@ -920,6 +899,19 @@ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz", "integrity": "sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ==" }, + "cheerio": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", + "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + } + }, "chownr": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", @@ -969,11 +961,6 @@ "restore-cursor": "^1.0.1" } }, - "cli-width": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.1.1.tgz", - "integrity": "sha1-pNKT72frt7iNSk1CwMzwDE0eNm0=" - }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -1259,11 +1246,38 @@ "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=" }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + }, + "dependencies": { + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + } + } + }, "css-value": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", "integrity": "sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo=" }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + }, "ctype": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", @@ -1668,14 +1682,6 @@ "is-obj": "^1.0.0" } }, - "duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", - "requires": { - "readable-stream": "~1.1.9" - } - }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", @@ -1685,78 +1691,51 @@ "jsbn": "~0.1.0" } }, - "editor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz", - "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=" - }, "electron-chromedriver": { - "version": "3.0.0-beta.1", - "resolved": "https://registry.npmjs.org/electron-chromedriver/-/electron-chromedriver-3.0.0-beta.1.tgz", - "integrity": "sha512-S8KuOWqTISSfeVccrh1XjqR5tARdkAbF93azz8TvuNJTKoIw7V54mLKoyhi2Hj5UvKuPLcrHmfa4B9Uh45A5lA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/electron-chromedriver/-/electron-chromedriver-2.0.0.tgz", + "integrity": "sha512-kERk/Wzhc9RzW9jUKXA5kJc4m8BlL6c9p5QH+CrIlst0saeqZL1Up7vzD4ZOnuBDpAVBBYJ4jhkAKIssf8ZlXg==", "requires": { "electron-download": "^4.1.0", "extract-zip": "^1.6.5" } }, "electron-download": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz", - "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.0.tgz", + "integrity": "sha1-v5MsdG8vh//MCdHdRy8v9rkYeEU=", "requires": { - "debug": "^3.0.0", + "debug": "^2.2.0", "env-paths": "^1.0.0", - "fs-extra": "^4.0.1", + "fs-extra": "^2.0.0", "minimist": "^1.2.0", - "nugget": "^2.0.1", + "nugget": "^2.0.0", "path-exists": "^3.0.0", - "rc": "^1.2.1", - "semver": "^5.4.1", - "sumchecker": "^2.0.2" + "rc": "^1.1.2", + "semver": "^5.3.0", + "sumchecker": "^2.0.1" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", "requires": { "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" + "jsonfile": "^2.1.0" } }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" } } }, "electron-link": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/electron-link/-/electron-link-0.3.3.tgz", - "integrity": "sha512-dxFY3o3E9kBkOyfaY66PWabK1AL5Re8qmy2Abz2/VhVkp2KtvUn6BZODTm9XpC0REgWxlQfRyHlNTlXRBPrWCQ==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/electron-link/-/electron-link-0.3.2.tgz", + "integrity": "sha512-V7QmtujzWgvrW5BI2CKmIRF+q+pkrFO5Lecd8TpibbBz+FfW5WQ4kCN0sZjNaUOMtGGroCib721OqIDEynjwgA==", "requires": { "ast-util": "^0.6.0", "encoding-down": "~5.0.0", @@ -1774,9 +1753,9 @@ "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==" }, "core-js": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.3.tgz", - "integrity": "sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ==" + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" }, "esprima": { "version": "4.0.1", @@ -1810,13 +1789,12 @@ } }, "electron-mksnapshot": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/electron-mksnapshot/-/electron-mksnapshot-3.0.10.tgz", - "integrity": "sha512-Toy6sAC3t9tgvq1kUYsx+4TRNPDj7Bzoo+1gx5FD8Q0YCS+tq+ter62Ot6dBXCKG9SwoaGBz84b++MgO0VobYw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/electron-mksnapshot/-/electron-mksnapshot-2.0.0.tgz", + "integrity": "sha512-OoZwZJNKgHP+DwhCGVTJEuDSeb478hOzAbHeg7dKGCHDbKKmUWmjGc+pEjxGutpqQ3Mn8hCdLzdx2c/lAJcTLA==", "requires": { "electron-download": "^4.1.0", - "extract-zip": "^1.6.5", - "temp": "^0.8.3" + "extract-zip": "^1.6.5" } }, "electron-osx-sign": { @@ -2236,11 +2214,6 @@ "es5-ext": "~0.10.14" } }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, "execa": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.4.0.tgz", @@ -2991,49 +2964,6 @@ "assert-plus": "^1.0.0" } }, - "ghauth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ghauth/-/ghauth-2.0.1.tgz", - "integrity": "sha1-ebfWiwvPjn0IUqI7FHU539MUrPY=", - "requires": { - "bl": "~0.9.4", - "hyperquest": "~1.2.0", - "mkdirp": "~0.5.0", - "read": "~1.0.5", - "xtend": "~4.0.0" - }, - "dependencies": { - "bl": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", - "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", - "requires": { - "readable-stream": "~1.0.26" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - } - } - }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -3049,14 +2979,6 @@ "resolved": "https://registry.npmjs.org/github-url-from-username-repo/-/github-url-from-username-repo-1.0.2.tgz", "integrity": "sha1-fdeTMNKr5pwQws73lxTJchV5Hfo=" }, - "github-url-to-object": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/github-url-to-object/-/github-url-to-object-1.6.0.tgz", - "integrity": "sha1-iR73+7+rqP7XFRCs2xtOk0apcNw=", - "requires": { - "is-url": "^1.1.0" - } - }, "glob": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", @@ -3345,47 +3267,6 @@ "sshpk": "^1.7.0" } }, - "hyperquest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hyperquest/-/hyperquest-1.2.0.tgz", - "integrity": "sha1-OeH+9miI3Hzg3sbA3YFPb8iUStU=", - "requires": { - "duplexer2": "~0.0.2", - "through2": "~0.6.3" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - } - } - }, "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", @@ -3394,11 +3275,6 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" - }, "ignore": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", @@ -3456,33 +3332,6 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, - "inquirer": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.8.5.tgz", - "integrity": "sha1-29dAz2yjtzEpamPOb22WGFHzNt8=", - "requires": { - "ansi-regex": "^1.1.1", - "chalk": "^1.0.0", - "cli-width": "^1.0.1", - "figures": "^1.3.5", - "lodash": "^3.3.1", - "readline2": "^0.1.1", - "rx": "^2.4.3", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz", - "integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=" - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - } - } - }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -3746,11 +3595,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" - }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -3804,11 +3648,6 @@ "resolved": "https://registry.npmjs.org/jju/-/jju-1.3.0.tgz", "integrity": "sha1-2t2e8BkkvHKLA/L3l5vb1i96Kqo=" }, - "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" - }, "joanna": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/joanna/-/joanna-0.0.10.tgz", @@ -4463,11 +4302,6 @@ "regex-cache": "^0.4.2" } }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", @@ -4647,11 +4481,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, "mv": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", @@ -4746,9 +4575,9 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "node-abi": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.7.0.tgz", - "integrity": "sha512-egTtvNoZLMjwxkL/5iiJKYKZgn2im0zP+G+PncMxICYGiD3aZtXUvEsDmu0pF8gpASvLZyD8v53qi1/ELaRZpg==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.7.1.tgz", + "integrity": "sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw==", "requires": { "semver": "^5.4.1" }, @@ -7572,6 +7401,14 @@ "set-blocking": "~2.0.0" } }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, "nugget": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", @@ -7800,6 +7637,14 @@ "error-ex": "^1.2.0" } }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "requires": { + "@types/node": "*" + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -7904,11 +7749,6 @@ } } }, - "pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" - }, "plist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz", @@ -8279,43 +8119,6 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, - "publish-release": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/publish-release/-/publish-release-1.6.0.tgz", - "integrity": "sha512-t+NFXTQN/VDTg9yJ8Uv5ZWQ7Ud1T5W1tPW+bmuo4g6uYVQTVNiwwRF6Td3EtXFTOafpEXJQEZqGG7IvIJwLwIg==", - "requires": { - "async": "^0.9.0", - "ghauth": "^2.0.0", - "github-url-to-object": "^1.4.2", - "inquirer": "^0.8.2", - "lodash": "^3.6.0", - "mime": "^1.3.4", - "minimist": "^1.1.1", - "pkginfo": "^0.3.0", - "pretty-bytes": "^1.0.4", - "progress-stream": "^1.0.1", - "request": "^2.54.0", - "single-line-log": "^0.4.1", - "string-editor": "^0.1.0" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - }, - "single-line-log": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz", - "integrity": "sha1-h6VWSfdJ14PsDc2AToFA2Yc8fO4=" - } - } - }, "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -8408,14 +8211,6 @@ "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-0.5.1.tgz", "integrity": "sha1-0L3PXSgKnRwp2m8RjMzizhU87x0=" }, - "read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "requires": { - "mute-stream": "~0.0.4" - } - }, "read-installed": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-3.1.3.tgz", @@ -8543,35 +8338,6 @@ "once": "^1.3.0" } }, - "readline2": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-0.1.1.tgz", - "integrity": "sha1-mUQ7pug7gw7zBRv9fcJBqCco1Wg=", - "requires": { - "mute-stream": "0.0.4", - "strip-ansi": "^2.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz", - "integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=" - }, - "mute-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz", - "integrity": "sha1-qSGZYKbV1dBGWXruUSUsZlX3F34=" - }, - "strip-ansi": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz", - "integrity": "sha1-32LBqpTtLxFOHQ8h/R1QSCt5pg4=", - "requires": { - "ansi-regex": "^1.0.0" - } - } - } - }, "recast": { "version": "0.10.33", "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.33.tgz", @@ -8788,24 +8554,6 @@ "uuid": "^3.1.0" } }, - "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "requires": { - "lodash": "^4.13.1" - } - }, - "request-promise-native": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", - "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", - "requires": { - "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8919,11 +8667,6 @@ "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.8.tgz", "integrity": "sha512-+GztYEPRpIsQoCSraWHDBs9WVy4eVME16zhOtDB4H9J4xN0XRhknnmLOl+4gRgZtu8dpp9N/utSPjKH/xmDzXg==" }, - "rx": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz", - "integrity": "sha1-Ia3H2A8CACr1Da6X/Z2/JIdV9WY=" - }, "rx-lite": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", @@ -8947,11 +8690,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" - }, "season": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/season/-/season-5.3.0.tgz", @@ -9356,19 +9094,6 @@ } } }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" - }, - "string-editor": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/string-editor/-/string-editor-0.1.2.tgz", - "integrity": "sha1-9f8bWsSu16xsL7jeI20VUbIPYdA=", - "requires": { - "editor": "^1.0.0" - } - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -9978,15 +9703,20 @@ } }, "terser": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-3.8.1.tgz", - "integrity": "sha512-FRin3gKQ0vm0xPPLuxw1FqpVgv1b2pBpYCaFb5qe6A7sD749Fnq1VbDiX3CEFM0BV0fqDzFtBfgmxhxCdzKQIg==", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.10.8.tgz", + "integrity": "sha512-GQJHWJ/vbx0EgRk+lBMONMmKaT+ifeo/XgT/hi3KpzEEFOERVyFuJSVXH8grcmJjiqKY35ds8rBCxvABUeyyuQ==", "requires": { - "commander": "~2.16.0", + "commander": "~2.17.1", "source-map": "~0.6.1", "source-map-support": "~0.5.6" }, "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10332,11 +10062,6 @@ "unist-util-is": "^2.1.1" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -10680,20 +10405,6 @@ "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" - }, "xmldom": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", diff --git a/script/package.json b/script/package.json index 10c6494cc..0cb86bc51 100644 --- a/script/package.json +++ b/script/package.json @@ -4,14 +4,14 @@ "dependencies": { "7zip-bin": "^4.0.2", "async": "2.0.1", - "aws-sdk": "^2.5.2", "babel-core": "5.8.38", + "cheerio": "1.0.0-rc.2", "coffeelint": "1.15.7", "colors": "1.1.2", "donna": "1.0.16", - "electron-chromedriver": "~3.0.0-beta.1", - "electron-link": "0.3.3", - "electron-mksnapshot": "~3.0.10", + "electron-chromedriver": "~2.0", + "electron-link": "0.3.2", + "electron-mksnapshot": "~2.0", "electron-packager": "7.3.0", "electron-winstaller": "2.6.4", "fs-admin": "^0.1.5", @@ -28,10 +28,7 @@ "npm": "6.2.0", "passwd-user": "2.1.0", "pegjs": "0.9.0", - "publish-release": "^1.6.0", "random-seed": "^0.3.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", "season": "5.3.0", "semver": "5.3.0", "standard": "8.4.0", diff --git a/script/postprocess-junit-results b/script/postprocess-junit-results new file mode 100755 index 000000000..bd7cbb4f8 --- /dev/null +++ b/script/postprocess-junit-results @@ -0,0 +1,87 @@ +#!/usr/bin/env node + +const yargs = require('yargs') + +const argv = yargs + .usage('Usage: $0 [options]') + .help('help') + .option('search-folder', { + string: true, + demandOption: true, + requiresArg: true, + describe: 'Directory to search for JUnit XML results' + }) + .option('test-results-files', { + string: true, + demandOption: true, + requiresArg: true, + describe: 'Glob that matches JUnit XML files within searchFolder' + }) + .wrap(yargs.terminalWidth()) + .argv + +const fs = require('fs') +const path = require('path') +const glob = require('glob') +const cheerio = require('cheerio') + +function discoverTestFiles() { + return new Promise((resolve, reject) => { + glob(argv.testResultsFiles, {cwd: argv.searchFolder}, (err, paths) => { + if (err) { + reject(err) + } else { + resolve(paths) + } + }) + }) +} + +async function postProcessJUnitXML(junitXmlPath) { + const fullPath = path.resolve(argv.searchFolder, junitXmlPath) + const friendlyName = path.basename(junitXmlPath, '.xml').replace(/-+/g, ' ') + + console.log(`${fullPath}: loading`) + + const original = await new Promise((resolve, reject) => { + fs.readFile(fullPath, {encoding: 'utf8'}, (err, content) => { + if (err) { + reject(err) + } else { + resolve(content) + } + }) + }) + + const $ = cheerio.load(original, { xmlMode: true }) + $('testcase').attr('name', (i, oldName) => `[${friendlyName}] ${oldName}`) + const modified = $.xml() + + await new Promise((resolve, reject) => { + fs.writeFile(fullPath, modified, {encoding: 'utf8'}, err => { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) + console.log(`${fullPath}: complete`) +} + +;(async function() { + const testResultFiles = await discoverTestFiles() + console.log(`Post-processing ${testResultFiles.length} JUnit XML files`) + + await Promise.all( + testResultFiles.map(postProcessJUnitXML) + ) + + console.log(`${testResultFiles.length} JUnit XML files complete`) +})().then( + () => process.exit(0), + err => { + console.error(err.stack || err) + process.exit(1) + } +) diff --git a/script/postprocess-junit-results.cmd b/script/postprocess-junit-results.cmd new file mode 100644 index 000000000..50ee48f37 --- /dev/null +++ b/script/postprocess-junit-results.cmd @@ -0,0 +1,5 @@ +@IF EXIST "%~dp0\node.exe" ( + "%~dp0\node.exe" "%~dp0\postprocess-junit-results" %* +) ELSE ( + node "%~dp0\postprocess-junit-results" %* +) diff --git a/script/publish-release b/script/publish-release deleted file mode 100644 index a2d56b9a8..000000000 --- a/script/publish-release +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env node - -'use strict' - -const path = require('path') -const glob = require('glob') -const publishRelease = require('publish-release') -const uploadToS3 = require('./lib/upload-to-s3') -const uploadLinuxPackages = require('./lib/upload-linux-packages') - -const CONFIG = require('./config') - -const yargs = require('yargs') -const argv = yargs - .usage('Usage: $0 [options]') - .help('help') - .describe('assets-path', 'Path to the folder where all release assets are stored') - .describe('s3-path', 'Indicates the S3 path in which the assets should be uploaded') - .describe('create-github-release', 'Creates a GitHub release for this build, draft if release branch or public if Nightly') - .describe('linux-repo-name', 'If specified, uploads Linux packages to the given repo name on packagecloud') - .wrap(yargs.terminalWidth()) - .argv - -const assetsPath = argv.assetsPath || path.join(CONFIG.repositoryRootPath, 'out') -const assetsPattern = '/**/*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*|atom-api.json)' -const assets = glob.sync(assetsPattern, { root: assetsPath, nodir: true }) -const bucketPath = argv.s3Path || `releases/v${CONFIG.computedAppVersion}/` - -if (!assets || assets.length === 0) { - console.error(`No assets found under specified path: ${assetsPath}`) - process.exit(1) -} - -async function uploadRelease() { - console.log(`Uploading ${assets.length} release assets for ${CONFIG.computedAppVersion} to S3 under '${bucketPath}'`) - - await uploadToS3( - process.env.ATOM_RELEASES_S3_KEY, - process.env.ATOM_RELEASES_S3_SECRET, - process.env.ATOM_RELEASES_S3_BUCKET, - bucketPath, - assets) - - if (argv.linuxRepoName) { - await uploadLinuxPackages( - argv.linuxRepoName, - process.env.PACKAGE_CLOUD_API_KEY, - CONFIG.computedAppVersion, - assets) - } else { - console.log("Skipping upload of Linux packages") - } - - if (argv.createGithubRelease) { - console.log(`Creating GitHub release v${CONFIG.computedAppVersion}`) - const release = - await publishReleaseAsync({ - token: process.env.GITHUB_TOKEN, - owner: 'atom', - repo: CONFIG.channel !== 'nightly' ? 'atom' : 'atom-nightly-releases', - name: CONFIG.computedAppVersion, - tag: `v${CONFIG.computedAppVersion}`, - draft: false, - prerelease: CONFIG.channel !== 'stable', - reuseRelease: true, - skipIfPublished: true, - assets - }) - - console.log("Release published successfully: ", release.html_url) - } else { - console.log("Skipping GitHub release creation") - } -} - -async function publishReleaseAsync(options) { - return new Promise((resolve, reject) => { - publishRelease(options, (err, release) => { - if (err) { - reject(err) - } else { - resolve(release) - } - }) - }) -} - -uploadRelease().catch((err) => { - console.error('An error occurred while uploading the release:\n\n', err) - process.exit(1) -}) diff --git a/script/publish-release.cmd b/script/publish-release.cmd deleted file mode 100644 index 46e077a3c..000000000 --- a/script/publish-release.cmd +++ /dev/null @@ -1,5 +0,0 @@ -@IF EXIST "%~dp0\node.exe" ( - "%~dp0\node.exe" "%~dp0\publish-release" %* -) ELSE ( - node "%~dp0\publish-release" %* -) diff --git a/script/test b/script/test index 5270a3912..263858139 100755 --- a/script/test +++ b/script/test @@ -68,7 +68,8 @@ function prepareEnv (suiteName) { if (process.env.TEST_JUNIT_XML_ROOT) { // Tell Jasmine to output this suite's results as a JUnit XML file to a subdirectory of the root, so that a // CI system can interpret it. - const outputPath = path.join(process.env.TEST_JUNIT_XML_ROOT, suiteName, 'test-results.xml') + const fileName = suiteName + '.xml' + const outputPath = path.join(process.env.TEST_JUNIT_XML_ROOT, fileName) env.TEST_JUNIT_XML_PATH = outputPath } diff --git a/script/vsts/get-release-version.js b/script/vsts/get-release-version.js index 13fbfbea5..61d284d96 100644 --- a/script/vsts/get-release-version.js +++ b/script/vsts/get-release-version.js @@ -13,7 +13,7 @@ const argv = yargs .argv async function getReleaseVersion () { - let releaseVersion = appMetadata.version + let releaseVersion = process.env.ATOM_RELEASE_VERSION || appMetadata.version if (argv.nightly) { const releases = await request({ url: 'https://api.github.com/repos/atom/atom-nightly-releases/releases', @@ -42,16 +42,17 @@ async function getReleaseVersion () { if (!process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER) { // Only set the build number on non-PR builds as it causes build errors when // non-admins send PRs to the repo - console.log(`##vso[build.updatebuildnumber]${releaseVersion}+${process.env.BUILD_BUILDNUMBER}`) + console.log(`##vso[build.updatebuildnumber]${releaseVersion}+${process.env.BUILD_BUILDID}`) } // Write out some variables that indicate whether artifacts should be uploaded const buildBranch = process.env.BUILD_SOURCEBRANCHNAME const isReleaseBranch = process.env.IS_RELEASE_BRANCH || argv.nightly || buildBranch.match(/\d\.\d+-releases/) !== null const isSignedZipBranch = - process.env.IS_SIGNED_ZIP_BRANCH || - buildBranch.startsWith('electron-') || - buildBranch === 'master' && !process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER + !isReleaseBranch && + (process.env.IS_SIGNED_ZIP_BRANCH || + buildBranch.startsWith('electron-') || + buildBranch === 'master' && !process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER) console.log(`##vso[task.setvariable variable=IsReleaseBranch;isOutput=true]${isReleaseBranch}`) console.log(`##vso[task.setvariable variable=IsSignedZipBranch;isOutput=true]${isSignedZipBranch}`) } diff --git a/script/vsts/lib/release-notes.js b/script/vsts/lib/release-notes.js new file mode 100644 index 000000000..c7c9e7d41 --- /dev/null +++ b/script/vsts/lib/release-notes.js @@ -0,0 +1,135 @@ +const semver = require('semver') +const octokit = require('@octokit/rest')() +const changelog = require('pr-changelog') +const childProcess = require('child_process') + +module.exports.getRelease = async function (releaseVersion, githubToken) { + if (githubToken) { + octokit.authenticate({ + type: 'oauth', + token: githubToken + }) + } + + const releases = await octokit.repos.getReleases({owner: 'atom', repo: 'atom'}) + const release = releases.data.find(r => semver.eq(r.name, releaseVersion)) + + return { + exists: release !== undefined, + isDraft: release && release.draft, + releaseNotes: release ? release.body : undefined + } +} + +module.exports.generateForVersion = async function (releaseVersion, githubToken, oldReleaseNotes) { + let oldVersion = null + let oldVersionName = null + const parsedVersion = semver.parse(releaseVersion) + const newVersionBranch = getBranchForVersion(parsedVersion) + + if (githubToken) { + changelog.setGithubAccessToken(githubToken) + octokit.authenticate({ + type: 'oauth', + token: githubToken + }) + } + + if (parsedVersion.prerelease && parsedVersion.prerelease[0] === 'beta0') { + // For beta0 releases, stable hasn't been released yet so compare against + // the stable version's release branch + oldVersion = `${parsedVersion.major}.${parsedVersion.minor - 1}-releases` + oldVersionName = `v${parsedVersion.major}.${parsedVersion.minor - 1}.0` + } else { + let releases = await octokit.repos.getReleases({owner: 'atom', repo: 'atom'}) + oldVersion = 'v' + getPreviousRelease(releaseVersion, releases.data).name + oldVersionName = oldVersion + } + + const allChangesText = await changelog.getChangelog({ + owner: 'atom', + repo: 'atom', + fromTag: oldVersion, + toTag: newVersionBranch, + dependencyKey: 'packageDependencies', + changelogFormatter: function ({pullRequests, owner, repo, fromTag, toTag}) { + let prString = changelog.pullRequestsToString(pullRequests) + let title = repo + if (repo === 'atom') { + title = 'Atom Core' + fromTag = oldVersionName + toTag = releaseVersion + } + return `### [${title}](https://github.com/${owner}/${repo})\n\n${fromTag}...${toTag}\n\n${prString}` + } + }) + + const writtenReleaseNotes = + extractWrittenReleaseNotes(oldReleaseNotes) || + '**TODO**: Pull relevant changes here!' + + return `## Notable Changes\n +${writtenReleaseNotes}\n +
    +All Changes\n +${allChangesText} +
    +` +} + +module.exports.generateForNightly = async function (releaseVersion, githubToken) { + const releases = await octokit.repos.getReleases({owner: 'atom', repo: 'atom-nightly-releases'}) + const previousRelease = getPreviousRelease(releaseVersion, releases.data) + const oldReleaseNotes = previousRelease ? previousRelease.body : undefined + + const latestCommitResult = childProcess.spawnSync('git', ['rev-parse', '--short', 'HEAD']) + + if (latestCommitResult && oldReleaseNotes) { + const latestCommit = latestCommitResult.stdout.toString().trim() + const extractMatch = oldReleaseNotes.match(/atom\/atom\/compare\/([0-9a-f]{5,40})\.\.\.([0-9a-f]{5,40})/) + if (extractMatch) { + return `### Click [here](https://github.com/atom/atom/compare/${extractMatch[2]}...${latestCommit}) to see the changes included with this release! :atom: :night_with_stars:` + } + } + + return undefined +} + +function extractWrittenReleaseNotes (oldReleaseNotes) { + if (oldReleaseNotes) { + const extractMatch = oldReleaseNotes.match(/^## Notable Changes\r\n([\s\S]*)
    /) + if (extractMatch && extractMatch[1]) { + return extractMatch[1].trim() + } + } + + return undefined +} + +function getPreviousRelease (version, allReleases) { + const versionIsStable = semver.prerelease(version) === null + + // Make sure versions are sorted before using them + allReleases.sort((v1, v2) => semver.rcompare(v1.name, v2.name)) + + for (let release of allReleases) { + if (versionIsStable && semver.prerelease(release.name)) { + continue + } + + if (semver.lt(release.name, version)) { + return release + } + } + + return null +} + +function getBranchForVersion (version) { + let parsedVersion = version + if (!(version instanceof semver.SemVer)) { + parsedVersion = semver.parse(version) + } + + return `${parsedVersion.major}.${parsedVersion.minor}-releases` +} diff --git a/script/lib/upload-linux-packages.js b/script/vsts/lib/upload-linux-packages.js similarity index 100% rename from script/lib/upload-linux-packages.js rename to script/vsts/lib/upload-linux-packages.js diff --git a/script/lib/upload-to-s3.js b/script/vsts/lib/upload-to-s3.js similarity index 100% rename from script/lib/upload-to-s3.js rename to script/vsts/lib/upload-to-s3.js diff --git a/script/vsts/nightly-release.yml b/script/vsts/nightly-release.yml index 0d0a84dbc..0d83405ac 100644 --- a/script/vsts/nightly-release.yml +++ b/script/vsts/nightly-release.yml @@ -43,7 +43,7 @@ jobs: # This has to be done separately because VSTS inexplicably # exits the script block after `npm install` completes. - script: | - cd script + cd script\vsts npm install displayName: npm install @@ -54,7 +54,7 @@ jobs: displayName: Download Release Artifacts - script: | - $(Build.SourcesDirectory)\script\publish-release.cmd --create-github-release --assets-path "$(System.ArtifactsDirectory)" + node $(Build.SourcesDirectory)\script\vsts\upload-artifacts.js --create-github-release --assets-path "$(System.ArtifactsDirectory)" env: GITHUB_TOKEN: $(GITHUB_TOKEN) ATOM_RELEASE_VERSION: $(ReleaseVersion) diff --git a/script/vsts/package-lock.json b/script/vsts/package-lock.json index a17d03b85..f0de21de5 100644 --- a/script/vsts/package-lock.json +++ b/script/vsts/package-lock.json @@ -3,11 +3,34 @@ "requires": true, "lockfileVersion": 1, "dependencies": { + "@octokit/rest": { + "version": "15.9.5", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.9.5.tgz", + "integrity": "sha512-vJEHSTnI4UAbCDTjVSQljPeX81zsQVNj2ruM5Oj5gxOttHD0TcfWeElcJYoITCMxQTgN6Y+bJFo6/+/0CqoacA==", + "requires": { + "before-after-hook": "^1.1.0", + "btoa-lite": "^1.0.0", + "debug": "^3.1.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.0", + "lodash": "^4.17.4", + "node-fetch": "^2.1.1", + "url-template": "^2.0.8" + } + }, "@sindresorhus/is": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -24,6 +47,11 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, "archive-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz", @@ -39,6 +67,11 @@ } } }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", @@ -49,11 +82,39 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "aws-sdk": { + "version": "2.292.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.292.0.tgz", + "integrity": "sha512-1Btm3fPwyy/pILfKaByP1MmwrjHtmos1fSORDcbGdc7PGyA5w0Yo9Jh/eqZSqiXH1asQEX1ZzHfTbt69vl4EGQ==", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.8", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.1.0", + "xml2js": "0.4.19" + }, + "dependencies": { + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -64,10 +125,470 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=" + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "requires": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" + } + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, "base64-js": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", - "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, "bcrypt-pbkdf": { "version": "1.0.1", @@ -78,23 +599,53 @@ "tweetnacl": "^0.14.3" } }, + "before-after-hook": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.1.0.tgz", + "integrity": "sha512-VOMDtYPwLbIncTxNoSzRyvaMxtXmLWLUqr8k5AfC1BzLk34HvBXaQX8snOwQZ4c0aX8aSERqtJSiI9/m2u5kuA==" + }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", + "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "readable-stream": "~1.0.26" } }, - "buffer": { - "version": "3.6.0", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", - "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", + "bluebird": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "base64-js": "0.0.8", + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=" + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } } }, "buffer-alloc": { @@ -152,6 +703,22 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + } + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -168,6 +735,23 @@ "url-to-options": "^1.0.1" } }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-width": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.1.1.tgz", + "integrity": "sha1-pNKT72frt7iNSk1CwMzwDE0eNm0=" + }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", @@ -212,6 +796,11 @@ "graceful-readlink": ">= 1.0.0" } }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, "config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", @@ -226,11 +815,24 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -239,6 +841,14 @@ "assert-plus": "^1.0.0" } }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -338,12 +948,12 @@ "dependencies": { "file-type": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" }, "get-stream": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "requires": { "object-assign": "^4.0.1", @@ -383,6 +993,27 @@ } } }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "~1.1.9" + }, + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -397,6 +1028,11 @@ "jsbn": "~0.1.0" } }, + "editor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz", + "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=" + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -413,11 +1049,39 @@ "is-arrayish": "^0.2.1" } }, + "es6-promise": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", + "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "expand-home-dir": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/expand-home-dir/-/expand-home-dir-0.0.3.tgz", + "integrity": "sha1-ct6KBIbMKKO71wRjU5iCW1tign0=" + }, "ext-list": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", @@ -463,6 +1127,15 @@ "pend": "~1.2.0" } }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, "file-type": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", @@ -514,6 +1187,35 @@ "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "fs-constants": { @@ -534,9 +1236,14 @@ "npm-conf": "^1.1.0" } }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "getpass": { @@ -547,6 +1254,48 @@ "assert-plus": "^1.0.0" } }, + "ghauth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ghauth/-/ghauth-2.0.1.tgz", + "integrity": "sha1-ebfWiwvPjn0IUqI7FHU539MUrPY=", + "requires": { + "bl": "~0.9.4", + "hyperquest": "~1.2.0", + "mkdirp": "~0.5.0", + "read": "~1.0.5", + "xtend": "~4.0.0" + } + }, + "github": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/github/-/github-0.1.16.tgz", + "integrity": "sha1-iV0qhbD+t5gNiawM5PRNyqA/F7U=" + }, + "github-url-to-object": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/github-url-to-object/-/github-url-to-object-1.6.0.tgz", + "integrity": "sha1-iR73+7+rqP7XFRCs2xtOk0apcNw=", + "requires": { + "is-url": "^1.1.0" + } + }, + "glob": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "integrity": "sha1-CqI1kxpKlqwT1g/6wvuHe9btT1g=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, "got": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", @@ -602,6 +1351,14 @@ "har-schema": "^2.0.0" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", @@ -625,6 +1382,15 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -635,10 +1401,45 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + } + }, + "hyperquest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperquest/-/hyperquest-1.2.0.tgz", + "integrity": "sha1-OeH+9miI3Hzg3sbA3YFPb8iUStU=", + "requires": { + "duplexer2": "~0.0.2", + "through2": "~0.6.3" + } + }, "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } }, "inherits": { "version": "2.0.3", @@ -650,15 +1451,50 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, + "inquirer": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.8.5.tgz", + "integrity": "sha1-29dAz2yjtzEpamPOb22WGFHzNt8=", + "requires": { + "ansi-regex": "^1.1.1", + "chalk": "^1.0.0", + "cli-width": "^1.0.1", + "figures": "^1.3.5", + "lodash": "^3.3.1", + "readline2": "^0.1.1", + "rx": "^2.4.3", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz", + "integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=" + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, "into-stream": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", "requires": { "from2": "^2.1.1", "p-is-promise": "^1.1.0" } }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -677,6 +1513,14 @@ "builtin-modules": "^1.0.0" } }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -715,15 +1559,20 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "isstream": { "version": "0.1.2", @@ -739,12 +1588,27 @@ "is-object": "^1.0.1" } }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -814,6 +1678,23 @@ "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -834,6 +1715,33 @@ } } }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", @@ -852,6 +1760,54 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "node-fetch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz", + "integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA==" + }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -914,6 +1870,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -950,7 +1911,7 @@ }, "p-is-promise": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" }, "p-timeout": { @@ -969,6 +1930,14 @@ "error-ex": "^1.2.0" } }, + "parse-link-header": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-0.4.1.tgz", + "integrity": "sha1-9r1hXcZxP9QJNc6XlF5NP1Iu3xQ=", + "requires": { + "xtend": "~4.0.0" + } + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -977,6 +1946,11 @@ "pinkie-promise": "^2.0.0" } }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, "path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", @@ -1015,21 +1989,149 @@ "pinkie": "^2.0.0" } }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" + }, + "pr-changelog": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/pr-changelog/-/pr-changelog-0.3.2.tgz", + "integrity": "sha512-CQayjupdJ4KB6EYzRuW7aXgXpRT0iOMuCn9DROC4ygqXpSgHW+eDzFz3l50icJq25yq1tnrLSBvh55aG+gy+MQ==", + "requires": { + "babel-plugin-syntax-async-functions": "^6.1.4", + "babel-plugin-transform-regenerator": "^6.1.4", + "babel-polyfill": "^6.1.4", + "babel-preset-es2015": "^6.1.4", + "bluebird": "^3.0.6", + "expand-home-dir": "0.0.3", + "github": "^0.1.16", + "moment": "^2.10.6", + "parse-link-header": "^0.4.1", + "yargs": "^3.31.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + }, + "yargs": { + "version": "3.32.0", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "requires": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + } + } + }, "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" }, + "pretty-bytes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.1.0" + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, + "progress-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", + "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", + "requires": { + "speedometer": "~0.1.2", + "through2": "~0.2.3" + }, + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "through2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "requires": { + "readable-stream": "~1.1.9", + "xtend": "~2.1.1" + } + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" }, + "publish-release": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/publish-release/-/publish-release-1.6.0.tgz", + "integrity": "sha512-t+NFXTQN/VDTg9yJ8Uv5ZWQ7Ud1T5W1tPW+bmuo4g6uYVQTVNiwwRF6Td3EtXFTOafpEXJQEZqGG7IvIJwLwIg==", + "requires": { + "async": "^0.9.0", + "ghauth": "^2.0.0", + "github-url-to-object": "^1.4.2", + "inquirer": "^0.8.2", + "lodash": "^3.6.0", + "mime": "^1.3.4", + "minimist": "^1.1.1", + "pkginfo": "^0.3.0", + "pretty-bytes": "^1.0.4", + "progress-stream": "^1.0.1", + "request": "^2.54.0", + "single-line-log": "^0.4.1", + "string-editor": "^0.1.0" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -1042,7 +2144,7 @@ }, "query-string": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "resolved": "http://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "requires": { "decode-uri-component": "^0.2.0", @@ -1050,6 +2152,19 @@ "strict-uri-encode": "^1.0.0" } }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "~0.0.4" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -1070,17 +2185,103 @@ } }, "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "readline2": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-0.1.1.tgz", + "integrity": "sha1-mUQ7pug7gw7zBRv9fcJBqCco1Wg=", + "requires": { + "mute-stream": "0.0.4", + "strip-ansi": "^2.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz", + "integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=" + }, + "mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha1-qSGZYKbV1dBGWXruUSUsZlX3F34=" + }, + "strip-ansi": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz", + "integrity": "sha1-32LBqpTtLxFOHQ8h/R1QSCt5pg4=", + "requires": { + "ansi-regex": "^1.0.0" + } + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "requires": { + "jsesc": "~0.5.0" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" } }, "request": { @@ -1146,6 +2347,11 @@ "lowercase-keys": "^1.0.0" } }, + "rx": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz", + "integrity": "sha1-Ia3H2A8CACr1Da6X/Z2/JIdV9WY=" + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1156,6 +2362,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, "seek-bzip": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", @@ -1165,15 +2376,25 @@ } }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "single-line-log": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz", + "integrity": "sha1-h6VWSfdJ14PsDc2AToFA2Yc8fO4=" + }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -1218,6 +2439,11 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" }, + "speedometer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", + "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=" + }, "sshpk": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", @@ -1244,6 +2470,14 @@ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, + "string-editor": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/string-editor/-/string-editor-0.1.2.tgz", + "integrity": "sha1-9f8bWsSu16xsL7jeI20VUbIPYdA=", + "requires": { + "editor": "^1.0.0" + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -1255,12 +2489,9 @@ } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "strip-ansi": { "version": "3.0.1", @@ -1286,6 +2517,14 @@ "is-natural-number": "^4.0.1" } }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, "strip-outer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", @@ -1294,25 +2533,77 @@ "escape-string-regexp": "^1.0.2" } }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, "tar-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", - "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "requires": { "bl": "^1.0.0", - "buffer-alloc": "^1.1.0", + "buffer-alloc": "^1.2.0", "end-of-stream": "^1.0.0", "fs-constants": "^1.0.0", "readable-stream": "^2.3.0", - "to-buffer": "^1.1.0", + "to-buffer": "^1.1.1", "xtend": "^4.0.0" + }, + "dependencies": { + "bl": { + "version": "1.2.2", + "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", @@ -1323,6 +2614,11 @@ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + }, "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", @@ -1331,6 +2627,11 @@ "punycode": "^1.4.1" } }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, "trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", @@ -1354,12 +2655,50 @@ "optional": true }, "unbzip2-stream": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.2.5.tgz", - "integrity": "sha512-izD3jxT8xkzwtXRUZjtmRwKnZoeECrfZ8ra/ketwOcusbZEp4mjULMnJOCfTDZBgGQAAY1AJ/IgxcwkavcX9Og==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz", + "integrity": "sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==", "requires": { "buffer": "^3.0.1", "through": "^2.3.6" + }, + "dependencies": { + "base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" + }, + "buffer": { + "version": "3.6.0", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", + "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", + "requires": { + "base64-js": "0.0.8", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } } }, "url-parse-lax": { @@ -1370,6 +2709,11 @@ "prepend-http": "^2.0.0" } }, + "url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" + }, "url-to-options": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", @@ -1428,6 +2772,20 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/script/vsts/package.json b/script/vsts/package.json index 10d544292..12f5d1449 100644 --- a/script/vsts/package.json +++ b/script/vsts/package.json @@ -2,9 +2,15 @@ "name": "atom-release-scripts", "description": "Atom release scripts", "dependencies": { + "@octokit/rest": "^15.9.5", + "aws-sdk": "^2.5.2", "download": "^7.1.0", + "glob": "7.0.3", + "pr-changelog": "^0.3.2", + "publish-release": "^1.6.0", "request": "^2.87.0", "request-promise-native": "^1.0.5", + "semver": "5.3.0", "yargs": "4.8.1" } } diff --git a/script/vsts/platforms/linux.yml b/script/vsts/platforms/linux.yml index eb70878c7..3ebb2a6bd 100644 --- a/script/vsts/platforms/linux.yml +++ b/script/vsts/platforms/linux.yml @@ -25,9 +25,25 @@ jobs: CI: true CI_PROVIDER: VSTS ATOM_JASMINE_REPORTER: list + TEST_JUNIT_XML_ROOT: $(Common.TestResultsDirectory)/junit displayName: Run tests condition: and(succeeded(), ne(variables['Atom.SkipTests'], 'true')) + - script: script/postprocess-junit-results --search-folder "${TEST_JUNIT_XML_ROOT}" --test-results-files "**/*.xml" + env: + TEST_JUNIT_XML_ROOT: $(Common.TestResultsDirectory)/junit + displayName: Post-process test results + condition: ne(variables['Atom.SkipTests'], 'true') + + - task: PublishTestResults@2 + inputs: + testResultsFormat: JUnit + searchFolder: $(Common.TestResultsDirectory)/junit + testResultsFiles: "**/*.xml" + mergeTestResults: true + testRunTitle: Linux + condition: ne(variables['Atom.SkipTests'], 'true') + - task: PublishBuildArtifacts@1 inputs: PathtoPublish: $(Build.SourcesDirectory)/out/atom.x86_64.rpm diff --git a/script/vsts/platforms/macos.yml b/script/vsts/platforms/macos.yml index 005f8b96f..0d8a28019 100644 --- a/script/vsts/platforms/macos.yml +++ b/script/vsts/platforms/macos.yml @@ -45,13 +45,35 @@ jobs: CI: true CI_PROVIDER: VSTS ATOM_JASMINE_REPORTER: list + TEST_JUNIT_XML_ROOT: $(Common.TestResultsDirectory)/junit displayName: Run tests condition: and(succeeded(), ne(variables['Atom.SkipTests'], 'true')) + - script: script/postprocess-junit-results --search-folder "${TEST_JUNIT_XML_ROOT}" --test-results-files "**/*.xml" + env: + TEST_JUNIT_XML_ROOT: $(Common.TestResultsDirectory)/junit + displayName: Post-process test results + condition: ne(variables['Atom.SkipTests'], 'true') + + - task: PublishTestResults@2 + inputs: + testResultsFormat: JUnit + searchFolder: $(Common.TestResultsDirectory)/junit + testResultsFiles: "**/*.xml" + mergeTestResults: true + testRunTitle: MacOS + condition: ne(variables['Atom.SkipTests'], 'true') + - script: | cp $(Build.SourcesDirectory)/out/*.zip $(Build.ArtifactStagingDirectory) displayName: Stage Artifacts + - script: | + mkdir -p $(Build.ArtifactStagingDirectory)/crash-reports + cp ${HOME}/Library/Logs/DiagnosticReports/*.crash $(Build.ArtifactStagingDirectory)/crash-reports + displayName: Stage Crash Reports + condition: failed() + - task: PublishBuildArtifacts@1 inputs: PathtoPublish: $(Build.ArtifactStagingDirectory)/atom-mac.zip @@ -75,3 +97,10 @@ jobs: ArtifactType: Container displayName: Upload atom-api.json condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/crash-reports + ArtifactName: crash-reports.zip + displayName: Upload Crash Reports + condition: failed() diff --git a/script/vsts/platforms/windows.yml b/script/vsts/platforms/windows.yml index 8f490b77c..019b59730 100644 --- a/script/vsts/platforms/windows.yml +++ b/script/vsts/platforms/windows.yml @@ -70,10 +70,28 @@ jobs: CI: true CI_PROVIDER: VSTS ATOM_JASMINE_REPORTER: list + TEST_JUNIT_XML_ROOT: $(Common.TestResultsDirectory)\junit BUILD_ARCH: $(buildArch) displayName: Run tests condition: and(succeeded(), ne(variables['Atom.SkipTests'], 'true')) + - script: > + node script\vsts\windows-run.js script\postprocess-junit-results.cmd + --search-folder %TEST_JUNIT_XML_ROOT% --test-results-files "**/*.xml" + env: + TEST_JUNIT_XML_ROOT: $(Common.TestResultsDirectory)\junit + displayName: Post-process test results + condition: ne(variables['Atom.SkipTests'], 'true') + + - task: PublishTestResults@2 + inputs: + testResultsFormat: JUnit + searchFolder: $(Common.TestResultsDirectory)\junit + testResultsFiles: "**/*.xml" + mergeTestResults: true + testRunTitle: Windows $(buildArch) + condition: ne(variables['Atom.SkipTests'], 'true') + - task: PublishBuildArtifacts@1 inputs: PathtoPublish: $(Build.SourcesDirectory)/out/atom-x64-windows.zip @@ -98,6 +116,15 @@ jobs: displayName: Upload atom-x64-$(ReleaseVersion)-full.nupkg condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true'), eq(variables['buildArch'], 'x64')) + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/atom-x64-$(ReleaseVersion)-delta.nupkg + ArtifactName: atom-x64-$(ReleaseVersion)-delta.nupkg + ArtifactType: Container + displayName: Upload atom-x64-$(ReleaseVersion)-delta.nupkg + condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true'), eq(variables['buildArch'], 'x64')) + continueOnError: true # Nightly builds don't produce delta packages yet, so don't fail the build + - task: PublishBuildArtifacts@1 inputs: PathtoPublish: $(Build.SourcesDirectory)/out/RELEASES-x64 @@ -130,6 +157,15 @@ jobs: displayName: Upload atom-$(ReleaseVersion)-full.nupkg condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true'), eq(variables['buildArch'], 'x86')) + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/atom-$(ReleaseVersion)-delta.nupkg + ArtifactName: atom-$(ReleaseVersion)-delta.nupkg + ArtifactType: Container + displayName: Upload atom-$(ReleaseVersion)-delta.nupkg + condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true'), eq(variables['buildArch'], 'x86')) + continueOnError: true # Nightly builds don't produce delta packages yet, so don't fail the build + - task: PublishBuildArtifacts@1 inputs: PathtoPublish: $(Build.SourcesDirectory)/out/RELEASES diff --git a/script/vsts/release-branch-build.yml b/script/vsts/release-branch-build.yml index 75e3ab627..de9d4e72f 100644 --- a/script/vsts/release-branch-build.yml +++ b/script/vsts/release-branch-build.yml @@ -1,6 +1,7 @@ trigger: - master - 1.* # VSTS only supports wildcards at the end +- electron-* resources: containers: @@ -49,7 +50,7 @@ jobs: # This has to be done separately because VSTS inexplicably # exits the script block after `npm install` completes. - script: | - cd script + cd script\vsts npm install displayName: npm install @@ -60,12 +61,23 @@ jobs: displayName: Download Release Artifacts - script: | - $(Build.SourcesDirectory)\script\publish-release.cmd --assets-path "$(System.ArtifactsDirectory)" --s3-path "vsts-artifacts/$(Build.BuildId)/" + node $(Build.SourcesDirectory)\script\vsts\upload-artifacts.js --create-github-release --assets-path "$(System.ArtifactsDirectory)" --linux-repo-name "atom-staging" env: + GITHUB_TOKEN: $(GITHUB_TOKEN) ATOM_RELEASE_VERSION: $(ReleaseVersion) ATOM_RELEASES_S3_KEY: $(ATOM_RELEASES_S3_KEY) ATOM_RELEASES_S3_SECRET: $(ATOM_RELEASES_S3_SECRET) ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET) PACKAGE_CLOUD_API_KEY: $(PACKAGE_CLOUD_API_KEY) + displayName: Create Draft Release + condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true')) + + - script: | + node $(Build.SourcesDirectory)\script\vsts\upload-artifacts.js --assets-path "$(System.ArtifactsDirectory)" --s3-path "vsts-artifacts/$(Build.BuildId)/" + env: + ATOM_RELEASE_VERSION: $(ReleaseVersion) + ATOM_RELEASES_S3_KEY: $(ATOM_RELEASES_S3_KEY) + ATOM_RELEASES_S3_SECRET: $(ATOM_RELEASES_S3_SECRET) + ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET) displayName: Upload CI Artifacts to S3 condition: and(succeeded(), eq(variables['IsSignedZipBranch'], 'true')) diff --git a/script/vsts/upload-artifacts.js b/script/vsts/upload-artifacts.js new file mode 100644 index 000000000..08fb27024 --- /dev/null +++ b/script/vsts/upload-artifacts.js @@ -0,0 +1,136 @@ +'use strict' + +const fs = require('fs') +const os = require('os') +const path = require('path') +const glob = require('glob') +const publishRelease = require('publish-release') +const releaseNotes = require('./lib/release-notes') +const uploadToS3 = require('./lib/upload-to-s3') +const uploadLinuxPackages = require('./lib/upload-linux-packages') + +const CONFIG = require('../config') + +const yargs = require('yargs') +const argv = yargs + .usage('Usage: $0 [options]') + .help('help') + .describe('assets-path', 'Path to the folder where all release assets are stored') + .describe('s3-path', 'Indicates the S3 path in which the assets should be uploaded') + .describe('create-github-release', 'Creates a GitHub release for this build, draft if release branch or public if Nightly') + .describe('linux-repo-name', 'If specified, uploads Linux packages to the given repo name on packagecloud') + .wrap(yargs.terminalWidth()) + .argv + +const releaseVersion = CONFIG.computedAppVersion +const isNightlyRelease = CONFIG.channel === 'nightly' +const assetsPath = argv.assetsPath || CONFIG.buildOutputPath +const assetsPattern = '/**/*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*|atom-api.json)' +const assets = glob.sync(assetsPattern, { root: assetsPath, nodir: true }) +const bucketPath = argv.s3Path || `releases/v${releaseVersion}/` + +if (!assets || assets.length === 0) { + console.error(`No assets found under specified path: ${assetsPath}`) + process.exit(1) +} + +async function uploadArtifacts () { + let releaseForVersion = + await releaseNotes.getRelease( + releaseVersion, + process.env.GITHUB_TOKEN) + + if (releaseForVersion.exists && !releaseForVersion.isDraft) { + console.log(`Published release already exists for ${releaseVersion}, skipping upload.`) + return + } + + console.log(`Uploading ${assets.length} release assets for ${releaseVersion} to S3 under '${bucketPath}'`) + + await uploadToS3( + process.env.ATOM_RELEASES_S3_KEY, + process.env.ATOM_RELEASES_S3_SECRET, + process.env.ATOM_RELEASES_S3_BUCKET, + bucketPath, + assets) + + if (argv.linuxRepoName) { + await uploadLinuxPackages( + argv.linuxRepoName, + process.env.PACKAGE_CLOUD_API_KEY, + releaseVersion, + assets) + } else { + console.log('\nNo Linux package repo name specified, skipping Linux package upload.') + } + + const oldReleaseNotes = releaseForVersion.releaseNotes + if (oldReleaseNotes) { + const oldReleaseNotesPath = path.resolve(os.tmpdir(), 'OLD_RELEASE_NOTES.md') + console.log(`Saving existing ${releaseVersion} release notes to ${oldReleaseNotesPath}`) + fs.writeFileSync(oldReleaseNotesPath, oldReleaseNotes, 'utf8') + + // This line instructs VSTS to upload the file as an artifact + console.log(`##vso[artifact.upload containerfolder=OldReleaseNotes;artifactname=OldReleaseNotes;]${oldReleaseNotesPath}`) + } + + if (argv.createGithubRelease) { + console.log(`\nGenerating new release notes for ${releaseVersion}`) + let newReleaseNotes = '' + if (isNightlyRelease) { + newReleaseNotes = + await releaseNotes.generateForNightly( + releaseVersion, + process.env.GITHUB_TOKEN, + oldReleaseNotes) + } else { + newReleaseNotes = + await releaseNotes.generateForVersion( + releaseVersion, + process.env.GITHUB_TOKEN, + oldReleaseNotes) + } + + console.log(`New release notes:\n\n${newReleaseNotes}`) + + console.log(`Creating GitHub release v${releaseVersion}`) + const release = + await publishReleaseAsync({ + token: process.env.GITHUB_TOKEN, + owner: 'atom', + repo: !isNightlyRelease ? 'atom' : 'atom-nightly-releases', + name: CONFIG.computedAppVersion, + notes: newReleaseNotes, + tag: `v${CONFIG.computedAppVersion}`, + draft: !isNightlyRelease, + prerelease: CONFIG.channel !== 'stable', + editRelease: true, + reuseRelease: true, + skipIfPublished: true, + assets + }) + + console.log('Release published successfully: ', release.html_url) + } else { + console.log('Skipping GitHub release creation') + } +} + +async function publishReleaseAsync (options) { + return new Promise((resolve, reject) => { + publishRelease(options, (err, release) => { + if (err) { + reject(err) + } else { + resolve(release) + } + }) + }) +} + +// Wrap the call the async function and catch errors from its promise because +// Node.js doesn't yet allow use of await at the script scope +uploadArtifacts().catch(err => { + console.error('An error occurred while uploading the release:\n\n', err) + process.exit(1) +}) diff --git a/spec/application-delegate-spec.js b/spec/application-delegate-spec.js index 5512c88ea..044e257b5 100644 --- a/spec/application-delegate-spec.js +++ b/spec/application-delegate-spec.js @@ -1,14 +1,13 @@ /** @babel */ -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' import ApplicationDelegate from '../src/application-delegate' describe('ApplicationDelegate', function () { describe('set/getTemporaryWindowState', function () { it('can serialize object trees containing redundant child object references', async function () { const applicationDelegate = new ApplicationDelegate() - const childObject = {c: 1} - const sentObject = {a: childObject, b: childObject} + const childObject = { c: 1 } + const sentObject = { a: childObject, b: childObject } await applicationDelegate.setTemporaryWindowState(sentObject) const receivedObject = await applicationDelegate.getTemporaryWindowState() diff --git a/spec/async-spec-helpers.js b/spec/async-spec-helpers.js index 90cb85e23..840a04b2a 100644 --- a/spec/async-spec-helpers.js +++ b/spec/async-spec-helpers.js @@ -1,38 +1,7 @@ -function beforeEach (fn) { - global.beforeEach(() => { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -function afterEach (fn) { - global.afterEach(() => { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) -} - -;['it', 'fit', 'ffit', 'fffit'].forEach(name => { - exports[name] = (description, fn) => { - if (fn === undefined) { - global[name](description) - return - } - - global[name](description, () => { - const result = fn() - if (result instanceof Promise) { - waitsForPromise(() => result) - } - }) - } -}) - -async function conditionPromise (condition, description = 'anonymous condition') { +async function conditionPromise ( + condition, + description = 'anonymous condition' +) { const startTime = Date.now() while (true) { @@ -54,16 +23,6 @@ function timeoutPromise (timeout) { }) } -function waitsForPromise (fn) { - const promise = fn() - global.waitsFor('spec promise to resolve', done => { - promise.then(done, error => { - jasmine.getEnv().currentSpec.fail(error) - done() - }) - }) -} - function emitterEventPromise (emitter, event, timeout = 15000) { return new Promise((resolve, reject) => { const timeoutHandle = setTimeout(() => { @@ -76,34 +35,6 @@ function emitterEventPromise (emitter, event, timeout = 15000) { }) } -function promisify (original) { - return function (...args) { - return new Promise((resolve, reject) => { - args.push((err, ...results) => { - if (err) { - reject(err) - } else { - resolve(...results) - } - }) - - return original(...args) - }) - } -} - -function promisifySome (obj, fnNames) { - const result = {} - for (const fnName of fnNames) { - result[fnName] = promisify(obj[fnName]) - } - return result -} - -exports.afterEach = afterEach -exports.beforeEach = beforeEach exports.conditionPromise = conditionPromise exports.emitterEventPromise = emitterEventPromise -exports.promisify = promisify -exports.promisifySome = promisifySome exports.timeoutPromise = timeoutPromise diff --git a/spec/atom-environment-spec.js b/spec/atom-environment-spec.js index 5e9617374..537617d56 100644 --- a/spec/atom-environment-spec.js +++ b/spec/atom-environment-spec.js @@ -1,5 +1,4 @@ -const {it, fit, ffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers') -const _ = require('underscore-plus') +const { conditionPromise } = require('./async-spec-helpers') const fs = require('fs') const path = require('path') const temp = require('temp').track() @@ -15,26 +14,26 @@ describe('AtomEnvironment', () => { describe('window sizing methods', () => { describe('::getPosition and ::setPosition', () => { let originalPosition = null - beforeEach(() => originalPosition = atom.getPosition()) + beforeEach(() => (originalPosition = atom.getPosition())) afterEach(() => atom.setPosition(originalPosition.x, originalPosition.y)) it('sets the position of the window, and can retrieve the position just set', () => { atom.setPosition(22, 45) - expect(atom.getPosition()).toEqual({x: 22, y: 45}) + expect(atom.getPosition()).toEqual({ x: 22, y: 45 }) }) }) describe('::getSize and ::setSize', () => { let originalSize = null - beforeEach(() => originalSize = atom.getSize()) + beforeEach(() => (originalSize = atom.getSize())) afterEach(() => atom.setSize(originalSize.width, originalSize.height)) it('sets the size of the window, and can retrieve the size just set', async () => { const newWidth = originalSize.width - 12 const newHeight = originalSize.height - 23 await atom.setSize(newWidth, newHeight) - expect(atom.getSize()).toEqual({width: newWidth, height: newHeight}) + expect(atom.getSize()).toEqual({ width: newWidth, height: newHeight }) }) }) }) @@ -67,9 +66,9 @@ describe('AtomEnvironment', () => { it('will open the dev tools when an error is triggered', async () => { try { - a + 1 + a + 1 // eslint-disable-line no-undef } catch (e) { - window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + window.onerror(e.toString(), 'abc', 2, 3, e) } await devToolsPromise @@ -88,10 +87,10 @@ describe('AtomEnvironment', () => { let error = null atom.onWillThrowError(willThrowSpy) try { - a + 1 + a + 1 // eslint-disable-line no-undef } catch (e) { error = e - window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + window.onerror(e.toString(), 'abc', 2, 3, e) } delete willThrowSpy.mostRecentCall.args[0].preventDefault @@ -109,9 +108,9 @@ describe('AtomEnvironment', () => { atom.onWillThrowError(willThrowSpy) try { - a + 1 + a + 1 // eslint-disable-line no-undef } catch (e) { - window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + window.onerror(e.toString(), 'abc', 2, 3, e) } expect(willThrowSpy).toHaveBeenCalled() @@ -122,16 +121,16 @@ describe('AtomEnvironment', () => { describe('::onDidThrowError', () => { let didThrowSpy = null - beforeEach(() => didThrowSpy = jasmine.createSpy()) + beforeEach(() => (didThrowSpy = jasmine.createSpy())) it('is called when there is an error', () => { let error = null atom.onDidThrowError(didThrowSpy) try { - a + 1 + a + 1 // eslint-disable-line no-undef } catch (e) { error = e - window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + window.onerror(e.toString(), 'abc', 2, 3, e) } expect(didThrowSpy).toHaveBeenCalledWith({ message: error.toString(), @@ -165,22 +164,24 @@ describe('AtomEnvironment', () => { describe('if passed a callback function', () => { it("calls the callback with the assertion failure's error object", () => { let error = null - atom.assert(false, 'a == b', e => error = e) + atom.assert(false, 'a == b', e => (error = e)) expect(error).toBe(errors[0]) }) }) describe('if passed metadata', () => { it("assigns the metadata on the assertion failure's error object", () => { - atom.assert(false, 'a == b', {foo: 'bar'}) - expect(errors[0].metadata).toEqual({foo: 'bar'}) + atom.assert(false, 'a == b', { foo: 'bar' }) + expect(errors[0].metadata).toEqual({ foo: 'bar' }) }) }) describe('when Atom has been built from source', () => { it('throws an error', () => { atom.isReleasedVersion.andReturn(false) - expect(() => atom.assert(false, 'testing')).toThrow('Assertion failed: testing') + expect(() => atom.assert(false, 'testing')).toThrow( + 'Assertion failed: testing' + ) }) }) }) @@ -195,9 +196,9 @@ describe('AtomEnvironment', () => { }) describe('saving and loading', () => { - beforeEach(() => atom.enablePersistence = true) + beforeEach(() => (atom.enablePersistence = true)) - afterEach(() => atom.enablePersistence = false) + afterEach(() => (atom.enablePersistence = false)) it('selects the state based on the current project paths', async () => { jasmine.useRealClock() @@ -210,7 +211,7 @@ describe('AtomEnvironment', () => { }) spyOn(atom, 'getLoadSettings').andCallFake(() => loadSettings) - spyOn(atom, 'serialize').andReturn({stuff: 'cool'}) + spyOn(atom, 'serialize').andReturn({ stuff: 'cool' }) atom.project.setPaths([dir1, dir2]) @@ -221,7 +222,7 @@ describe('AtomEnvironment', () => { expect(await atom.loadState()).toBeFalsy() loadSettings.initialPaths = [dir2, dir1] - expect(await atom.loadState()).toEqual({stuff: 'cool'}) + expect(await atom.loadState()).toEqual({ stuff: 'cool' }) }) it('saves state when the CPU is idle after a keydown or mousedown event', () => { @@ -231,7 +232,9 @@ describe('AtomEnvironment', () => { const idleCallbacks = [] atomEnv.initialize({ window: { - requestIdleCallback (callback) { idleCallbacks.push(callback) }, + requestIdleCallback (callback) { + idleCallbacks.push(callback) + }, addEventListener () {}, removeEventListener () {} }, @@ -244,16 +247,16 @@ describe('AtomEnvironment', () => { atomEnv.document.dispatchEvent(keydown) advanceClock(atomEnv.saveStateDebounceInterval) idleCallbacks.shift()() - expect(atomEnv.saveState).toHaveBeenCalledWith({isUnloading: false}) - expect(atomEnv.saveState).not.toHaveBeenCalledWith({isUnloading: true}) + expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: false }) + expect(atomEnv.saveState).not.toHaveBeenCalledWith({ isUnloading: true }) atomEnv.saveState.reset() const mousedown = new MouseEvent('mousedown') atomEnv.document.dispatchEvent(mousedown) advanceClock(atomEnv.saveStateDebounceInterval) idleCallbacks.shift()() - expect(atomEnv.saveState).toHaveBeenCalledWith({isUnloading: false}) - expect(atomEnv.saveState).not.toHaveBeenCalledWith({isUnloading: true}) + expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: false }) + expect(atomEnv.saveState).not.toHaveBeenCalledWith({ isUnloading: true }) atomEnv.destroy() }) @@ -265,7 +268,9 @@ describe('AtomEnvironment', () => { const idleCallbacks = [] atomEnv.initialize({ window: { - requestIdleCallback (callback) { idleCallbacks.push(callback) }, + requestIdleCallback (callback) { + idleCallbacks.push(callback) + }, addEventListener () {}, removeEventListener () {} }, @@ -278,7 +283,7 @@ describe('AtomEnvironment', () => { atomEnv.document.dispatchEvent(mousedown) expect(atomEnv.saveState).not.toHaveBeenCalled() await atomEnv.prepareToUnloadEditorWindow() - expect(atomEnv.saveState).toHaveBeenCalledWith({isUnloading: true}) + expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: true }) advanceClock(atomEnv.saveStateDebounceInterval) idleCallbacks.shift()() @@ -294,11 +299,13 @@ describe('AtomEnvironment', () => { }) it('serializes the project state with all the options supplied in saveState', async () => { - spyOn(atom.project, 'serialize').andReturn({foo: 42}) + spyOn(atom.project, 'serialize').andReturn({ foo: 42 }) - await atom.saveState({anyOption: 'any option'}) + await atom.saveState({ anyOption: 'any option' }) expect(atom.project.serialize.calls.length).toBe(1) - expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({anyOption: 'any option'}) + expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({ + anyOption: 'any option' + }) }) it('serializes the text editor registry', async () => { @@ -309,20 +316,22 @@ describe('AtomEnvironment', () => { const atom2 = new AtomEnvironment({ applicationDelegate: atom.applicationDelegate, window: document.createElement('div'), - document: Object.assign( - document.createElement('div'), - { - body: document.createElement('div'), - head: document.createElement('div') - } - ) + document: Object.assign(document.createElement('div'), { + body: document.createElement('div'), + head: document.createElement('div') + }) }) - atom2.initialize({document, window}) + atom2.initialize({ document, window }) await atom2.deserialize(atom.serialize()) await atom2.packages.activatePackage('language-text') const editor2 = atom2.workspace.getActiveTextEditor() - expect(editor2.getBuffer().getLanguageMode().getLanguageId()).toBe('text.plain') + expect( + editor2 + .getBuffer() + .getLanguageMode() + .getLanguageId() + ).toBe('text.plain') atom2.destroy() }) @@ -335,10 +344,13 @@ describe('AtomEnvironment', () => { }) spyOn(atom.notifications, 'addError') - await atom.deserialize({project: 'should work'}) - expect(atom.notifications.addError).toHaveBeenCalledWith('Unable to open project directory', { - description: 'Project directory `/foo` is no longer on disk.' - }) + await atom.deserialize({ project: 'should work' }) + expect(atom.notifications.addError).toHaveBeenCalledWith( + 'Unable to open project directory', + { + description: 'Project directory `/foo` is no longer on disk.' + } + ) }) it('accumulates and reports two errors with one notification', async () => { @@ -349,10 +361,14 @@ describe('AtomEnvironment', () => { }) spyOn(atom.notifications, 'addError') - await atom.deserialize({project: 'should work'}) - expect(atom.notifications.addError).toHaveBeenCalledWith('Unable to open 2 project directories', { - description: 'Project directories `/foo` and `/wat` are no longer on disk.' - }) + await atom.deserialize({ project: 'should work' }) + expect(atom.notifications.addError).toHaveBeenCalledWith( + 'Unable to open 2 project directories', + { + description: + 'Project directories `/foo` and `/wat` are no longer on disk.' + } + ) }) it('accumulates and reports three+ errors with one notification', async () => { @@ -363,17 +379,23 @@ describe('AtomEnvironment', () => { }) spyOn(atom.notifications, 'addError') - await atom.deserialize({project: 'should work'}) - expect(atom.notifications.addError).toHaveBeenCalledWith('Unable to open 4 project directories', { - description: 'Project directories `/foo`, `/wat`, `/stuff`, and `/things` are no longer on disk.' - }) + await atom.deserialize({ project: 'should work' }) + expect(atom.notifications.addError).toHaveBeenCalledWith( + 'Unable to open 4 project directories', + { + description: + 'Project directories `/foo`, `/wat`, `/stuff`, and `/things` are no longer on disk.' + } + ) }) }) }) describe('openInitialEmptyEditorIfNecessary', () => { describe('when there are no paths set', () => { - beforeEach(() => spyOn(atom, 'getLoadSettings').andReturn({initialPaths: []})) + beforeEach(() => + spyOn(atom, 'getLoadSettings').andReturn({ initialPaths: [] }) + ) it('opens an empty buffer', () => { spyOn(atom.workspace, 'open') @@ -396,7 +418,9 @@ describe('AtomEnvironment', () => { describe('when the project has a path', () => { beforeEach(() => { - spyOn(atom, 'getLoadSettings').andReturn({initialPaths: ['something']}) + spyOn(atom, 'getLoadSettings').andReturn({ + initialPaths: ['something'] + }) spyOn(atom.workspace, 'open') }) @@ -410,7 +434,6 @@ describe('AtomEnvironment', () => { describe('adding a project folder', () => { it('does nothing if the user dismisses the file picker', () => { const initialPaths = atom.project.getPaths() - const tempDirectory = temp.mkdirSync('a-new-directory') spyOn(atom, 'pickFolder').andCallFake(callback => callback(null)) atom.addProjectFolder() expect(atom.project.getPaths()).toEqual(initialPaths) @@ -423,9 +446,11 @@ describe('AtomEnvironment', () => { }) it('adds the selected folder to the project', async () => { - const initialPaths = atom.project.setPaths([]) + atom.project.setPaths([]) const tempDirectory = temp.mkdirSync('a-new-directory') - spyOn(atom, 'pickFolder').andCallFake(callback => callback([tempDirectory])) + spyOn(atom, 'pickFolder').andCallFake(callback => + callback([tempDirectory]) + ) await atom.addProjectFolder() expect(atom.project.getPaths()).toEqual([tempDirectory]) expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalled() @@ -437,7 +462,9 @@ describe('AtomEnvironment', () => { beforeEach(() => { spyOn(atom, 'getStateKey').andCallFake(dirs => dirs.join(':')) - spyOn(atom, 'loadState').andCallFake(async (key) => key === __dirname ? state : null) + spyOn(atom, 'loadState').andCallFake(async key => + key === __dirname ? state : null + ) spyOn(atom, 'attemptRestoreProjectStateForPaths') spyOn(atom, 'pickFolder').andCallFake(callback => callback([__dirname])) atom.project.setPaths([]) @@ -446,7 +473,10 @@ describe('AtomEnvironment', () => { describe('when there are no project folders', () => { it('attempts to restore the project state', async () => { await atom.addProjectFolder() - expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [__dirname]) + expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith( + state, + [__dirname] + ) expect(atom.project.getPaths()).toEqual([]) }) }) @@ -481,7 +511,9 @@ describe('AtomEnvironment', () => { fs.writeFileSync(filePath2, 'def') fs.writeFileSync(filePath3, 'ghi') - const env1 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate}) + const env1 = new AtomEnvironment({ + applicationDelegate: atom.applicationDelegate + }) env1.project.setPaths([projectPath]) await env1.workspace.open(filePath1) await env1.workspace.open(filePath2) @@ -489,8 +521,14 @@ describe('AtomEnvironment', () => { const env1State = env1.serialize() env1.destroy() - const env2 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate}) - await env2.attemptRestoreProjectStateForPaths(env1State, [projectPath], [filePath2]) + const env2 = new AtomEnvironment({ + applicationDelegate: atom.applicationDelegate + }) + await env2.attemptRestoreProjectStateForPaths( + env1State, + [projectPath], + [filePath2] + ) const restoredURIs = env2.workspace.getPaneItems().map(p => p.getURI()) expect(restoredURIs).toEqual([filePath1, filePath2, filePath3]) env2.destroy() @@ -500,12 +538,18 @@ describe('AtomEnvironment', () => { it("doesn't prompt the user to restore state", () => { const dock = atom.workspace.getLeftDock() dock.getActivePane().addItem({ - getTitle () { return 'title' }, + getTitle () { + return 'title' + }, element: document.createElement('div') }) const state = {} spyOn(atom, 'confirm') - atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename]) + atom.attemptRestoreProjectStateForPaths( + state, + [__dirname], + [__filename] + ) expect(atom.confirm).not.toHaveBeenCalled() }) }) @@ -527,7 +571,11 @@ describe('AtomEnvironment', () => { spyOn(atom.project, 'addPath') spyOn(atom.workspace, 'open') const state = Symbol() - atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename]) + atom.attemptRestoreProjectStateForPaths( + state, + [__dirname], + [__filename] + ) expect(atom.confirm).toHaveBeenCalled() }) }) @@ -539,7 +587,11 @@ describe('AtomEnvironment', () => { spyOn(atom.workspace, 'open') const state = Symbol() - atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename]) + atom.attemptRestoreProjectStateForPaths( + state, + [__dirname], + [__filename] + ) expect(atom.confirm).toHaveBeenCalled() await conditionPromise(() => atom.project.addPath.callCount === 1) @@ -554,7 +606,11 @@ describe('AtomEnvironment', () => { spyOn(atom, 'open') const state = Symbol() - atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename]) + atom.attemptRestoreProjectStateForPaths( + state, + [__dirname], + [__filename] + ) expect(atom.confirm).toHaveBeenCalled() await conditionPromise(() => atom.open.callCount === 1) expect(atom.open).toHaveBeenCalledWith({ @@ -571,8 +627,16 @@ describe('AtomEnvironment', () => { it('saves the BlobStore so it can be loaded after reload', () => { const configDirPath = temp.mkdirSync('atom-spec-environment') const fakeBlobStore = jasmine.createSpyObj('blob store', ['save']) - const atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, enablePersistence: true}) - atomEnvironment.initialize({configDirPath, blobStore: fakeBlobStore, window, document}) + const atomEnvironment = new AtomEnvironment({ + applicationDelegate: atom.applicationDelegate, + enablePersistence: true + }) + atomEnvironment.initialize({ + configDirPath, + blobStore: fakeBlobStore, + window, + document + }) atomEnvironment.unloadEditorWindow() @@ -584,16 +648,19 @@ describe('AtomEnvironment', () => { describe('::destroy()', () => { it('does not throw exceptions when unsubscribing from ipc events (regression)', async () => { - const configDirPath = temp.mkdirSync('atom-spec-environment') const fakeDocument = { addEventListener () {}, removeEventListener () {}, head: document.createElement('head'), body: document.createElement('body') } - const atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate}) - atomEnvironment.initialize({window, document: fakeDocument}) - spyOn(atomEnvironment.packages, 'loadPackages').andReturn(Promise.resolve()) + const atomEnvironment = new AtomEnvironment({ + applicationDelegate: atom.applicationDelegate + }) + atomEnvironment.initialize({ window, document: fakeDocument }) + spyOn(atomEnvironment.packages, 'loadPackages').andReturn( + Promise.resolve() + ) spyOn(atomEnvironment.packages, 'activate').andReturn(Promise.resolve()) spyOn(atomEnvironment, 'displayWindow').andReturn(Promise.resolve()) await atomEnvironment.startEditorWindow() @@ -606,17 +673,21 @@ describe('AtomEnvironment', () => { let atomEnvironment, envLoaded, spy beforeEach(() => { - let resolve = null - const promise = new Promise((r) => { resolve = r }) + let resolvePromise = null + const promise = new Promise(resolve => { + resolvePromise = resolve + }) envLoaded = () => { - resolve() + resolvePromise() return promise } atomEnvironment = new AtomEnvironment({ applicationDelegate: atom.applicationDelegate, - updateProcessEnv () { return promise } + updateProcessEnv () { + return promise + } }) - atomEnvironment.initialize({window, document}) + atomEnvironment.initialize({ window, document }) spy = jasmine.createSpy() }) @@ -650,23 +721,30 @@ describe('AtomEnvironment', () => { describe('when the opened path exists', () => { it('opens a file', async () => { const pathToOpen = __filename - await atom.openLocations([{pathToOpen}]) + await atom.openLocations([{ pathToOpen }]) expect(atom.project.getPaths()).toEqual([]) }) it('opens a directory as a project folder', async () => { const pathToOpen = __dirname - await atom.openLocations([{pathToOpen}]) - expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual([]) + await atom.openLocations([{ pathToOpen }]) + expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual( + [] + ) expect(atom.project.getPaths()).toEqual([pathToOpen]) }) }) describe('when the opened path does not exist', () => { it('opens it as a new file', async () => { - const pathToOpen = path.join(__dirname, 'this-path-does-not-exist.txt') - await atom.openLocations([{pathToOpen}]) - expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual([pathToOpen]) + const pathToOpen = path.join( + __dirname, + 'this-path-does-not-exist.txt' + ) + await atom.openLocations([{ pathToOpen }]) + expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual( + [pathToOpen] + ) expect(atom.project.getPaths()).toEqual([]) }) @@ -678,9 +756,9 @@ describe('AtomEnvironment', () => { const existingDir = path.join(__dirname, 'fixtures') await atom.openLocations([ - {pathToOpen: nonExistent, mustBeDirectory: true}, - {pathToOpen: existingFile, mustBeDirectory: true}, - {pathToOpen: existingDir, mustBeDirectory: true} + { pathToOpen: nonExistent, mustBeDirectory: true }, + { pathToOpen: existingFile, mustBeDirectory: true }, + { pathToOpen: existingDir, mustBeDirectory: true } ]) expect(atom.workspace.getTextEditors()).toEqual([]) @@ -688,7 +766,9 @@ describe('AtomEnvironment', () => { expect(atom.notifications.addWarning).toHaveBeenCalledWith( 'Unable to open project folders', - {description: `The directories \`${nonExistent}\` and \`${existingFile}\` do not exist.`} + { + description: `The directories \`${nonExistent}\` and \`${existingFile}\` do not exist.` + } ) }) }) @@ -697,15 +777,23 @@ describe('AtomEnvironment', () => { let serviceDisposable beforeEach(() => { - serviceDisposable = atom.packages.serviceHub.provide('atom.directory-provider', '0.1.0', { - directoryForURISync (uri) { - if (uri.startsWith('remote://')) { - return { getPath() { return uri } } - } else { - return null + serviceDisposable = atom.packages.serviceHub.provide( + 'atom.directory-provider', + '0.1.0', + { + directoryForURISync (uri) { + if (uri.startsWith('remote://')) { + return { + getPath () { + return uri + } + } + } else { + return null + } } } - }) + ) waitsFor(() => atom.project.directoryProviders.length > 0) }) @@ -717,7 +805,7 @@ describe('AtomEnvironment', () => { it("adds it to the project's paths as is", async () => { const pathToOpen = 'remote://server:7644/some/dir/path' spyOn(atom.project, 'addPath') - await atom.openLocations([{pathToOpen}]) + await atom.openLocations([{ pathToOpen }]) expect(atom.project.addPath).toHaveBeenCalledWith(pathToOpen) }) }) @@ -729,7 +817,11 @@ describe('AtomEnvironment', () => { beforeEach(() => { spyOn(atom, 'getStateKey').andCallFake(dirs => dirs.join(':')) spyOn(atom, 'loadState').andCallFake(function (key) { - if (key === __dirname) { return Promise.resolve(state) } else { return Promise.resolve(null) } + if (key === __dirname) { + return Promise.resolve(state) + } else { + return Promise.resolve(null) + } }) spyOn(atom, 'attemptRestoreProjectStateForPaths') }) @@ -737,8 +829,12 @@ describe('AtomEnvironment', () => { describe('when there are no project folders', () => { it('attempts to restore the project state', async () => { const pathToOpen = __dirname - await atom.openLocations([{pathToOpen}]) - expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [pathToOpen], []) + await atom.openLocations([{ pathToOpen }]) + expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith( + state, + [pathToOpen], + [] + ) expect(atom.project.getPaths()).toEqual([]) }) @@ -755,17 +851,28 @@ describe('AtomEnvironment', () => { }) await atom.openLocations([ - {pathToOpen: existingDir}, - {pathToOpen: missingDir, mustBeDirectory: true} + { pathToOpen: existingDir }, + { pathToOpen: missingDir, mustBeDirectory: true } ]) - expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [existingDir], []) + expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith( + state, + [existingDir], + [] + ) expect(atom.project.getPaths(), [existingDir]) }) it('opens the specified files', async () => { - await atom.openLocations([{pathToOpen: __dirname}, {pathToOpen: __filename}]) - expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [__dirname], [__filename]) + await atom.openLocations([ + { pathToOpen: __dirname }, + { pathToOpen: __filename } + ]) + expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith( + state, + [__dirname], + [__filename] + ) expect(atom.project.getPaths()).toEqual([]) }) }) @@ -775,7 +882,7 @@ describe('AtomEnvironment', () => { it('does not attempt to restore the project state, instead adding the project paths', async () => { const pathToOpen = path.join(__dirname, 'fixtures') - await atom.openLocations([{pathToOpen, forceAddToWindow: true}]) + await atom.openLocations([{ pathToOpen, forceAddToWindow: true }]) expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalled() expect(atom.project.getPaths()).toEqual([__dirname, pathToOpen]) }) @@ -783,8 +890,10 @@ describe('AtomEnvironment', () => { it('opens the specified files', async () => { const pathToOpen = path.join(__dirname, 'fixtures') const fileToOpen = path.join(pathToOpen, 'michelle-is-awesome.txt') - await atom.openLocations([{pathToOpen}, {pathToOpen: fileToOpen}]) - expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalledWith(state, [pathToOpen], [fileToOpen]) + await atom.openLocations([{ pathToOpen }, { pathToOpen: fileToOpen }]) + expect( + atom.attemptRestoreProjectStateForPaths + ).not.toHaveBeenCalledWith(state, [pathToOpen], [fileToOpen]) expect(atom.project.getPaths()).toEqual([__dirname, pathToOpen]) }) }) diff --git a/spec/atom-paths-spec.js b/spec/atom-paths-spec.js index f4bbbf2b7..cc4bb769b 100644 --- a/spec/atom-paths-spec.js +++ b/spec/atom-paths-spec.js @@ -1,14 +1,17 @@ /** @babel */ -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' -import {app} from 'remote' +import { app } from 'remote' import atomPaths from '../src/atom-paths' import fs from 'fs-plus' import path from 'path' const temp = require('temp').track() -describe("AtomPaths", () => { - const portableAtomHomePath = path.join(atomPaths.getAppDirectory(), '..', '.atom') +describe('AtomPaths', () => { + const portableAtomHomePath = path.join( + atomPaths.getAppDirectory(), + '..', + '.atom' + ) afterEach(() => { atomPaths.setAtomHome(app.getPath('home')) @@ -18,8 +21,9 @@ describe("AtomPaths", () => { describe('when a portable .atom folder exists', () => { beforeEach(() => { delete process.env.ATOM_HOME - if (!fs.existsSync(portableAtomHomePath)) + if (!fs.existsSync(portableAtomHomePath)) { fs.mkdirSync(portableAtomHomePath) + } }) afterEach(() => { @@ -69,6 +73,7 @@ describe("AtomPaths", () => { }) describe('setUserData', () => { + let tempAtomConfigPath = null let tempAtomHomePath = null let electronUserDataPath = null let defaultElectronUserDataPath = null diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index ab9e0ed70..563085934 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -1,5 +1,5 @@ const AutoUpdateManager = require('../src/auto-update-manager') -const {remote} = require('electron') +const { remote } = require('electron') const electronAutoUpdater = remote.require('electron').autoUpdater describe('AutoUpdateManager (renderer)', () => { @@ -8,7 +8,9 @@ describe('AutoUpdateManager (renderer)', () => { let autoUpdateManager beforeEach(() => { - autoUpdateManager = new AutoUpdateManager({applicationDelegate: atom.applicationDelegate}) + autoUpdateManager = new AutoUpdateManager({ + applicationDelegate: atom.applicationDelegate + }) autoUpdateManager.initialize() }) @@ -69,14 +71,16 @@ describe('AutoUpdateManager (renderer)', () => { autoUpdateManager.onUpdateError(spy) electronAutoUpdater.emit('error', {}, 'an error message') waitsFor(() => spy.callCount === 1) - runs(() => expect(autoUpdateManager.getErrorMessage()).toBe('an error message')) + runs(() => + expect(autoUpdateManager.getErrorMessage()).toBe('an error message') + ) }) }) describe('::platformSupportsUpdates', () => { let state, releaseChannel it('returns true on macOS and Windows when in stable', () => { - spyOn(autoUpdateManager, 'getState').andCallFake(() => state) + spyOn(autoUpdateManager, 'getState').andCallFake(() => state) spyOn(atom, 'getReleaseChannel').andCallFake(() => releaseChannel) state = 'idle' diff --git a/spec/command-installer-spec.js b/spec/command-installer-spec.js index b303d4954..1cf6af5c2 100644 --- a/spec/command-installer-spec.js +++ b/spec/command-installer-spec.js @@ -1,7 +1,6 @@ const path = require('path') const fs = require('fs-plus') const temp = require('temp').track() -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers'); const CommandInstaller = require('../src/command-installer') describe('CommandInstaller on #darwin', () => { @@ -12,14 +11,25 @@ describe('CommandInstaller on #darwin', () => { resourcesPath = temp.mkdirSync('atom-app') atomBinPath = path.join(resourcesPath, 'app', 'atom.sh') - apmBinPath = path.join(resourcesPath, 'app', 'apm', 'node_modules', '.bin', 'apm') + apmBinPath = path.join( + resourcesPath, + 'app', + 'apm', + 'node_modules', + '.bin', + 'apm' + ) fs.writeFileSync(atomBinPath, '') fs.writeFileSync(apmBinPath, '') fs.chmodSync(atomBinPath, '755') fs.chmodSync(apmBinPath, '755') - spyOn(CommandInstaller.prototype, 'getResourcesDirectory').andReturn(resourcesPath) - spyOn(CommandInstaller.prototype, 'getInstallDirectory').andReturn(installationPath) + spyOn(CommandInstaller.prototype, 'getResourcesDirectory').andReturn( + resourcesPath + ) + spyOn(CommandInstaller.prototype, 'getInstallDirectory').andReturn( + installationPath + ) }) afterEach(() => { @@ -32,7 +42,9 @@ describe('CommandInstaller on #darwin', () => { const appDelegate = jasmine.createSpyObj('appDelegate', ['confirm']) installer = new CommandInstaller(appDelegate) installer.initialize('2.0.2') - spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback(new Error('an error'))) + spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => + callback(new Error('an error')) + ) installer.installShellCommandsInteractively() @@ -43,7 +55,9 @@ describe('CommandInstaller on #darwin', () => { appDelegate.confirm.reset() installer.installAtomCommand.andCallFake((__, callback) => callback()) - spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback(new Error('another error'))) + spyOn(installer, 'installApmCommand').andCallFake((__, callback) => + callback(new Error('another error')) + ) installer.installShellCommandsInteractively() @@ -57,8 +71,12 @@ describe('CommandInstaller on #darwin', () => { const appDelegate = jasmine.createSpyObj('appDelegate', ['confirm']) installer = new CommandInstaller(appDelegate) installer.initialize('2.0.2') - spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback(undefined, 'atom')) - spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback(undefined, 'apm')) + spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => + callback(undefined, 'atom') + ) + spyOn(installer, 'installApmCommand').andCallFake((__, callback) => + callback(undefined, 'apm') + ) installer.installShellCommandsInteractively() @@ -81,9 +99,13 @@ describe('CommandInstaller on #darwin', () => { waitsFor(done => { installer.installAtomCommand(false, error => { expect(error).toBeNull() - expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath)) + expect(fs.realpathSync(installedAtomPath)).toBe( + fs.realpathSync(atomBinPath) + ) expect(fs.isExecutableSync(installedAtomPath)).toBe(true) - expect(fs.isFileSync(path.join(installationPath, 'atom-beta'))).toBe(false) + expect(fs.isFileSync(path.join(installationPath, 'atom-beta'))).toBe( + false + ) done() }) }) @@ -96,9 +118,13 @@ describe('CommandInstaller on #darwin', () => { waitsFor(done => { installer.installApmCommand(false, error => { expect(error).toBeNull() - expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath)) + expect(fs.realpathSync(installedApmPath)).toBe( + fs.realpathSync(apmBinPath) + ) expect(fs.isExecutableSync(installedApmPath)).toBeTruthy() - expect(fs.isFileSync(path.join(installationPath, 'apm-beta'))).toBe(false) + expect(fs.isFileSync(path.join(installationPath, 'apm-beta'))).toBe( + false + ) done() }) }) @@ -118,7 +144,9 @@ describe('CommandInstaller on #darwin', () => { waitsFor(done => { installer.installAtomCommand(false, error => { expect(error).toBeNull() - expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath)) + expect(fs.realpathSync(installedAtomPath)).toBe( + fs.realpathSync(atomBinPath) + ) expect(fs.isExecutableSync(installedAtomPath)).toBe(true) expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe(false) done() @@ -133,7 +161,9 @@ describe('CommandInstaller on #darwin', () => { waitsFor(done => { installer.installApmCommand(false, error => { expect(error).toBeNull() - expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath)) + expect(fs.realpathSync(installedApmPath)).toBe( + fs.realpathSync(apmBinPath) + ) expect(fs.isExecutableSync(installedApmPath)).toBeTruthy() expect(fs.isFileSync(path.join(installationPath, 'apm'))).toBe(false) done() @@ -155,7 +185,9 @@ describe('CommandInstaller on #darwin', () => { waitsFor(done => { installer.installAtomCommand(false, error => { expect(error).toBeNull() - expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath)) + expect(fs.realpathSync(installedAtomPath)).toBe( + fs.realpathSync(atomBinPath) + ) expect(fs.isExecutableSync(installedAtomPath)).toBe(true) expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe(false) done() @@ -170,9 +202,13 @@ describe('CommandInstaller on #darwin', () => { waitsFor(done => { installer.installApmCommand(false, error => { expect(error).toBeNull() - expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath)) + expect(fs.realpathSync(installedApmPath)).toBe( + fs.realpathSync(apmBinPath) + ) expect(fs.isExecutableSync(installedApmPath)).toBeTruthy() - expect(fs.isFileSync(path.join(installationPath, 'nightly'))).toBe(false) + expect(fs.isFileSync(path.join(installationPath, 'nightly'))).toBe( + false + ) done() }) }) diff --git a/spec/command-registry-spec.js b/spec/command-registry-spec.js index 03ef0cc34..597a72c35 100644 --- a/spec/command-registry-spec.js +++ b/spec/command-registry-spec.js @@ -1,289 +1,306 @@ -const CommandRegistry = require('../src/command-registry'); -const _ = require('underscore-plus'); -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers'); +const CommandRegistry = require('../src/command-registry') +const _ = require('underscore-plus') -describe("CommandRegistry", () => { - let registry, parent, child, grandchild; +describe('CommandRegistry', () => { + let registry, parent, child, grandchild beforeEach(() => { - parent = document.createElement("div"); - child = document.createElement("div"); - grandchild = document.createElement("div"); - parent.classList.add('parent'); - child.classList.add('child'); - grandchild.classList.add('grandchild'); - child.appendChild(grandchild); - parent.appendChild(child); - document.querySelector('#jasmine-content').appendChild(parent); + parent = document.createElement('div') + child = document.createElement('div') + grandchild = document.createElement('div') + parent.classList.add('parent') + child.classList.add('child') + grandchild.classList.add('grandchild') + child.appendChild(grandchild) + parent.appendChild(child) + document.querySelector('#jasmine-content').appendChild(parent) - registry = new CommandRegistry; - registry.attach(parent); - }); + registry = new CommandRegistry() + registry.attach(parent) + }) - afterEach(() => registry.destroy()); + afterEach(() => registry.destroy()) - describe("when a command event is dispatched on an element", () => { - it("invokes callbacks with selectors matching the target", () => { - let called = false; + describe('when a command event is dispatched on an element', () => { + it('invokes callbacks with selectors matching the target', () => { + let called = false registry.add('.grandchild', 'command', function (event) { - expect(this).toBe(grandchild); - expect(event.type).toBe('command'); - expect(event.eventPhase).toBe(Event.BUBBLING_PHASE); - expect(event.target).toBe(grandchild); - expect(event.currentTarget).toBe(grandchild); - called = true; - }); + expect(this).toBe(grandchild) + expect(event.type).toBe('command') + expect(event.eventPhase).toBe(Event.BUBBLING_PHASE) + expect(event.target).toBe(grandchild) + expect(event.currentTarget).toBe(grandchild) + called = true + }) - grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true})); - expect(called).toBe(true); - }); + grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true })) + expect(called).toBe(true) + }) - it("invokes callbacks with selectors matching ancestors of the target", () => { - const calls = []; + it('invokes callbacks with selectors matching ancestors of the target', () => { + const calls = [] registry.add('.child', 'command', function (event) { - expect(this).toBe(child); - expect(event.target).toBe(grandchild); - expect(event.currentTarget).toBe(child); - calls.push('child'); - }); + expect(this).toBe(child) + expect(event.target).toBe(grandchild) + expect(event.currentTarget).toBe(child) + calls.push('child') + }) - registry.add('.parent', 'command', function (event) { - expect(this).toBe(parent); - expect(event.target).toBe(grandchild); - expect(event.currentTarget).toBe(parent); - calls.push('parent'); - }); + registry.add('.parent', 'command', function (event) { + expect(this).toBe(parent) + expect(event.target).toBe(grandchild) + expect(event.currentTarget).toBe(parent) + calls.push('parent') + }) - grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true})); - expect(calls).toEqual(['child', 'parent']); - }); + grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true })) + expect(calls).toEqual(['child', 'parent']) + }) - it("invokes inline listeners prior to listeners applied via selectors", () => { - const calls = []; - registry.add('.grandchild', 'command', () => calls.push('grandchild')); - registry.add(child, 'command', () => calls.push('child-inline')); - registry.add('.child', 'command', () => calls.push('child')); - registry.add('.parent', 'command', () => calls.push('parent')); + it('invokes inline listeners prior to listeners applied via selectors', () => { + const calls = [] + registry.add('.grandchild', 'command', () => calls.push('grandchild')) + registry.add(child, 'command', () => calls.push('child-inline')) + registry.add('.child', 'command', () => calls.push('child')) + registry.add('.parent', 'command', () => calls.push('parent')) - grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true})); - expect(calls).toEqual(['grandchild', 'child-inline', 'child', 'parent']); - }); + grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true })) + expect(calls).toEqual(['grandchild', 'child-inline', 'child', 'parent']) + }) - it("orders multiple matching listeners for an element by selector specificity", () => { - child.classList.add('foo', 'bar'); - const calls = []; + it('orders multiple matching listeners for an element by selector specificity', () => { + child.classList.add('foo', 'bar') + const calls = [] - registry.add('.foo.bar', 'command', () => calls.push('.foo.bar')); - registry.add('.foo', 'command', () => calls.push('.foo')); - registry.add('.bar', 'command', () => calls.push('.bar')); // specificity ties favor commands added later, like CSS + registry.add('.foo.bar', 'command', () => calls.push('.foo.bar')) + registry.add('.foo', 'command', () => calls.push('.foo')) + registry.add('.bar', 'command', () => calls.push('.bar')) // specificity ties favor commands added later, like CSS - grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true})); - expect(calls).toEqual(['.foo.bar', '.bar', '.foo']); - }); + grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true })) + expect(calls).toEqual(['.foo.bar', '.bar', '.foo']) + }) - it("orders inline listeners by reverse registration order", () => { - const calls = []; - registry.add(child, 'command', () => calls.push('child1')); - registry.add(child, 'command', () => calls.push('child2')); - child.dispatchEvent(new CustomEvent('command', {bubbles: true})); - expect(calls).toEqual(['child2', 'child1']); - }); + it('orders inline listeners by reverse registration order', () => { + const calls = [] + registry.add(child, 'command', () => calls.push('child1')) + registry.add(child, 'command', () => calls.push('child2')) + child.dispatchEvent(new CustomEvent('command', { bubbles: true })) + expect(calls).toEqual(['child2', 'child1']) + }) - it("stops bubbling through ancestors when .stopPropagation() is called on the event", () => { - const calls = []; + it('stops bubbling through ancestors when .stopPropagation() is called on the event', () => { + const calls = [] - registry.add('.parent', 'command', () => calls.push('parent')); - registry.add('.child', 'command', () => calls.push('child-2')); - registry.add('.child', 'command', (event) => { - calls.push('child-1'); - event.stopPropagation(); - }); + registry.add('.parent', 'command', () => calls.push('parent')) + registry.add('.child', 'command', () => calls.push('child-2')) + registry.add('.child', 'command', event => { + calls.push('child-1') + event.stopPropagation() + }) - const dispatchedEvent = new CustomEvent('command', {bubbles: true}); - spyOn(dispatchedEvent, 'stopPropagation'); - grandchild.dispatchEvent(dispatchedEvent); - expect(calls).toEqual(['child-1', 'child-2']); - expect(dispatchedEvent.stopPropagation).toHaveBeenCalled(); - }); + const dispatchedEvent = new CustomEvent('command', { bubbles: true }) + spyOn(dispatchedEvent, 'stopPropagation') + grandchild.dispatchEvent(dispatchedEvent) + expect(calls).toEqual(['child-1', 'child-2']) + expect(dispatchedEvent.stopPropagation).toHaveBeenCalled() + }) - it("stops invoking callbacks when .stopImmediatePropagation() is called on the event", () => { - const calls = []; + it('stops invoking callbacks when .stopImmediatePropagation() is called on the event', () => { + const calls = [] - registry.add('.parent', 'command', () => calls.push('parent')); - registry.add('.child', 'command', () => calls.push('child-2')); - registry.add('.child', 'command', (event) => { - calls.push('child-1'); - event.stopImmediatePropagation(); - }); + registry.add('.parent', 'command', () => calls.push('parent')) + registry.add('.child', 'command', () => calls.push('child-2')) + registry.add('.child', 'command', event => { + calls.push('child-1') + event.stopImmediatePropagation() + }) - const dispatchedEvent = new CustomEvent('command', {bubbles: true}); - spyOn(dispatchedEvent, 'stopImmediatePropagation'); - grandchild.dispatchEvent(dispatchedEvent); - expect(calls).toEqual(['child-1']); - expect(dispatchedEvent.stopImmediatePropagation).toHaveBeenCalled(); - }); + const dispatchedEvent = new CustomEvent('command', { bubbles: true }) + spyOn(dispatchedEvent, 'stopImmediatePropagation') + grandchild.dispatchEvent(dispatchedEvent) + expect(calls).toEqual(['child-1']) + expect(dispatchedEvent.stopImmediatePropagation).toHaveBeenCalled() + }) - it("forwards .preventDefault() calls from the synthetic event to the original", () => { - registry.add('.child', 'command', event => event.preventDefault()); + it('forwards .preventDefault() calls from the synthetic event to the original', () => { + registry.add('.child', 'command', event => event.preventDefault()) - const dispatchedEvent = new CustomEvent('command', {bubbles: true}); - spyOn(dispatchedEvent, 'preventDefault'); - grandchild.dispatchEvent(dispatchedEvent); - expect(dispatchedEvent.preventDefault).toHaveBeenCalled(); - }); + const dispatchedEvent = new CustomEvent('command', { bubbles: true }) + spyOn(dispatchedEvent, 'preventDefault') + grandchild.dispatchEvent(dispatchedEvent) + expect(dispatchedEvent.preventDefault).toHaveBeenCalled() + }) - it("forwards .abortKeyBinding() calls from the synthetic event to the original", () => { - registry.add('.child', 'command', event => event.abortKeyBinding()); + it('forwards .abortKeyBinding() calls from the synthetic event to the original', () => { + registry.add('.child', 'command', event => event.abortKeyBinding()) - const dispatchedEvent = new CustomEvent('command', {bubbles: true}); - dispatchedEvent.abortKeyBinding = jasmine.createSpy('abortKeyBinding'); - grandchild.dispatchEvent(dispatchedEvent); - expect(dispatchedEvent.abortKeyBinding).toHaveBeenCalled(); - }); + const dispatchedEvent = new CustomEvent('command', { bubbles: true }) + dispatchedEvent.abortKeyBinding = jasmine.createSpy('abortKeyBinding') + grandchild.dispatchEvent(dispatchedEvent) + expect(dispatchedEvent.abortKeyBinding).toHaveBeenCalled() + }) - it("copies non-standard properties from the original event to the synthetic event", () => { - let syntheticEvent = null; - registry.add('.child', 'command', event => syntheticEvent = event); + it('copies non-standard properties from the original event to the synthetic event', () => { + let syntheticEvent = null + registry.add('.child', 'command', event => (syntheticEvent = event)) - const dispatchedEvent = new CustomEvent('command', {bubbles: true}); - dispatchedEvent.nonStandardProperty = 'testing'; - grandchild.dispatchEvent(dispatchedEvent); - expect(syntheticEvent.nonStandardProperty).toBe('testing'); - }); + const dispatchedEvent = new CustomEvent('command', { bubbles: true }) + dispatchedEvent.nonStandardProperty = 'testing' + grandchild.dispatchEvent(dispatchedEvent) + expect(syntheticEvent.nonStandardProperty).toBe('testing') + }) - it("allows listeners to be removed via a disposable returned by ::add", () => { - let calls = []; + it('allows listeners to be removed via a disposable returned by ::add', () => { + let calls = [] - const disposable1 = registry.add('.parent', 'command', () => calls.push('parent')); - const disposable2 = registry.add('.child', 'command', () => calls.push('child')); + const disposable1 = registry.add('.parent', 'command', () => + calls.push('parent') + ) + const disposable2 = registry.add('.child', 'command', () => + calls.push('child') + ) - disposable1.dispose(); - grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true})); - expect(calls).toEqual(['child']); + disposable1.dispose() + grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true })) + expect(calls).toEqual(['child']) - calls = []; - disposable2.dispose(); - grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true})); - expect(calls).toEqual([]); - }); + calls = [] + disposable2.dispose() + grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true })) + expect(calls).toEqual([]) + }) - it("allows multiple commands to be registered under one selector when called with an object", () => { - let calls = []; + it('allows multiple commands to be registered under one selector when called with an object', () => { + let calls = [] const disposable = registry.add('.child', { - 'command-1'() { - calls.push('command-1'); + 'command-1' () { + calls.push('command-1') }, - 'command-2'() { - calls.push('command-2'); + 'command-2' () { + calls.push('command-2') } - }); + }) - grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true})); - grandchild.dispatchEvent(new CustomEvent('command-2', {bubbles: true})); + grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true })) + grandchild.dispatchEvent(new CustomEvent('command-2', { bubbles: true })) - expect(calls).toEqual(['command-1', 'command-2']); + expect(calls).toEqual(['command-1', 'command-2']) - calls = []; - disposable.dispose(); - grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true})); - grandchild.dispatchEvent(new CustomEvent('command-2', {bubbles: true})); - expect(calls).toEqual([]); - }); + calls = [] + disposable.dispose() + grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true })) + grandchild.dispatchEvent(new CustomEvent('command-2', { bubbles: true })) + expect(calls).toEqual([]) + }) - it("invokes callbacks registered with ::onWillDispatch and ::onDidDispatch", () => { - const sequence = []; + it('invokes callbacks registered with ::onWillDispatch and ::onDidDispatch', () => { + const sequence = [] - registry.onDidDispatch(event => sequence.push(['onDidDispatch', event])); + registry.onDidDispatch(event => sequence.push(['onDidDispatch', event])) - registry.add('.grandchild', 'command', event => sequence.push(['listener', event])); + registry.add('.grandchild', 'command', event => + sequence.push(['listener', event]) + ) - registry.onWillDispatch(event => sequence.push(['onWillDispatch', event])); + registry.onWillDispatch(event => sequence.push(['onWillDispatch', event])) - grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true})); + grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true })) - expect(sequence[0][0]).toBe('onWillDispatch'); - expect(sequence[1][0]).toBe('listener'); - expect(sequence[2][0]).toBe('onDidDispatch'); + expect(sequence[0][0]).toBe('onWillDispatch') + expect(sequence[1][0]).toBe('listener') + expect(sequence[2][0]).toBe('onDidDispatch') - expect(sequence[0][1] === sequence[1][1] && sequence[1][1] === sequence[2][1]).toBe(true); - expect(sequence[0][1].constructor).toBe(CustomEvent); - expect(sequence[0][1].target).toBe(grandchild); - }); - }); + expect( + sequence[0][1] === sequence[1][1] && sequence[1][1] === sequence[2][1] + ).toBe(true) + expect(sequence[0][1].constructor).toBe(CustomEvent) + expect(sequence[0][1].target).toBe(grandchild) + }) + }) - describe("::add(selector, commandName, callback)", () => { - it("throws an error when called with an invalid selector", () => { - const badSelector = '<>'; - let addError = null; + describe('::add(selector, commandName, callback)', () => { + it('throws an error when called with an invalid selector', () => { + const badSelector = '<>' + let addError = null try { - registry.add(badSelector, 'foo:bar', () => {}); + registry.add(badSelector, 'foo:bar', () => {}) } catch (error) { - addError = error; + addError = error } - expect(addError.message).toContain(badSelector); - }); + expect(addError.message).toContain(badSelector) + }) - it("throws an error when called with a null callback and selector target", () => { - const badCallback = null; + it('throws an error when called with a null callback and selector target', () => { + const badCallback = null expect(() => { - registry.add('.selector', 'foo:bar', badCallback); - }).toThrow(new Error('Cannot register a command with a null listener.')); - }); + registry.add('.selector', 'foo:bar', badCallback) + }).toThrow(new Error('Cannot register a command with a null listener.')) + }) - it("throws an error when called with a null callback and object target", () => { - const badCallback = null; + it('throws an error when called with a null callback and object target', () => { + const badCallback = null expect(() => { - registry.add(document.body, 'foo:bar', badCallback); - }).toThrow(new Error('Cannot register a command with a null listener.')); - }); + registry.add(document.body, 'foo:bar', badCallback) + }).toThrow(new Error('Cannot register a command with a null listener.')) + }) - it("throws an error when called with an object listener without a didDispatch method", () => { + it('throws an error when called with an object listener without a didDispatch method', () => { const badListener = { title: 'a listener without a didDispatch callback', description: 'this should throw an error' - }; + } expect(() => { - registry.add(document.body, 'foo:bar', badListener); - }).toThrow(new Error('Listener must be a callback function or an object with a didDispatch method.')); - }); - }); + registry.add(document.body, 'foo:bar', badListener) + }).toThrow( + new Error( + 'Listener must be a callback function or an object with a didDispatch method.' + ) + ) + }) + }) - describe("::findCommands({target})", () => { - it("returns command descriptors that can be invoked on the target or its ancestors", () => { - registry.add('.parent', 'namespace:command-1', () => {}); - registry.add('.child', 'namespace:command-2', () => {}); - registry.add('.grandchild', 'namespace:command-3', () => {}); - registry.add('.grandchild.no-match', 'namespace:command-4', () => {}); + describe('::findCommands({target})', () => { + it('returns command descriptors that can be invoked on the target or its ancestors', () => { + registry.add('.parent', 'namespace:command-1', () => {}) + registry.add('.child', 'namespace:command-2', () => {}) + registry.add('.grandchild', 'namespace:command-3', () => {}) + registry.add('.grandchild.no-match', 'namespace:command-4', () => {}) - registry.add(grandchild, 'namespace:inline-command-1', () => {}); - registry.add(child, 'namespace:inline-command-2', () => {}); + registry.add(grandchild, 'namespace:inline-command-1', () => {}) + registry.add(child, 'namespace:inline-command-2', () => {}) - const commands = registry.findCommands({target: grandchild}); - const nonJqueryCommands = _.reject(commands, cmd => cmd.jQuery); + const commands = registry.findCommands({ target: grandchild }) + const nonJqueryCommands = _.reject(commands, cmd => cmd.jQuery) expect(nonJqueryCommands).toEqual([ - {name: 'namespace:inline-command-1', displayName: 'Namespace: Inline Command 1'}, - {name: 'namespace:command-3', displayName: 'Namespace: Command 3'}, - {name: 'namespace:inline-command-2', displayName: 'Namespace: Inline Command 2'}, - {name: 'namespace:command-2', displayName: 'Namespace: Command 2'}, - {name: 'namespace:command-1', displayName: 'Namespace: Command 1'} - ]); - }); + { + name: 'namespace:inline-command-1', + displayName: 'Namespace: Inline Command 1' + }, + { name: 'namespace:command-3', displayName: 'Namespace: Command 3' }, + { + name: 'namespace:inline-command-2', + displayName: 'Namespace: Inline Command 2' + }, + { name: 'namespace:command-2', displayName: 'Namespace: Command 2' }, + { name: 'namespace:command-1', displayName: 'Namespace: Command 1' } + ]) + }) - it("returns command descriptors with arbitrary metadata if set in a listener object", () => { - registry.add('.grandchild', 'namespace:command-1', () => {}); + it('returns command descriptors with arbitrary metadata if set in a listener object', () => { + registry.add('.grandchild', 'namespace:command-1', () => {}) registry.add('.grandchild', 'namespace:command-2', { displayName: 'Custom Command 2', metadata: { some: 'other', object: 'data' }, - didDispatch() {} - }); + didDispatch () {} + }) registry.add('.grandchild', 'namespace:command-3', { name: 'some:other:incorrect:commandname', displayName: 'Custom Command 3', @@ -291,10 +308,10 @@ describe("CommandRegistry", () => { some: 'other', object: 'data' }, - didDispatch() {} - }); + didDispatch () {} + }) - const commands = registry.findCommands({target: grandchild}); + const commands = registry.findCommands({ target: grandchild }) expect(commands).toEqual([ { displayName: 'Namespace: Command 1', @@ -303,143 +320,163 @@ describe("CommandRegistry", () => { { displayName: 'Custom Command 2', metadata: { - some : 'other', - object : 'data' + some: 'other', + object: 'data' }, name: 'namespace:command-2' }, { displayName: 'Custom Command 3', metadata: { - some : 'other', - object : 'data' + some: 'other', + object: 'data' }, name: 'namespace:command-3' } - ]); - }); + ]) + }) - it("returns command descriptors with arbitrary metadata if set on a listener function", () => { + it('returns command descriptors with arbitrary metadata if set on a listener function', () => { function listener () {} listener.displayName = 'Custom Command 2' listener.metadata = { some: 'other', object: 'data' - }; + } - registry.add('.grandchild', 'namespace:command-2', listener); - const commands = registry.findCommands({target: grandchild}); + registry.add('.grandchild', 'namespace:command-2', listener) + const commands = registry.findCommands({ target: grandchild }) expect(commands).toEqual([ { - displayName : 'Custom Command 2', + displayName: 'Custom Command 2', metadata: { some: 'other', object: 'data' }, name: 'namespace:command-2' } - ]); - }); - }); - - describe("::dispatch(target, commandName)", () => { - it("simulates invocation of the given command ", () => { - let called = false; - registry.add('.grandchild', 'command', function (event) { - expect(this).toBe(grandchild); - expect(event.type).toBe('command'); - expect(event.eventPhase).toBe(Event.BUBBLING_PHASE); - expect(event.target).toBe(grandchild); - expect(event.currentTarget).toBe(grandchild); - called = true; - }); - - registry.dispatch(grandchild, 'command'); - expect(called).toBe(true); - }); - - it("returns a promise if any listeners matched the command", () => { - registry.add('.grandchild', 'command', () => {}); - - expect(registry.dispatch(grandchild, 'command').constructor.name).toBe("Promise"); - expect(registry.dispatch(grandchild, 'bogus')).toBe(null); - expect(registry.dispatch(parent, 'command')).toBe(null); - }); - - it("returns a promise that resolves when the listeners resolve", async () => { - jasmine.useRealClock(); - registry.add('.grandchild', 'command', () => 1); - registry.add('.grandchild', 'command', () => Promise.resolve(2)); - registry.add('.grandchild', 'command', () => new Promise((resolve) => { - setTimeout(() => { resolve(3); }, 1); - })); - - const values = await registry.dispatch(grandchild, 'command'); - expect(values).toEqual([3, 2, 1]); - }); - - it("returns a promise that rejects when a listener is rejected", async () => { - jasmine.useRealClock(); - registry.add('.grandchild', 'command', () => 1); - registry.add('.grandchild', 'command', () => Promise.resolve(2)); - registry.add('.grandchild', 'command', () => new Promise((resolve, reject) => { - setTimeout(() => { reject(3); }, 1); - })); - - let value; - try { - value = await registry.dispatch(grandchild, 'command'); - } catch (err) { - value = err; - } - expect(value).toBe(3); - }); - }); - - describe("::getSnapshot and ::restoreSnapshot", () => - it("removes all command handlers except for those in the snapshot", () => { - registry.add('.parent', 'namespace:command-1', () => {}); - registry.add('.child', 'namespace:command-2', () => {}); - const snapshot = registry.getSnapshot(); - registry.add('.grandchild', 'namespace:command-3', () => {}); - - expect(registry.findCommands({target: grandchild}).slice(0, 3)).toEqual([ - {name: 'namespace:command-3', displayName: 'Namespace: Command 3'}, - {name: 'namespace:command-2', displayName: 'Namespace: Command 2'}, - {name: 'namespace:command-1', displayName: 'Namespace: Command 1'} - ]); - - registry.restoreSnapshot(snapshot); - - expect(registry.findCommands({target: grandchild}).slice(0, 2)).toEqual([ - {name: 'namespace:command-2', displayName: 'Namespace: Command 2'}, - {name: 'namespace:command-1', displayName: 'Namespace: Command 1'} - ]); - - registry.add('.grandchild', 'namespace:command-3', () => {}); - registry.restoreSnapshot(snapshot); - - expect(registry.findCommands({target: grandchild}).slice(0, 2)).toEqual([ - {name: 'namespace:command-2', displayName: 'Namespace: Command 2'}, - {name: 'namespace:command-1', displayName: 'Namespace: Command 1'} - ]); - }) -); - - describe("::attach(rootNode)", () => - it("adds event listeners for any previously-added commands", () => { - const registry2 = new CommandRegistry; - - const commandSpy = jasmine.createSpy('command-callback'); - registry2.add('.grandchild', 'command-1', commandSpy); - - grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true})); - expect(commandSpy).not.toHaveBeenCalled(); - - registry2.attach(parent); - - grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true})); - expect(commandSpy).toHaveBeenCalled(); + ]) }) - ); -}); + }) + + describe('::dispatch(target, commandName)', () => { + it('simulates invocation of the given command ', () => { + let called = false + registry.add('.grandchild', 'command', function (event) { + expect(this).toBe(grandchild) + expect(event.type).toBe('command') + expect(event.eventPhase).toBe(Event.BUBBLING_PHASE) + expect(event.target).toBe(grandchild) + expect(event.currentTarget).toBe(grandchild) + called = true + }) + + registry.dispatch(grandchild, 'command') + expect(called).toBe(true) + }) + + it('returns a promise if any listeners matched the command', () => { + registry.add('.grandchild', 'command', () => {}) + + expect(registry.dispatch(grandchild, 'command').constructor.name).toBe( + 'Promise' + ) + expect(registry.dispatch(grandchild, 'bogus')).toBe(null) + expect(registry.dispatch(parent, 'command')).toBe(null) + }) + + it('returns a promise that resolves when the listeners resolve', async () => { + jasmine.useRealClock() + registry.add('.grandchild', 'command', () => 1) + registry.add('.grandchild', 'command', () => Promise.resolve(2)) + registry.add( + '.grandchild', + 'command', + () => + new Promise(resolve => { + setTimeout(() => { + resolve(3) + }, 1) + }) + ) + + const values = await registry.dispatch(grandchild, 'command') + expect(values).toEqual([3, 2, 1]) + }) + + it('returns a promise that rejects when a listener is rejected', async () => { + jasmine.useRealClock() + registry.add('.grandchild', 'command', () => 1) + registry.add('.grandchild', 'command', () => Promise.resolve(2)) + registry.add( + '.grandchild', + 'command', + () => + new Promise((resolve, reject) => { + setTimeout(() => { + reject(3) + }, 1) + }) + ) + + let value + try { + value = await registry.dispatch(grandchild, 'command') + } catch (err) { + value = err + } + expect(value).toBe(3) + }) + }) + + describe('::getSnapshot and ::restoreSnapshot', () => + it('removes all command handlers except for those in the snapshot', () => { + registry.add('.parent', 'namespace:command-1', () => {}) + registry.add('.child', 'namespace:command-2', () => {}) + const snapshot = registry.getSnapshot() + registry.add('.grandchild', 'namespace:command-3', () => {}) + + expect(registry.findCommands({ target: grandchild }).slice(0, 3)).toEqual( + [ + { name: 'namespace:command-3', displayName: 'Namespace: Command 3' }, + { name: 'namespace:command-2', displayName: 'Namespace: Command 2' }, + { name: 'namespace:command-1', displayName: 'Namespace: Command 1' } + ] + ) + + registry.restoreSnapshot(snapshot) + + expect(registry.findCommands({ target: grandchild }).slice(0, 2)).toEqual( + [ + { name: 'namespace:command-2', displayName: 'Namespace: Command 2' }, + { name: 'namespace:command-1', displayName: 'Namespace: Command 1' } + ] + ) + + registry.add('.grandchild', 'namespace:command-3', () => {}) + registry.restoreSnapshot(snapshot) + + expect(registry.findCommands({ target: grandchild }).slice(0, 2)).toEqual( + [ + { name: 'namespace:command-2', displayName: 'Namespace: Command 2' }, + { name: 'namespace:command-1', displayName: 'Namespace: Command 1' } + ] + ) + })) + + describe('::attach(rootNode)', () => + it('adds event listeners for any previously-added commands', () => { + const registry2 = new CommandRegistry() + + const commandSpy = jasmine.createSpy('command-callback') + registry2.add('.grandchild', 'command-1', commandSpy) + + grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true })) + expect(commandSpy).not.toHaveBeenCalled() + + registry2.attach(parent) + + grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true })) + expect(commandSpy).toHaveBeenCalled() + })) +}) diff --git a/spec/config-file-spec.js b/spec/config-file-spec.js index ae5e05fe6..d5ae98170 100644 --- a/spec/config-file-spec.js +++ b/spec/config-file-spec.js @@ -1,4 +1,3 @@ -const {it, fit, ffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers') const fs = require('fs-plus') const path = require('path') const temp = require('temp').track() @@ -42,22 +41,25 @@ describe('ConfigFile', () => { const event = new Promise(resolve => configFile.onDidChange(resolve)) - writeFileSync(filePath, dedent ` + writeFileSync( + filePath, + dedent` '*': foo: 'bar' 'javascript': foo: 'baz' - `) + ` + ) expect(await event).toEqual({ - '*': {foo: 'bar'}, - 'javascript': {foo: 'baz'} + '*': { foo: 'bar' }, + javascript: { foo: 'baz' } }) expect(configFile.get()).toEqual({ - '*': {foo: 'bar'}, - 'javascript': {foo: 'baz'} + '*': { foo: 'bar' }, + javascript: { foo: 'baz' } }) }) }) @@ -69,25 +71,33 @@ describe('ConfigFile', () => { const message = new Promise(resolve => configFile.onDidError(resolve)) - writeFileSync(filePath, dedent ` + writeFileSync( + filePath, + dedent` um what? - `, 2) + `, + 2 + ) expect(await message).toContain('Failed to load `the-config.cson`') const event = new Promise(resolve => configFile.onDidChange(resolve)) - writeFileSync(filePath, dedent ` + writeFileSync( + filePath, + dedent` '*': foo: 'bar' 'javascript': foo: 'baz' - `, 4) + `, + 4 + ) expect(await event).toEqual({ - '*': {foo: 'bar'}, - 'javascript': {foo: 'baz'} + '*': { foo: 'bar' }, + javascript: { foo: 'baz' } }) }) }) @@ -115,7 +125,7 @@ describe('ConfigFile', () => { }) function writeFileSync (filePath, content, seconds = 2) { - const utime = (Date.now() / 1000) + seconds + const utime = Date.now() / 1000 + seconds fs.writeFileSync(filePath, content) fs.utimesSync(filePath, utime, utime) } diff --git a/spec/config-spec.js b/spec/config-spec.js index 53cf9fffc..f942d62a5 100644 --- a/spec/config-spec.js +++ b/spec/config-spec.js @@ -19,121 +19,193 @@ describe('Config', () => { }) it("returns a deep clone of the key path's value", () => { - atom.config.set('value', {array: [1, {b: 2}, 3]}) + atom.config.set('value', { array: [1, { b: 2 }, 3] }) const retrievedValue = atom.config.get('value') retrievedValue.array[0] = 4 retrievedValue.array[1].b = 2.1 - expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]}) + expect(atom.config.get('value')).toEqual({ array: [1, { b: 2 }, 3] }) }) it('merges defaults into the returned value if both the assigned value and the default value are objects', () => { - atom.config.setDefaults('foo.bar', {baz: 1, ok: 2}) - atom.config.set('foo.bar', {baz: 3}) - expect(atom.config.get('foo.bar')).toEqual({baz: 3, ok: 2}) + atom.config.setDefaults('foo.bar', { baz: 1, ok: 2 }) + atom.config.set('foo.bar', { baz: 3 }) + expect(atom.config.get('foo.bar')).toEqual({ baz: 3, ok: 2 }) - atom.config.setDefaults('other', {baz: 1}) + atom.config.setDefaults('other', { baz: 1 }) atom.config.set('other', 7) expect(atom.config.get('other')).toBe(7) - atom.config.set('bar.baz', {a: 3}) - atom.config.setDefaults('bar', {baz: 7}) - expect(atom.config.get('bar.baz')).toEqual({a: 3}) + atom.config.set('bar.baz', { a: 3 }) + atom.config.setDefaults('bar', { baz: 7 }) + expect(atom.config.get('bar.baz')).toEqual({ a: 3 }) }) describe("when a 'sources' option is specified", () => it('only retrieves values from the specified sources', () => { - atom.config.set('x.y', 1, {scopeSelector: '.foo', source: 'a'}) - atom.config.set('x.y', 2, {scopeSelector: '.foo', source: 'b'}) - atom.config.set('x.y', 3, {scopeSelector: '.foo', source: 'c'}) - atom.config.setSchema('x.y', {type: 'integer', default: 4}) + atom.config.set('x.y', 1, { scopeSelector: '.foo', source: 'a' }) + atom.config.set('x.y', 2, { scopeSelector: '.foo', source: 'b' }) + atom.config.set('x.y', 3, { scopeSelector: '.foo', source: 'c' }) + atom.config.setSchema('x.y', { type: 'integer', default: 4 }) - expect(atom.config.get('x.y', {sources: ['a'], scope: ['.foo']})).toBe(1) - expect(atom.config.get('x.y', {sources: ['b'], scope: ['.foo']})).toBe(2) - expect(atom.config.get('x.y', {sources: ['c'], scope: ['.foo']})).toBe(3) + expect( + atom.config.get('x.y', { sources: ['a'], scope: ['.foo'] }) + ).toBe(1) + expect( + atom.config.get('x.y', { sources: ['b'], scope: ['.foo'] }) + ).toBe(2) + expect( + atom.config.get('x.y', { sources: ['c'], scope: ['.foo'] }) + ).toBe(3) // Schema defaults never match a specific source. We could potentially add a special "schema" source. - expect(atom.config.get('x.y', {sources: ['x'], scope: ['.foo']})).toBeUndefined() + expect( + atom.config.get('x.y', { sources: ['x'], scope: ['.foo'] }) + ).toBeUndefined() - expect(atom.config.get(null, {sources: ['a'], scope: ['.foo']}).x.y).toBe(1) - }) - ) + expect( + atom.config.get(null, { sources: ['a'], scope: ['.foo'] }).x.y + ).toBe(1) + })) describe("when an 'excludeSources' option is specified", () => it('only retrieves values from the specified sources', () => { atom.config.set('x.y', 0) - atom.config.set('x.y', 1, {scopeSelector: '.foo', source: 'a'}) - atom.config.set('x.y', 2, {scopeSelector: '.foo', source: 'b'}) - atom.config.set('x.y', 3, {scopeSelector: '.foo', source: 'c'}) - atom.config.setSchema('x.y', {type: 'integer', default: 4}) + atom.config.set('x.y', 1, { scopeSelector: '.foo', source: 'a' }) + atom.config.set('x.y', 2, { scopeSelector: '.foo', source: 'b' }) + atom.config.set('x.y', 3, { scopeSelector: '.foo', source: 'c' }) + atom.config.setSchema('x.y', { type: 'integer', default: 4 }) - expect(atom.config.get('x.y', {excludeSources: ['a'], scope: ['.foo']})).toBe(3) - expect(atom.config.get('x.y', {excludeSources: ['c'], scope: ['.foo']})).toBe(2) - expect(atom.config.get('x.y', {excludeSources: ['b', 'c'], scope: ['.foo']})).toBe(1) - expect(atom.config.get('x.y', {excludeSources: ['b', 'c', 'a'], scope: ['.foo']})).toBe(0) - expect(atom.config.get('x.y', {excludeSources: ['b', 'c', 'a', atom.config.getUserConfigPath()], scope: ['.foo']})).toBe(4) - expect(atom.config.get('x.y', {excludeSources: [atom.config.getUserConfigPath()]})).toBe(4) - }) - ) + expect( + atom.config.get('x.y', { excludeSources: ['a'], scope: ['.foo'] }) + ).toBe(3) + expect( + atom.config.get('x.y', { excludeSources: ['c'], scope: ['.foo'] }) + ).toBe(2) + expect( + atom.config.get('x.y', { + excludeSources: ['b', 'c'], + scope: ['.foo'] + }) + ).toBe(1) + expect( + atom.config.get('x.y', { + excludeSources: ['b', 'c', 'a'], + scope: ['.foo'] + }) + ).toBe(0) + expect( + atom.config.get('x.y', { + excludeSources: ['b', 'c', 'a', atom.config.getUserConfigPath()], + scope: ['.foo'] + }) + ).toBe(4) + expect( + atom.config.get('x.y', { + excludeSources: [atom.config.getUserConfigPath()] + }) + ).toBe(4) + })) describe("when a 'scope' option is given", () => { it('returns the property with the most specific scope selector', () => { - atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee'}) - atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double'}) - atom.config.set('foo.bar.baz', 11, {scopeSelector: '.source'}) + atom.config.set('foo.bar.baz', 42, { + scopeSelector: '.source.coffee .string.quoted.double.coffee' + }) + atom.config.set('foo.bar.baz', 22, { + scopeSelector: '.source .string.quoted.double' + }) + atom.config.set('foo.bar.baz', 11, { scopeSelector: '.source' }) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']})).toBe(42) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.js', '.string.quoted.double.js']})).toBe(22) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.js', '.variable.assignment.js']})).toBe(11) - expect(atom.config.get('foo.bar.baz', {scope: ['.text']})).toBeUndefined() + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.coffee', '.string.quoted.double.coffee'] + }) + ).toBe(42) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.js', '.string.quoted.double.js'] + }) + ).toBe(22) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.js', '.variable.assignment.js'] + }) + ).toBe(11) + expect( + atom.config.get('foo.bar.baz', { scope: ['.text'] }) + ).toBeUndefined() }) it('favors the most recently added properties in the event of a specificity tie', () => { - atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.single'}) - atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source.coffee .string.quoted.double'}) + atom.config.set('foo.bar.baz', 42, { + scopeSelector: '.source.coffee .string.quoted.single' + }) + atom.config.set('foo.bar.baz', 22, { + scopeSelector: '.source.coffee .string.quoted.double' + }) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.single']})).toBe(42) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.single.double']})).toBe(22) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.coffee', '.string.quoted.single'] + }) + ).toBe(42) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.coffee', '.string.quoted.single.double'] + }) + ).toBe(22) }) describe('when there are global defaults', () => it('falls back to the global when there is no scoped property specified', () => { - atom.config.setDefaults('foo', {hasDefault: 'ok'}) - expect(atom.config.get('foo.hasDefault', {scope: ['.source.coffee', '.string.quoted.single']})).toBe('ok') - }) - ) + atom.config.setDefaults('foo', { hasDefault: 'ok' }) + expect( + atom.config.get('foo.hasDefault', { + scope: ['.source.coffee', '.string.quoted.single'] + }) + ).toBe('ok') + })) describe('when package settings are added after user settings', () => it("returns the user's setting because the user's setting has higher priority", () => { - atom.config.set('foo.bar.baz', 100, {scopeSelector: '.source.coffee'}) - atom.config.set('foo.bar.baz', 1, {scopeSelector: '.source.coffee', source: 'some-package'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(100) - }) - ) + atom.config.set('foo.bar.baz', 100, { + scopeSelector: '.source.coffee' + }) + atom.config.set('foo.bar.baz', 1, { + scopeSelector: '.source.coffee', + source: 'some-package' + }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(100) + })) }) }) describe('.getAll(keyPath, {scope, sources, excludeSources})', () => { it('reads all of the values for a given key-path', () => { expect(atom.config.set('foo', 41)).toBe(true) - expect(atom.config.set('foo', 43, {scopeSelector: '.a .b'})).toBe(true) - expect(atom.config.set('foo', 42, {scopeSelector: '.a'})).toBe(true) - expect(atom.config.set('foo', 44, {scopeSelector: '.a .b.c'})).toBe(true) + expect(atom.config.set('foo', 43, { scopeSelector: '.a .b' })).toBe(true) + expect(atom.config.set('foo', 42, { scopeSelector: '.a' })).toBe(true) + expect(atom.config.set('foo', 44, { scopeSelector: '.a .b.c' })).toBe( + true + ) - expect(atom.config.set('foo', -44, {scopeSelector: '.d'})).toBe(true) + expect(atom.config.set('foo', -44, { scopeSelector: '.d' })).toBe(true) - expect(atom.config.getAll('foo', {scope: ['.a', '.b.c']})).toEqual([ - {scopeSelector: '.a .b.c', value: 44}, - {scopeSelector: '.a .b', value: 43}, - {scopeSelector: '.a', value: 42}, - {scopeSelector: '*', value: 41} + expect(atom.config.getAll('foo', { scope: ['.a', '.b.c'] })).toEqual([ + { scopeSelector: '.a .b.c', value: 44 }, + { scopeSelector: '.a .b', value: 43 }, + { scopeSelector: '.a', value: 42 }, + { scopeSelector: '*', value: 41 } ]) }) it("includes the schema's default value", () => { - atom.config.setSchema('foo', {type: 'number', default: 40}) - expect(atom.config.set('foo', 43, {scopeSelector: '.a .b'})).toBe(true) - expect(atom.config.getAll('foo', {scope: ['.a', '.b.c']})).toEqual([ - {scopeSelector: '.a .b', value: 43}, - {scopeSelector: '*', value: 40} + atom.config.setSchema('foo', { type: 'number', default: 40 }) + expect(atom.config.set('foo', 43, { scopeSelector: '.a .b' })).toBe(true) + expect(atom.config.getAll('foo', { scope: ['.a', '.b.c'] })).toEqual([ + { scopeSelector: '.a .b', value: 43 }, + { scopeSelector: '*', value: 40 } ]) }) }) @@ -155,23 +227,33 @@ describe('Config', () => { }) it("does not save when a non-default 'source' is given", () => { - atom.config.set('foo.bar.baz', 42, {source: 'some-other-source', scopeSelector: '.a'}) + atom.config.set('foo.bar.baz', 42, { + source: 'some-other-source', + scopeSelector: '.a' + }) advanceClock(500) expect(savedSettings.length).toBe(0) }) it("does not allow a 'source' option without a 'scopeSelector'", () => { - expect(() => atom.config.set('foo', 1, {source: ['.source.ruby']})).toThrow() + expect(() => + atom.config.set('foo', 1, { source: ['.source.ruby'] }) + ).toThrow() }) describe('when the key-path is null', () => it('sets the root object', () => { - expect(atom.config.set(null, {editor: {tabLength: 6}})).toBe(true) + expect(atom.config.set(null, { editor: { tabLength: 6 } })).toBe(true) expect(atom.config.get('editor.tabLength')).toBe(6) - expect(atom.config.set(null, {editor: {tabLength: 8, scopeSelector: ['.source.js']}})).toBe(true) - expect(atom.config.get('editor.tabLength', {scope: ['.source.js']})).toBe(8) - }) - ) + expect( + atom.config.set(null, { + editor: { tabLength: 8, scopeSelector: ['.source.js'] } + }) + ).toBe(true) + expect( + atom.config.get('editor.tabLength', { scope: ['.source.js'] }) + ).toBe(8) + })) describe('when the value equals the default value', () => it("does not store the value in the user's config", () => { @@ -192,7 +274,7 @@ describe('Config', () => { }, sameObject: { type: 'object', - default: {a: 1, b: 2} + default: { a: 1, b: 2 } }, null: { type: '*', @@ -203,8 +285,7 @@ describe('Config', () => { default: undefined } } - } - ) + }) expect(atom.config.settings.foo).toBeUndefined() atom.config.set('foo.same', 1) @@ -212,32 +293,52 @@ describe('Config', () => { atom.config.set('foo.sameArray', [1, 2, 3]) atom.config.set('foo.null', undefined) atom.config.set('foo.undefined', null) - atom.config.set('foo.sameObject', {b: 2, a: 1}) + atom.config.set('foo.sameObject', { b: 2, a: 1 }) const userConfigPath = atom.config.getUserConfigPath() - expect(atom.config.get('foo.same', {sources: [userConfigPath]})).toBeUndefined() + expect( + atom.config.get('foo.same', { sources: [userConfigPath] }) + ).toBeUndefined() expect(atom.config.get('foo.changes')).toBe(2) - expect(atom.config.get('foo.changes', {sources: [userConfigPath]})).toBe(2) + expect( + atom.config.get('foo.changes', { sources: [userConfigPath] }) + ).toBe(2) atom.config.set('foo.changes', 1) - expect(atom.config.get('foo.changes', {sources: [userConfigPath]})).toBeUndefined() - }) - ) + expect( + atom.config.get('foo.changes', { sources: [userConfigPath] }) + ).toBeUndefined() + })) describe("when a 'scopeSelector' is given", () => it('sets the value and overrides the others', () => { - atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee'}) - atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double'}) - atom.config.set('foo.bar.baz', 11, {scopeSelector: '.source'}) + atom.config.set('foo.bar.baz', 42, { + scopeSelector: '.source.coffee .string.quoted.double.coffee' + }) + atom.config.set('foo.bar.baz', 22, { + scopeSelector: '.source .string.quoted.double' + }) + atom.config.set('foo.bar.baz', 11, { scopeSelector: '.source' }) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']})).toBe(42) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.coffee', '.string.quoted.double.coffee'] + }) + ).toBe(42) - expect(atom.config.set('foo.bar.baz', 100, {scopeSelector: '.source.coffee .string.quoted.double.coffee'})).toBe(true) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']})).toBe(100) - }) - ) + expect( + atom.config.set('foo.bar.baz', 100, { + scopeSelector: '.source.coffee .string.quoted.double.coffee' + }) + ).toBe(true) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.coffee', '.string.quoted.double.coffee'] + }) + ).toBe(100) + })) }) describe('.unset(keyPath, {source, scopeSelector})', () => { @@ -263,12 +364,11 @@ describe('Config', () => { default: 0 } } - } - ) + }) ) it('sets the value of the key path to its default', () => { - atom.config.setDefaults('a', {b: 3}) + atom.config.setDefaults('a', { b: 3 }) atom.config.set('a.b', 4) expect(atom.config.get('a.b')).toBe(4) atom.config.unset('a.b') @@ -281,7 +381,7 @@ describe('Config', () => { }) it('calls ::save()', () => { - atom.config.setDefaults('a', {b: 3}) + atom.config.setDefaults('a', { b: 3 }) atom.config.set('a.b', 4) savedSettings.length = 0 @@ -293,125 +393,204 @@ describe('Config', () => { describe("when no 'scopeSelector' is given", () => { describe("when a 'source' but no key-path is given", () => it('removes all scoped settings with the given source', () => { - atom.config.set('foo.bar.baz', 1, {scopeSelector: '.a', source: 'source-a'}) - atom.config.set('foo.bar.quux', 2, {scopeSelector: '.b', source: 'source-a'}) - expect(atom.config.get('foo.bar', {scope: ['.a.b']})).toEqual({baz: 1, quux: 2}) + atom.config.set('foo.bar.baz', 1, { + scopeSelector: '.a', + source: 'source-a' + }) + atom.config.set('foo.bar.quux', 2, { + scopeSelector: '.b', + source: 'source-a' + }) + expect(atom.config.get('foo.bar', { scope: ['.a.b'] })).toEqual({ + baz: 1, + quux: 2 + }) - atom.config.unset(null, {source: 'source-a'}) - expect(atom.config.get('foo.bar', {scope: ['.a']})).toEqual({baz: 0, ok: 0}) - }) - ) + atom.config.unset(null, { source: 'source-a' }) + expect(atom.config.get('foo.bar', { scope: ['.a'] })).toEqual({ + baz: 0, + ok: 0 + }) + })) describe("when a 'source' and a key-path is given", () => it('removes all scoped settings with the given source and key-path', () => { atom.config.set('foo.bar.baz', 1) - atom.config.set('foo.bar.baz', 2, {scopeSelector: '.a', source: 'source-a'}) - atom.config.set('foo.bar.baz', 3, {scopeSelector: '.a.b', source: 'source-b'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.a.b']})).toEqual(3) + atom.config.set('foo.bar.baz', 2, { + scopeSelector: '.a', + source: 'source-a' + }) + atom.config.set('foo.bar.baz', 3, { + scopeSelector: '.a.b', + source: 'source-b' + }) + expect(atom.config.get('foo.bar.baz', { scope: ['.a.b'] })).toEqual(3) - atom.config.unset('foo.bar.baz', {source: 'source-b'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.a.b']})).toEqual(2) + atom.config.unset('foo.bar.baz', { source: 'source-b' }) + expect(atom.config.get('foo.bar.baz', { scope: ['.a.b'] })).toEqual(2) expect(atom.config.get('foo.bar.baz')).toEqual(1) - }) - ) + })) describe("when no 'source' is given", () => it('removes all scoped and unscoped properties for that key-path', () => { - atom.config.setDefaults('foo.bar', {baz: 100}) + atom.config.setDefaults('foo.bar', { baz: 100 }) - atom.config.set('foo.bar', {baz: 1, ok: 2}, {scopeSelector: '.a'}) - atom.config.set('foo.bar', {baz: 11, ok: 12}, {scopeSelector: '.b'}) - atom.config.set('foo.bar', {baz: 21, ok: 22}) + atom.config.set('foo.bar', { baz: 1, ok: 2 }, { scopeSelector: '.a' }) + atom.config.set( + 'foo.bar', + { baz: 11, ok: 12 }, + { scopeSelector: '.b' } + ) + atom.config.set('foo.bar', { baz: 21, ok: 22 }) atom.config.unset('foo.bar.baz') - expect(atom.config.get('foo.bar.baz', {scope: ['.a']})).toBe(100) - expect(atom.config.get('foo.bar.baz', {scope: ['.b']})).toBe(100) + expect(atom.config.get('foo.bar.baz', { scope: ['.a'] })).toBe(100) + expect(atom.config.get('foo.bar.baz', { scope: ['.b'] })).toBe(100) expect(atom.config.get('foo.bar.baz')).toBe(100) - expect(atom.config.get('foo.bar.ok', {scope: ['.a']})).toBe(2) - expect(atom.config.get('foo.bar.ok', {scope: ['.b']})).toBe(12) + expect(atom.config.get('foo.bar.ok', { scope: ['.a'] })).toBe(2) + expect(atom.config.get('foo.bar.ok', { scope: ['.b'] })).toBe(12) expect(atom.config.get('foo.bar.ok')).toBe(22) - }) - ) + })) }) describe("when a 'scopeSelector' is given", () => { it('restores the global default when no scoped default set', () => { - atom.config.setDefaults('foo', {bar: {baz: 10}}) - atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + atom.config.setDefaults('foo', { bar: { baz: 10 } }) + atom.config.set('foo.bar.baz', 55, { scopeSelector: '.source.coffee' }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(55) - atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(10) + atom.config.unset('foo.bar.baz', { scopeSelector: '.source.coffee' }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(10) }) it('restores the scoped default when a scoped default is set', () => { - atom.config.setDefaults('foo', {bar: {baz: 10}}) - atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee', source: 'some-source'}) - atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) - atom.config.set('foo.bar.ok', 100, {scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + atom.config.setDefaults('foo', { bar: { baz: 10 } }) + atom.config.set('foo.bar.baz', 42, { + scopeSelector: '.source.coffee', + source: 'some-source' + }) + atom.config.set('foo.bar.baz', 55, { scopeSelector: '.source.coffee' }) + atom.config.set('foo.bar.ok', 100, { scopeSelector: '.source.coffee' }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(55) - atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(42) - expect(atom.config.get('foo.bar.ok', {scope: ['.source.coffee']})).toBe(100) + atom.config.unset('foo.bar.baz', { scopeSelector: '.source.coffee' }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(42) + expect( + atom.config.get('foo.bar.ok', { scope: ['.source.coffee'] }) + ).toBe(100) }) it('calls ::save()', () => { - atom.config.setDefaults('foo', {bar: {baz: 10}}) - atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) + atom.config.setDefaults('foo', { bar: { baz: 10 } }) + atom.config.set('foo.bar.baz', 55, { scopeSelector: '.source.coffee' }) savedSettings.length = 0 - atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) + atom.config.unset('foo.bar.baz', { scopeSelector: '.source.coffee' }) advanceClock(150) expect(savedSettings.length).toBe(1) }) it('allows removing settings for a specific source and scope selector', () => { - atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee', source: 'source-a'}) - atom.config.set('foo.bar.baz', 65, {scopeSelector: '.source.coffee', source: 'source-b'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(65) + atom.config.set('foo.bar.baz', 55, { + scopeSelector: '.source.coffee', + source: 'source-a' + }) + atom.config.set('foo.bar.baz', 65, { + scopeSelector: '.source.coffee', + source: 'source-b' + }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(65) - atom.config.unset('foo.bar.baz', {source: 'source-b', scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string']})).toBe(55) + atom.config.unset('foo.bar.baz', { + source: 'source-b', + scopeSelector: '.source.coffee' + }) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.coffee', '.string'] + }) + ).toBe(55) }) it('allows removing all settings for a specific source', () => { - atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee', source: 'source-a'}) - atom.config.set('foo.bar.baz', 65, {scopeSelector: '.source.coffee', source: 'source-b'}) - atom.config.set('foo.bar.ok', 65, {scopeSelector: '.source.coffee', source: 'source-b'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(65) + atom.config.set('foo.bar.baz', 55, { + scopeSelector: '.source.coffee', + source: 'source-a' + }) + atom.config.set('foo.bar.baz', 65, { + scopeSelector: '.source.coffee', + source: 'source-b' + }) + atom.config.set('foo.bar.ok', 65, { + scopeSelector: '.source.coffee', + source: 'source-b' + }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(65) - atom.config.unset(null, {source: 'source-b', scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string']})).toBe(55) - expect(atom.config.get('foo.bar.ok', {scope: ['.source.coffee', '.string']})).toBe(0) + atom.config.unset(null, { + source: 'source-b', + scopeSelector: '.source.coffee' + }) + expect( + atom.config.get('foo.bar.baz', { + scope: ['.source.coffee', '.string'] + }) + ).toBe(55) + expect( + atom.config.get('foo.bar.ok', { + scope: ['.source.coffee', '.string'] + }) + ).toBe(0) }) it('does not call ::save or add a scoped property when no value has been set', () => { // see https://github.com/atom/atom/issues/4175 - atom.config.setDefaults('foo', {bar: {baz: 10}}) - atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(10) + atom.config.setDefaults('foo', { bar: { baz: 10 } }) + atom.config.unset('foo.bar.baz', { scopeSelector: '.source.coffee' }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(10) expect(savedSettings.length).toBe(0) - const scopedProperties = atom.config.scopedSettingsStore.propertiesForSource('user-config') + const scopedProperties = atom.config.scopedSettingsStore.propertiesForSource( + 'user-config' + ) expect(scopedProperties['.coffee.source']).toBeUndefined() }) it('removes the scoped value when it was the only set value on the object', () => { - atom.config.setDefaults('foo', {bar: {baz: 10}}) - atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) - atom.config.set('foo.bar.ok', 20, {scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + atom.config.setDefaults('foo', { bar: { baz: 10 } }) + atom.config.set('foo.bar.baz', 55, { scopeSelector: '.source.coffee' }) + atom.config.set('foo.bar.ok', 20, { scopeSelector: '.source.coffee' }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(55) advanceClock(150) savedSettings.length = 0 - atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(10) - expect(atom.config.get('foo.bar.ok', {scope: ['.source.coffee']})).toBe(20) + atom.config.unset('foo.bar.baz', { scopeSelector: '.source.coffee' }) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(10) + expect( + atom.config.get('foo.bar.ok', { scope: ['.source.coffee'] }) + ).toBe(20) advanceClock(150) expect(savedSettings[0]['.coffee.source']).toEqual({ @@ -422,7 +601,7 @@ describe('Config', () => { } }) - atom.config.unset('foo.bar.ok', {scopeSelector: '.source.coffee'}) + atom.config.unset('foo.bar.ok', { scopeSelector: '.source.coffee' }) advanceClock(150) expect(savedSettings.length).toBe(2) @@ -430,15 +609,17 @@ describe('Config', () => { }) it('does not call ::save when the value is already at the default', () => { - atom.config.setDefaults('foo', {bar: {baz: 10}}) + atom.config.setDefaults('foo', { bar: { baz: 10 } }) atom.config.set('foo.bar.baz', 55) advanceClock(150) savedSettings.length = 0 - atom.config.unset('foo.bar.ok', {scopeSelector: '.source.coffee'}) + atom.config.unset('foo.bar.ok', { scopeSelector: '.source.coffee' }) advanceClock(150) expect(savedSettings.length).toBe(0) - expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + expect( + atom.config.get('foo.bar.baz', { scope: ['.source.coffee'] }) + ).toBe(55) }) }) }) @@ -453,15 +634,24 @@ describe('Config', () => { atom.config.onDidChange('foo.bar.baz', observeHandler) }) - it('does not fire the given callback with the current value at the keypath', () => expect(observeHandler).not.toHaveBeenCalled()) + it('does not fire the given callback with the current value at the keypath', () => + expect(observeHandler).not.toHaveBeenCalled()) it('fires the callback every time the observed value changes', () => { atom.config.set('foo.bar.baz', 'value 2') - expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 2', oldValue: 'value 1'}) + expect(observeHandler).toHaveBeenCalledWith({ + newValue: 'value 2', + oldValue: 'value 1' + }) observeHandler.reset() - observeHandler.andCallFake(() => { throw new Error('oops') }) + observeHandler.andCallFake(() => { + throw new Error('oops') + }) expect(() => atom.config.set('foo.bar.baz', 'value 1')).toThrow('oops') - expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 1', oldValue: 'value 2'}) + expect(observeHandler).toHaveBeenCalledWith({ + newValue: 'value 1', + oldValue: 'value 2' + }) observeHandler.reset() // Regression: exception in earlier handler shouldn't put observer @@ -478,59 +668,93 @@ describe('Config', () => { atom.config.onDidChange(observeHandler) }) - it('does not fire the given callback initially', () => expect(observeHandler).not.toHaveBeenCalled()) + it('does not fire the given callback initially', () => + expect(observeHandler).not.toHaveBeenCalled()) it('fires the callback every time any value changes', () => { observeHandler.reset() // clear the initial call atom.config.set('foo.bar.baz', 'value 2') expect(observeHandler).toHaveBeenCalled() - expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe('value 2') - expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe('value 1') + expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe( + 'value 2' + ) + expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe( + 'value 1' + ) observeHandler.reset() atom.config.set('foo.bar.baz', 'value 1') expect(observeHandler).toHaveBeenCalled() - expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe('value 1') - expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe('value 2') + expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe( + 'value 1' + ) + expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe( + 'value 2' + ) observeHandler.reset() atom.config.set('foo.bar.int', 1) expect(observeHandler).toHaveBeenCalled() - expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.int).toBe(1) - expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.int).toBe(undefined) + expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.int).toBe( + 1 + ) + expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.int).toBe( + undefined + ) }) }) describe("when a 'scope' is given", () => it('calls the supplied callback when the value at the descriptor/keypath changes', () => { const changeSpy = jasmine.createSpy('onDidChange callback') - atom.config.onDidChange('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']}, changeSpy) + atom.config.onDidChange( + 'foo.bar.baz', + { scope: ['.source.coffee', '.string.quoted.double.coffee'] }, + changeSpy + ) atom.config.set('foo.bar.baz', 12) - expect(changeSpy).toHaveBeenCalledWith({oldValue: undefined, newValue: 12}) + expect(changeSpy).toHaveBeenCalledWith({ + oldValue: undefined, + newValue: 12 + }) changeSpy.reset() - atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double', source: 'a'}) - expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: 22}) + atom.config.set('foo.bar.baz', 22, { + scopeSelector: '.source .string.quoted.double', + source: 'a' + }) + expect(changeSpy).toHaveBeenCalledWith({ oldValue: 12, newValue: 22 }) changeSpy.reset() - atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) - expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 42}) + atom.config.set('foo.bar.baz', 42, { + scopeSelector: '.source.coffee .string.quoted.double.coffee', + source: 'b' + }) + expect(changeSpy).toHaveBeenCalledWith({ oldValue: 22, newValue: 42 }) changeSpy.reset() - atom.config.unset(null, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) - expect(changeSpy).toHaveBeenCalledWith({oldValue: 42, newValue: 22}) + atom.config.unset(null, { + scopeSelector: '.source.coffee .string.quoted.double.coffee', + source: 'b' + }) + expect(changeSpy).toHaveBeenCalledWith({ oldValue: 42, newValue: 22 }) changeSpy.reset() - atom.config.unset(null, {scopeSelector: '.source .string.quoted.double', source: 'a'}) - expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 12}) + atom.config.unset(null, { + scopeSelector: '.source .string.quoted.double', + source: 'a' + }) + expect(changeSpy).toHaveBeenCalledWith({ oldValue: 22, newValue: 12 }) changeSpy.reset() atom.config.set('foo.bar.baz', undefined) - expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: undefined}) + expect(changeSpy).toHaveBeenCalledWith({ + oldValue: 12, + newValue: undefined + }) changeSpy.reset() - }) - ) + })) }) describe('.observe(keyPath, {scope})', () => { @@ -542,7 +766,8 @@ describe('Config', () => { observeSubscription = atom.config.observe('foo.bar.baz', observeHandler) }) - it('fires the given callback with the current value at the keypath', () => expect(observeHandler).toHaveBeenCalledWith('value 1')) + it('fires the given callback with the current value at the keypath', () => + expect(observeHandler).toHaveBeenCalledWith('value 1')) it('fires the callback every time the observed value changes', () => { observeHandler.reset() // clear the initial call @@ -555,7 +780,7 @@ describe('Config', () => { advanceClock(100) // complete pending save that was requested in ::set observeHandler.reset() - atom.config.resetUserSettings({foo: {}}) + atom.config.resetUserSettings({ foo: {} }) expect(observeHandler).toHaveBeenCalledWith(undefined) }) @@ -600,17 +825,29 @@ describe('Config', () => { }) it('allows settings to be observed in a specific scope', () => { - atom.config.observe('foo.bar.baz', {scope: ['.some.scope']}, observeHandler) - atom.config.observe('foo.bar.baz', {scope: ['.another.scope']}, otherHandler) + atom.config.observe( + 'foo.bar.baz', + { scope: ['.some.scope'] }, + observeHandler + ) + atom.config.observe( + 'foo.bar.baz', + { scope: ['.another.scope'] }, + otherHandler + ) - atom.config.set('foo.bar.baz', 'value 2', {scopeSelector: '.some'}) + atom.config.set('foo.bar.baz', 'value 2', { scopeSelector: '.some' }) expect(observeHandler).toHaveBeenCalledWith('value 2') expect(otherHandler).not.toHaveBeenCalledWith('value 2') }) it('calls the callback when properties with more specific selectors are removed', () => { const changeSpy = jasmine.createSpy() - atom.config.observe('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']}, changeSpy) + atom.config.observe( + 'foo.bar.baz', + { scope: ['.source.coffee', '.string.quoted.double.coffee'] }, + changeSpy + ) expect(changeSpy).toHaveBeenCalledWith('value 1') changeSpy.reset() @@ -618,19 +855,31 @@ describe('Config', () => { expect(changeSpy).toHaveBeenCalledWith(12) changeSpy.reset() - atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double', source: 'a'}) + atom.config.set('foo.bar.baz', 22, { + scopeSelector: '.source .string.quoted.double', + source: 'a' + }) expect(changeSpy).toHaveBeenCalledWith(22) changeSpy.reset() - atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) + atom.config.set('foo.bar.baz', 42, { + scopeSelector: '.source.coffee .string.quoted.double.coffee', + source: 'b' + }) expect(changeSpy).toHaveBeenCalledWith(42) changeSpy.reset() - atom.config.unset(null, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) + atom.config.unset(null, { + scopeSelector: '.source.coffee .string.quoted.double.coffee', + source: 'b' + }) expect(changeSpy).toHaveBeenCalledWith(22) changeSpy.reset() - atom.config.unset(null, {scopeSelector: '.source .string.quoted.double', source: 'a'}) + atom.config.unset(null, { + scopeSelector: '.source .string.quoted.double', + source: 'a' + }) expect(changeSpy).toHaveBeenCalledWith(12) changeSpy.reset() @@ -657,7 +906,10 @@ describe('Config', () => { }) expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + expect(changeSpy.argsForCall[0][0]).toEqual({ + newValue: 3, + oldValue: undefined + }) }) it('does not emit an event if no changes occur while paused', () => { @@ -683,14 +935,19 @@ describe('Config', () => { return Promise.resolve('a result') }) - waitsForPromise(() => transactionPromise.then(result => { - promiseResult = result - })) + waitsForPromise(() => + transactionPromise.then(result => { + promiseResult = result + }) + ) runs(() => { expect(promiseResult).toBe('a result') expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + expect(changeSpy.argsForCall[0][0]).toEqual({ + newValue: 3, + oldValue: undefined + }) }) }) @@ -703,14 +960,19 @@ describe('Config', () => { return Promise.reject(new Error('an error')) }) - waitsForPromise(() => transactionPromise.catch(error => { - promiseError = error - })) + waitsForPromise(() => + transactionPromise.catch(error => { + promiseError = error + }) + ) runs(() => { expect(promiseError.message).toBe('an error') expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + expect(changeSpy.argsForCall[0][0]).toEqual({ + newValue: 3, + oldValue: undefined + }) }) }) @@ -724,14 +986,19 @@ describe('Config', () => { throw error }) - waitsForPromise(() => transactionPromise.catch(e => { - promiseError = e - })) + waitsForPromise(() => + transactionPromise.catch(e => { + promiseError = e + }) + ) runs(() => { expect(promiseError).toBe(error) expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + expect(changeSpy.argsForCall[0][0]).toEqual({ + newValue: 3, + oldValue: undefined + }) }) }) }) @@ -740,10 +1007,10 @@ describe('Config', () => { it("returns an array of all of the config's source names", () => { expect(atom.config.getSources()).toEqual([]) - atom.config.set('a.b', 1, {scopeSelector: '.x1', source: 'source-1'}) - atom.config.set('a.c', 1, {scopeSelector: '.x1', source: 'source-1'}) - atom.config.set('a.b', 2, {scopeSelector: '.x2', source: 'source-2'}) - atom.config.set('a.b', 1, {scopeSelector: '.x3', source: 'source-3'}) + atom.config.set('a.b', 1, { scopeSelector: '.x1', source: 'source-1' }) + atom.config.set('a.c', 1, { scopeSelector: '.x1', source: 'source-1' }) + atom.config.set('a.b', 2, { scopeSelector: '.x2', source: 'source-2' }) + atom.config.set('a.b', 1, { scopeSelector: '.x3', source: 'source-3' }) expect(atom.config.getSources()).toEqual([ 'source-1', @@ -758,10 +1025,10 @@ describe('Config', () => { atom.config.set('a.b.c', 1) atom.config.set('a.b.d', 2) atom.config.set('x.y.z', 3) - atom.config.setDefaults('a.b', {e: 4, f: 5}) + atom.config.setDefaults('a.b', { e: 4, f: 5 }) atom.config.save() - expect(savedSettings).toEqual([{'*': atom.config.settings}]) + expect(savedSettings).toEqual([{ '*': atom.config.settings }]) }) it('serializes properties in alphabetical order', () => { @@ -774,12 +1041,12 @@ describe('Config', () => { atom.config.save() const writtenConfig = savedSettings[0] - expect(writtenConfig).toEqual({'*': atom.config.settings}) + expect(writtenConfig).toEqual({ '*': atom.config.settings }) let expectedKeys = ['bar', 'baz', 'foo'] let foundKeys = [] for (const key in writtenConfig['*']) { - if ((expectedKeys).includes(key)) { + if (expectedKeys.includes(key)) { foundKeys.push(key) } } @@ -787,7 +1054,7 @@ describe('Config', () => { expectedKeys = ['bar', 'foo'] foundKeys = [] for (const key in writtenConfig['*']['baz']) { - if ((expectedKeys).includes(key)) { + if (expectedKeys.includes(key)) { foundKeys.push(key) } } @@ -796,17 +1063,18 @@ describe('Config', () => { describe('when scoped settings are defined', () => { it('serializes any explicitly set config settings', () => { - atom.config.set('foo.bar', 'ruby', {scopeSelector: '.source.ruby'}) - atom.config.set('foo.omg', 'wow', {scopeSelector: '.source.ruby'}) - atom.config.set('foo.bar', 'coffee', {scopeSelector: '.source.coffee'}) + atom.config.set('foo.bar', 'ruby', { scopeSelector: '.source.ruby' }) + atom.config.set('foo.omg', 'wow', { scopeSelector: '.source.ruby' }) + atom.config.set('foo.bar', 'coffee', { + scopeSelector: '.source.coffee' + }) savedSettings.length = 0 atom.config.save() const writtenConfig = savedSettings[0] expect(writtenConfig).toEqualJson({ - '*': - atom.config.settings, + '*': atom.config.settings, '.ruby.source': { foo: { bar: 'ruby', @@ -856,7 +1124,9 @@ describe('Config', () => { } }) expect(atom.config.get('foo.bar')).toBe('baz') - expect(atom.config.get('foo.bar', {scope: ['.source.ruby']})).toBe('more-specific') + expect(atom.config.get('foo.bar', { scope: ['.source.ruby'] })).toBe( + 'more-specific' + ) }) }) @@ -878,20 +1148,22 @@ describe('Config', () => { }) expect(atom.config.get('foo.int')).toBe(12) expect(atom.config.get('foo.bar')).toBe('omg') - expect(atom.config.get('foo.int', {scope: ['.source.ruby']})).toBe(12) - expect(atom.config.get('foo.bar', {scope: ['.source.ruby']})).toBe('scoped') + expect(atom.config.get('foo.int', { scope: ['.source.ruby'] })).toBe(12) + expect(atom.config.get('foo.bar', { scope: ['.source.ruby'] })).toBe( + 'scoped' + ) }) }) it('updates the config data based on the file contents', () => { - atom.config.resetUserSettings({foo: {bar: 'baz'}}) + atom.config.resetUserSettings({ foo: { bar: 'baz' } }) expect(atom.config.get('foo.bar')).toBe('baz') }) it('notifies observers for updated keypaths on load', () => { const observeHandler = jasmine.createSpy('observeHandler') atom.config.observe('foo.bar', observeHandler) - atom.config.resetUserSettings({foo: {bar: 'baz'}}) + atom.config.resetUserSettings({ foo: { bar: 'baz' } }) expect(observeHandler).toHaveBeenCalledWith('baz') }) @@ -912,14 +1184,14 @@ describe('Config', () => { it('does not fire a change event for paths that did not change', () => { atom.config.resetUserSettings({ - foo: {bar: 'baz', int: 3} + foo: { bar: 'baz', int: 3 } }) const noChangeSpy = jasmine.createSpy('unchanged') - atom.config.onDidChange('foo.bar', (noChangeSpy)) + atom.config.onDidChange('foo.bar', noChangeSpy) atom.config.resetUserSettings({ - foo: {bar: 'baz', int: 4} + foo: { bar: 'baz', int: 4 } }) expect(noChangeSpy).not.toHaveBeenCalled() @@ -936,14 +1208,14 @@ describe('Config', () => { }) atom.config.resetUserSettings({ - foo: {bar: ['baz', 'quux'], int: 2} + foo: { bar: ['baz', 'quux'], int: 2 } }) const noChangeSpy = jasmine.createSpy('unchanged') - atom.config.onDidChange('foo.bar', (noChangeSpy)) + atom.config.onDidChange('foo.bar', noChangeSpy) atom.config.resetUserSettings({ - foo: {bar: ['baz', 'quux'], int: 2} + foo: { bar: ['baz', 'quux'], int: 2 } }) expect(noChangeSpy).not.toHaveBeenCalled() @@ -953,18 +1225,18 @@ describe('Config', () => { describe('when a setting with a default is removed', () => { it('resets the setting back to the default', () => { atom.config.resetUserSettings({ - foo: {bar: ['baz', 'quux'], int: 2} + foo: { bar: ['baz', 'quux'], int: 2 } }) const events = [] atom.config.onDidChange('foo.int', event => events.push(event)) atom.config.resetUserSettings({ - foo: {bar: ['baz', 'quux']} + foo: { bar: ['baz', 'quux'] } }) expect(events.length).toBe(1) - expect(events[0]).toEqual({oldValue: 2, newValue: 12}) + expect(events[0]).toEqual({ oldValue: 2, newValue: 12 }) }) }) }) @@ -978,7 +1250,9 @@ describe('Config', () => { expect(atom.config.pushAtKeyPath('foo.bar.baz', 'b')).toBe(2) expect(atom.config.get('foo.bar.baz')).toEqual(['a', 'b']) - expect(observeHandler).toHaveBeenCalledWith(atom.config.get('foo.bar.baz')) + expect(observeHandler).toHaveBeenCalledWith( + atom.config.get('foo.bar.baz') + ) }) }) @@ -991,7 +1265,9 @@ describe('Config', () => { expect(atom.config.unshiftAtKeyPath('foo.bar.baz', 'a')).toBe(2) expect(atom.config.get('foo.bar.baz')).toEqual(['a', 'b']) - expect(observeHandler).toHaveBeenCalledWith(atom.config.get('foo.bar.baz')) + expect(observeHandler).toHaveBeenCalledWith( + atom.config.get('foo.bar.baz') + ) }) }) @@ -1002,21 +1278,26 @@ describe('Config', () => { atom.config.observe('foo.bar.baz', observeHandler) observeHandler.reset() - expect(atom.config.removeAtKeyPath('foo.bar.baz', 'b')).toEqual(['a', 'c']) + expect(atom.config.removeAtKeyPath('foo.bar.baz', 'b')).toEqual([ + 'a', + 'c' + ]) expect(atom.config.get('foo.bar.baz')).toEqual(['a', 'c']) - expect(observeHandler).toHaveBeenCalledWith(atom.config.get('foo.bar.baz')) + expect(observeHandler).toHaveBeenCalledWith( + atom.config.get('foo.bar.baz') + ) }) }) describe('.setDefaults(keyPath, defaults)', () => { it('assigns any previously-unassigned keys to the object at the key path', () => { - atom.config.set('foo.bar.baz', {a: 1}) - atom.config.setDefaults('foo.bar.baz', {a: 2, b: 3, c: 4}) + atom.config.set('foo.bar.baz', { a: 1 }) + atom.config.setDefaults('foo.bar.baz', { a: 2, b: 3, c: 4 }) expect(atom.config.get('foo.bar.baz.a')).toBe(1) expect(atom.config.get('foo.bar.baz.b')).toBe(3) expect(atom.config.get('foo.bar.baz.c')).toBe(4) - atom.config.setDefaults('foo.quux', {x: 0, y: 1}) + atom.config.setDefaults('foo.quux', { x: 0, y: 1 }) expect(atom.config.get('foo.quux.x')).toBe(0) expect(atom.config.get('foo.quux.y')).toBe(1) }) @@ -1025,7 +1306,7 @@ describe('Config', () => { const updatedCallback = jasmine.createSpy('updated') atom.config.onDidChange('foo.bar.baz.a', updatedCallback) expect(updatedCallback.callCount).toBe(0) - atom.config.setDefaults('foo.bar.baz', {a: 2}) + atom.config.setDefaults('foo.bar.baz', { a: 2 }) expect(updatedCallback.callCount).toBe(1) }) }) @@ -1165,7 +1446,7 @@ describe('Config', () => { default: 12 }) - expect(atom.config.getSchema('foo.baz')).toEqual({type: 'any'}) + expect(atom.config.getSchema('foo.baz')).toEqual({ type: 'any' }) expect(atom.config.getSchema('foo.bar.anInt.baz')).toBe(null) }) @@ -1182,8 +1463,12 @@ describe('Config', () => { atom.config.setSchema('foo.bar.str', schema) expect(atom.config.get('foo.bar.str')).toBe('ok') - expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('omg') - expect(atom.config.get('foo.bar.str', {scope: ['.source.coffee']})).toBe('ok') + expect(atom.config.get('foo.bar.str', { scope: ['.source.js'] })).toBe( + 'omg' + ) + expect( + atom.config.get('foo.bar.str', { scope: ['.source.coffee'] }) + ).toBe('ok') }) describe('when a schema is added after config values have been set', () => { @@ -1206,47 +1491,93 @@ describe('Config', () => { it('respects the new schema when values are set', () => { expect(atom.config.set('foo.bar.str', 'global')).toBe(true) - expect(atom.config.set('foo.bar.str', 'scoped', {scopeSelector: '.source.js'})).toBe(true) + expect( + atom.config.set('foo.bar.str', 'scoped', { + scopeSelector: '.source.js' + }) + ).toBe(true) expect(atom.config.get('foo.bar.str')).toBe('global') - expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('scoped') + expect(atom.config.get('foo.bar.str', { scope: ['.source.js'] })).toBe( + 'scoped' + ) expect(atom.config.set('foo.bar.noschema', 'nsGlobal')).toBe(true) - expect(atom.config.set('foo.bar.noschema', 'nsScoped', {scopeSelector: '.source.js'})).toBe(true) + expect( + atom.config.set('foo.bar.noschema', 'nsScoped', { + scopeSelector: '.source.js' + }) + ).toBe(true) expect(atom.config.get('foo.bar.noschema')).toBe('nsGlobal') - expect(atom.config.get('foo.bar.noschema', {scope: ['.source.js']})).toBe('nsScoped') + expect( + atom.config.get('foo.bar.noschema', { scope: ['.source.js'] }) + ).toBe('nsScoped') expect(atom.config.set('foo.bar.int', 'nope')).toBe(true) - expect(atom.config.set('foo.bar.int', 'notanint', {scopeSelector: '.source.js'})).toBe(true) - expect(atom.config.set('foo.bar.int', 23, {scopeSelector: '.source.coffee'})).toBe(true) + expect( + atom.config.set('foo.bar.int', 'notanint', { + scopeSelector: '.source.js' + }) + ).toBe(true) + expect( + atom.config.set('foo.bar.int', 23, { + scopeSelector: '.source.coffee' + }) + ).toBe(true) expect(atom.config.get('foo.bar.int')).toBe('nope') - expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe('notanint') - expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + expect(atom.config.get('foo.bar.int', { scope: ['.source.js'] })).toBe( + 'notanint' + ) + expect( + atom.config.get('foo.bar.int', { scope: ['.source.coffee'] }) + ).toBe(23) atom.config.setSchema('foo.bar', schema) expect(atom.config.get('foo.bar.str')).toBe('global') - expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('scoped') + expect(atom.config.get('foo.bar.str', { scope: ['.source.js'] })).toBe( + 'scoped' + ) expect(atom.config.get('foo.bar.noschema')).toBe('nsGlobal') - expect(atom.config.get('foo.bar.noschema', {scope: ['.source.js']})).toBe('nsScoped') + expect( + atom.config.get('foo.bar.noschema', { scope: ['.source.js'] }) + ).toBe('nsScoped') expect(atom.config.get('foo.bar.int')).toBe(2) - expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe(2) - expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + expect(atom.config.get('foo.bar.int', { scope: ['.source.js'] })).toBe( + 2 + ) + expect( + atom.config.get('foo.bar.int', { scope: ['.source.coffee'] }) + ).toBe(23) }) it('sets all values that adhere to the schema', () => { expect(atom.config.set('foo.bar.int', 10)).toBe(true) - expect(atom.config.set('foo.bar.int', 15, {scopeSelector: '.source.js'})).toBe(true) - expect(atom.config.set('foo.bar.int', 23, {scopeSelector: '.source.coffee'})).toBe(true) + expect( + atom.config.set('foo.bar.int', 15, { scopeSelector: '.source.js' }) + ).toBe(true) + expect( + atom.config.set('foo.bar.int', 23, { + scopeSelector: '.source.coffee' + }) + ).toBe(true) expect(atom.config.get('foo.bar.int')).toBe(10) - expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe(15) - expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + expect(atom.config.get('foo.bar.int', { scope: ['.source.js'] })).toBe( + 15 + ) + expect( + atom.config.get('foo.bar.int', { scope: ['.source.coffee'] }) + ).toBe(23) atom.config.setSchema('foo.bar', schema) expect(atom.config.get('foo.bar.int')).toBe(10) - expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe(15) - expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + expect(atom.config.get('foo.bar.int', { scope: ['.source.js'] })).toBe( + 15 + ) + expect( + atom.config.get('foo.bar.int', { scope: ['.source.coffee'] }) + ).toBe(23) }) }) @@ -1449,7 +1780,7 @@ describe('Config', () => { expect(atom.config.set('foo.bar.aString', [])).toBe(false) expect(atom.config.get('foo.bar.aString')).toBe('ok') - expect(atom.config.set('foo.bar.aString', {nope: 'nope'})).toBe(false) + expect(atom.config.set('foo.bar.aString', { nope: 'nope' })).toBe(false) expect(atom.config.get('foo.bar.aString')).toBe('ok') }) @@ -1468,8 +1799,7 @@ describe('Config', () => { atom.config.setSchema('foo.bar.aString', schema) atom.config.set('foo.bar.aString', 'abcdefg') expect(atom.config.get('foo.bar.aString')).toBe('abc') - }) - ) + })) }) describe('when the value has an "object" type', () => { @@ -1501,8 +1831,7 @@ describe('Config', () => { nestedObject: { nestedBool: 'true' } - } - ) + }) expect(atom.config.get('foo.bar')).toEqual({ anInt: 23, nestedObject: { @@ -1512,13 +1841,14 @@ describe('Config', () => { }) it('will set only the values that adhere to the schema', () => { - expect(atom.config.set('foo.bar', { - anInt: 'nope', - nestedObject: { - nestedBool: true - } - } - )).toBe(true) + expect( + atom.config.set('foo.bar', { + anInt: 'nope', + nestedObject: { + nestedBool: true + } + }) + ).toBe(true) expect(atom.config.get('foo.bar.anInt')).toEqual(12) expect(atom.config.get('foo.bar.nestedObject.nestedBool')).toEqual(true) }) @@ -1534,17 +1864,19 @@ describe('Config', () => { } }, additionalProperties: false - } - ) + }) - expect(atom.config.set('foo.bar', {anInt: 5, somethingElse: 'ok'})).toBe(true) + expect( + atom.config.set('foo.bar', { anInt: 5, somethingElse: 'ok' }) + ).toBe(true) expect(atom.config.get('foo.bar.anInt')).toBe(5) expect(atom.config.get('foo.bar.somethingElse')).toBeUndefined() - expect(atom.config.set('foo.bar.somethingElse', {anInt: 5})).toBe(false) + expect(atom.config.set('foo.bar.somethingElse', { anInt: 5 })).toBe( + false + ) expect(atom.config.get('foo.bar.somethingElse')).toBeUndefined() - }) - ) + })) describe('when the value has an additionalProperties schema', () => it('validates properties of the object against that schema', () => { @@ -1559,21 +1891,23 @@ describe('Config', () => { additionalProperties: { type: 'string' } - } - ) + }) - expect(atom.config.set('foo.bar', {anInt: 5, somethingElse: 'ok'})).toBe(true) + expect( + atom.config.set('foo.bar', { anInt: 5, somethingElse: 'ok' }) + ).toBe(true) expect(atom.config.get('foo.bar.anInt')).toBe(5) expect(atom.config.get('foo.bar.somethingElse')).toBe('ok') expect(atom.config.set('foo.bar.somethingElse', 7)).toBe(false) expect(atom.config.get('foo.bar.somethingElse')).toBe('ok') - expect(atom.config.set('foo.bar', {anInt: 6, somethingElse: 7})).toBe(true) + expect( + atom.config.set('foo.bar', { anInt: 6, somethingElse: 7 }) + ).toBe(true) expect(atom.config.get('foo.bar.anInt')).toBe(6) expect(atom.config.get('foo.bar.somethingElse')).toBe(undefined) - }) - ) + })) }) describe('when the value has an "array" type', () => { @@ -1647,46 +1981,131 @@ describe('Config', () => { it('coerces various types to a color object', () => { atom.config.set('foo.bar.aColor', 'red') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 0, blue: 0, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 0, + blue: 0, + alpha: 1 + }) atom.config.set('foo.bar.aColor', '#020') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 34, blue: 0, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 0, + green: 34, + blue: 0, + alpha: 1 + }) atom.config.set('foo.bar.aColor', '#abcdef') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 171, green: 205, blue: 239, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 171, + green: 205, + blue: 239, + alpha: 1 + }) atom.config.set('foo.bar.aColor', 'rgb(1,2,3)') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 1, green: 2, blue: 3, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 1, + green: 2, + blue: 3, + alpha: 1 + }) atom.config.set('foo.bar.aColor', 'rgba(4,5,6,.7)') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 4, green: 5, blue: 6, alpha: 0.7}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 4, + green: 5, + blue: 6, + alpha: 0.7 + }) atom.config.set('foo.bar.aColor', 'hsl(120,100%,50%)') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 255, blue: 0, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 0, + green: 255, + blue: 0, + alpha: 1 + }) atom.config.set('foo.bar.aColor', 'hsla(120,100%,50%,0.3)') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 255, blue: 0, alpha: 0.3}) - atom.config.set('foo.bar.aColor', {red: 100, green: 255, blue: 2, alpha: 0.5}) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 100, green: 255, blue: 2, alpha: 0.5}) - atom.config.set('foo.bar.aColor', {red: 255}) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 0, blue: 0, alpha: 1}) - atom.config.set('foo.bar.aColor', {red: 1000}) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 0, blue: 0, alpha: 1}) - atom.config.set('foo.bar.aColor', {red: 'dark'}) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 0, blue: 0, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 0, + green: 255, + blue: 0, + alpha: 0.3 + }) + atom.config.set('foo.bar.aColor', { + red: 100, + green: 255, + blue: 2, + alpha: 0.5 + }) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 100, + green: 255, + blue: 2, + alpha: 0.5 + }) + atom.config.set('foo.bar.aColor', { red: 255 }) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 0, + blue: 0, + alpha: 1 + }) + atom.config.set('foo.bar.aColor', { red: 1000 }) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 0, + blue: 0, + alpha: 1 + }) + atom.config.set('foo.bar.aColor', { red: 'dark' }) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 0, + green: 0, + blue: 0, + alpha: 1 + }) }) it('reverts back to the default value when undefined is passed to set', () => { atom.config.set('foo.bar.aColor', undefined) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 255, + blue: 255, + alpha: 1 + }) }) it('will not set non-colors', () => { atom.config.set('foo.bar.aColor', null) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 255, + blue: 255, + alpha: 1 + }) atom.config.set('foo.bar.aColor', 'nope') - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 255, + blue: 255, + alpha: 1 + }) atom.config.set('foo.bar.aColor', 30) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 255, + blue: 255, + alpha: 1 + }) atom.config.set('foo.bar.aColor', false) - expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + expect(atom.config.get('foo.bar.aColor')).toEqual({ + red: 255, + green: 255, + blue: 255, + alpha: 1 + }) }) it('returns a clone of the Color when returned in a parent object', () => { @@ -1726,9 +2145,9 @@ describe('Config', () => { type: 'string', default: 'one', enum: [ - {value: 'one', description: 'One'}, + { value: 'one', description: 'One' }, 'two', - {value: 'three', description: 'Three'} + { value: 'three', description: 'Three' } ] } } @@ -1821,54 +2240,78 @@ describe('Config', () => { const dummyPath = '/Users/dummy/path.json' describe('project settings', () => { it('returns a deep clone of the property value', () => { - atom.config.resetProjectSettings({'*': {'value': {array: [1, {b: 2}, 3]}}}, dummyPath) + atom.config.resetProjectSettings( + { '*': { value: { array: [1, { b: 2 }, 3] } } }, + dummyPath + ) const retrievedValue = atom.config.get('value') retrievedValue.array[0] = 4 retrievedValue.array[1].b = 2.1 - expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]}) + expect(atom.config.get('value')).toEqual({ array: [1, { b: 2 }, 3] }) }) it('properly gets project settings', () => { - atom.config.resetProjectSettings({'*': {'foo': 'wei'}}, dummyPath) + atom.config.resetProjectSettings({ '*': { foo: 'wei' } }, dummyPath) expect(atom.config.get('foo')).toBe('wei') - atom.config.resetProjectSettings({'*': {'foo': {'bar': 'baz'}}}, dummyPath) + atom.config.resetProjectSettings( + { '*': { foo: { bar: 'baz' } } }, + dummyPath + ) expect(atom.config.get('foo.bar')).toBe('baz') }) it('gets project settings with higher priority than regular settings', () => { atom.config.set('foo', 'bar') - atom.config.resetProjectSettings({'*': {'foo': 'baz'}}, dummyPath) + atom.config.resetProjectSettings({ '*': { foo: 'baz' } }, dummyPath) expect(atom.config.get('foo')).toBe('baz') }) it('correctly gets nested and scoped properties for project settings', () => { expect(atom.config.set('foo.bar.str', 'global')).toBe(true) - expect(atom.config.set('foo.bar.str', 'scoped', {scopeSelector: '.source.js'})).toBe(true) + expect( + atom.config.set('foo.bar.str', 'scoped', { + scopeSelector: '.source.js' + }) + ).toBe(true) expect(atom.config.get('foo.bar.str')).toBe('global') - expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('scoped') + expect( + atom.config.get('foo.bar.str', { scope: ['.source.js'] }) + ).toBe('scoped') }) it('returns a deep clone of the property value', () => { - atom.config.set('value', {array: [1, {b: 2}, 3]}) + atom.config.set('value', { array: [1, { b: 2 }, 3] }) const retrievedValue = atom.config.get('value') retrievedValue.array[0] = 4 retrievedValue.array[1].b = 2.1 - expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]}) + expect(atom.config.get('value')).toEqual({ array: [1, { b: 2 }, 3] }) }) it('gets scoped values correctly', () => { - atom.config.set('foo', 'bam', {scope: ['second']}) - expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('bam') - atom.config.resetProjectSettings({'*': {'foo': 'baz'}, 'second': {'foo': 'bar'}}, dummyPath) - expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('baz') + atom.config.set('foo', 'bam', { scope: ['second'] }) + expect(atom.config.get('foo', { scopeSelector: 'second' })).toBe( + 'bam' + ) + atom.config.resetProjectSettings( + { '*': { foo: 'baz' }, second: { foo: 'bar' } }, + dummyPath + ) + expect(atom.config.get('foo', { scopeSelector: 'second' })).toBe( + 'baz' + ) atom.config.clearProjectSettings() - expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('bam') + expect(atom.config.get('foo', { scopeSelector: 'second' })).toBe( + 'bam' + ) }) it('clears project settings correctly', () => { atom.config.set('foo', 'bar') expect(atom.config.get('foo')).toBe('bar') - atom.config.resetProjectSettings({'*': {'foo': 'baz'}, 'second': {'foo': 'bar'}}, dummyPath) + atom.config.resetProjectSettings( + { '*': { foo: 'baz' }, second: { foo: 'bar' } }, + dummyPath + ) expect(atom.config.get('foo')).toBe('baz') expect(atom.config.getSources().length).toBe(1) atom.config.clearProjectSettings() @@ -1881,12 +2324,14 @@ describe('Config', () => { describe('config.getAll', () => { const dummyPath = '/Users/dummy/path.json' it('gets settings in the same way .get would return them', () => { - atom.config.resetProjectSettings({'*': {'a': 'b'}}, dummyPath) + atom.config.resetProjectSettings({ '*': { a: 'b' } }, dummyPath) atom.config.set('a', 'f') - expect(atom.config.getAll('a')).toEqual([{ - scopeSelector: '*', - value: 'b' - }]) + expect(atom.config.getAll('a')).toEqual([ + { + scopeSelector: '*', + value: 'b' + } + ]) }) }) }) diff --git a/spec/decoration-manager-spec.coffee b/spec/decoration-manager-spec.coffee index 02073a3a2..b53458cd3 100644 --- a/spec/decoration-manager-spec.coffee +++ b/spec/decoration-manager-spec.coffee @@ -1,7 +1,6 @@ DecorationManager = require '../src/decoration-manager' TextEditor = require '../src/text-editor' -# Tests crash the renderer process on Electron 3.0, disabling for now. describe "DecorationManager", -> [decorationManager, buffer, editor, markerLayer1, markerLayer2] = [] diff --git a/spec/dock-spec.js b/spec/dock-spec.js index 4713347a8..8d2a0907c 100644 --- a/spec/dock-spec.js +++ b/spec/dock-spec.js @@ -2,7 +2,6 @@ const Grim = require('grim') -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' import etch from 'etch' const getNextUpdatePromise = () => etch.getScheduler().nextUpdatePromise @@ -16,7 +15,12 @@ describe('Dock', () => { dock.onDidChangeVisible(didChangeVisibleSpy) expect(dock.isVisible()).toBe(false) - expect(document.activeElement).toBe(atom.workspace.getCenter().getActivePane().getElement()) + expect(document.activeElement).toBe( + atom.workspace + .getCenter() + .getActivePane() + .getElement() + ) dock.activate() expect(dock.isVisible()).toBe(true) expect(document.activeElement).toBe(dock.getActivePane().getElement()) @@ -36,7 +40,12 @@ describe('Dock', () => { expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(true) dock.hide() - expect(document.activeElement).toBe(atom.workspace.getCenter().getActivePane().getElement()) + expect(document.activeElement).toBe( + atom.workspace + .getCenter() + .getActivePane() + .getElement() + ) expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(false) dock.activate() @@ -44,13 +53,18 @@ describe('Dock', () => { expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(true) dock.toggle() - expect(document.activeElement).toBe(atom.workspace.getCenter().getActivePane().getElement()) + expect(document.activeElement).toBe( + atom.workspace + .getCenter() + .getActivePane() + .getElement() + ) expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(false) // Don't change focus if the dock was not focused in the first place const modalElement = document.createElement('div') modalElement.setAttribute('tabindex', -1) - atom.workspace.addModalPanel({item: modalElement}) + atom.workspace.addModalPanel({ item: modalElement }) modalElement.focus() expect(document.activeElement).toBe(modalElement) @@ -68,13 +82,18 @@ describe('Dock', () => { it('opens the dock', async () => { const item = { element: document.createElement('div'), - getDefaultLocation() { return 'left' } + getDefaultLocation () { + return 'left' + } } - await atom.workspace.open(item, {activatePane: false}) + await atom.workspace.open(item, { activatePane: false }) expect(atom.workspace.getLeftDock().isVisible()).toBe(false) - atom.workspace.getLeftDock().getPanes()[0].activate() + atom.workspace + .getLeftDock() + .getPanes()[0] + .activate() expect(atom.workspace.getLeftDock().isVisible()).toBe(true) }) }) @@ -145,47 +164,63 @@ describe('Dock', () => { describe('when the dock resize handle is double-clicked', () => { describe('when the dock is open', () => { - it('resizes a vertically-oriented dock to the current item\'s preferred width', async () => { + it("resizes a vertically-oriented dock to the current item's preferred width", async () => { jasmine.attachToDOM(atom.workspace.getElement()) const item = { element: document.createElement('div'), - getDefaultLocation() { return 'left' }, - getPreferredWidth() { return 142 }, - getPreferredHeight() { return 122 } + getDefaultLocation () { + return 'left' + }, + getPreferredWidth () { + return 142 + }, + getPreferredHeight () { + return 122 + } } await atom.workspace.open(item) const dock = atom.workspace.getLeftDock() const dockElement = dock.getElement() - dock.setState({size: 300}) + dock.setState({ size: 300 }) await getNextUpdatePromise() expect(dockElement.offsetWidth).toBe(300) - dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2})) + dockElement + .querySelector('.atom-dock-resize-handle') + .dispatchEvent(new MouseEvent('mousedown', { detail: 2 })) await getNextUpdatePromise() expect(dockElement.offsetWidth).toBe(item.getPreferredWidth()) }) - it('resizes a horizontally-oriented dock to the current item\'s preferred width', async () => { + it("resizes a horizontally-oriented dock to the current item's preferred width", async () => { jasmine.attachToDOM(atom.workspace.getElement()) const item = { element: document.createElement('div'), - getDefaultLocation() { return 'bottom' }, - getPreferredWidth() { return 122 }, - getPreferredHeight() { return 142 } + getDefaultLocation () { + return 'bottom' + }, + getPreferredWidth () { + return 122 + }, + getPreferredHeight () { + return 142 + } } await atom.workspace.open(item) const dock = atom.workspace.getBottomDock() const dockElement = dock.getElement() - dock.setState({size: 300}) + dock.setState({ size: 300 }) await getNextUpdatePromise() expect(dockElement.offsetHeight).toBe(300) - dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2})) + dockElement + .querySelector('.atom-dock-resize-handle') + .dispatchEvent(new MouseEvent('mousedown', { detail: 2 })) await getNextUpdatePromise() expect(dockElement.offsetHeight).toBe(item.getPreferredHeight()) @@ -198,19 +233,31 @@ describe('Dock', () => { const item = { element: document.createElement('div'), - getDefaultLocation() { return 'bottom' }, - getPreferredWidth() { return 122 }, - getPreferredHeight() { return 142 } + getDefaultLocation () { + return 'bottom' + }, + getPreferredWidth () { + return 122 + }, + getPreferredHeight () { + return 142 + } } - await atom.workspace.open(item, {activatePane: false}) + await atom.workspace.open(item, { activatePane: false }) const dockElement = atom.workspace.getBottomDock().getElement() - dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2})) + dockElement + .querySelector('.atom-dock-resize-handle') + .dispatchEvent(new MouseEvent('mousedown', { detail: 2 })) expect(dockElement.offsetHeight).toBe(0) - expect(dockElement.querySelector('.atom-dock-inner').offsetHeight).toBe(0) + expect(dockElement.querySelector('.atom-dock-inner').offsetHeight).toBe( + 0 + ) // The content should be masked away. - expect(dockElement.querySelector('.atom-dock-mask').offsetHeight).toBe(0) + expect(dockElement.querySelector('.atom-dock-mask').offsetHeight).toBe( + 0 + ) }) }) }) @@ -222,8 +269,12 @@ describe('Dock', () => { const createItem = preferredWidth => ({ element: document.createElement('div'), - getDefaultLocation() { return 'left' }, - getPreferredWidth() { return preferredWidth } + getDefaultLocation () { + return 'left' + }, + getPreferredWidth () { + return preferredWidth + } }) const dock = atom.workspace.getLeftDock() @@ -257,7 +308,9 @@ describe('Dock', () => { const item = { element: document.createElement('div'), - getDefaultLocation() { return 'left' } + getDefaultLocation () { + return 'left' + } } const dock = atom.workspace.getLeftDock() expect(dock.getPaneItems()).toHaveLength(0) @@ -275,11 +328,15 @@ describe('Dock', () => { const item = { element: document.createElement('div'), - getDefaultLocation() { return 'left' }, - getPreferredWidth() { return 122 }, - serialize: () => ({deserializer: 'DockTestItem'}) + getDefaultLocation () { + return 'left' + }, + getPreferredWidth () { + return 122 + }, + serialize: () => ({ deserializer: 'DockTestItem' }) } - const itemDeserializer = atom.deserializers.add({ + atom.deserializers.add({ name: 'DockTestItem', deserialize: () => item }) @@ -287,10 +344,10 @@ describe('Dock', () => { const dockElement = dock.getElement() await atom.workspace.open(item) - dock.setState({size: 150}) + dock.setState({ size: 150 }) expect(dockElement.offsetWidth).toBe(150) const serialized = dock.serialize() - dock.setState({size: 122}) + dock.setState({ size: 122 }) expect(dockElement.offsetWidth).toBe(122) dock.destroyActivePane() dock.deserialize(serialized, atom.deserializers) @@ -302,8 +359,12 @@ describe('Dock', () => { const item = { element: document.createElement('div'), - getDefaultLocation() { return 'left' }, - getPreferredWidth() { return 122 } + getDefaultLocation () { + return 'left' + }, + getPreferredWidth () { + return 122 + } } const dock = atom.workspace.getLeftDock() @@ -324,16 +385,22 @@ describe('Dock', () => { element.setAttribute('is', 'tabs-tab') element.item = { element, - getDefaultLocation() { return 'left' }, - getPreferredWidth() { return 144 } + getDefaultLocation () { + return 'left' + }, + getPreferredWidth () { + return 144 + } } const dragEvent = new DragEvent('dragstart') - Object.defineProperty(dragEvent, 'target', {value: element}) + Object.defineProperty(dragEvent, 'target', { value: element }) atom.workspace.getElement().handleDragStart(dragEvent) await getNextUpdatePromise() - expect(atom.workspace.getLeftDock().refs.wrapperElement.offsetWidth).toBe(144) + expect(atom.workspace.getLeftDock().refs.wrapperElement.offsetWidth).toBe( + 144 + ) }) it('does nothing when text nodes are dragged', () => { @@ -342,9 +409,11 @@ describe('Dock', () => { const textNode = document.createTextNode('hello') const dragEvent = new DragEvent('dragstart') - Object.defineProperty(dragEvent, 'target', {value: textNode}) + Object.defineProperty(dragEvent, 'target', { value: textNode }) - expect(() => atom.workspace.getElement().handleDragStart(dragEvent)).not.toThrow() + expect(() => + atom.workspace.getElement().handleDragStart(dragEvent) + ).not.toThrow() }) }) diff --git a/spec/fixtures/babel/invalid.js b/spec/fixtures/babel/invalid.js index 513264538..585a4365b 100644 --- a/spec/fixtures/babel/invalid.js +++ b/spec/fixtures/babel/invalid.js @@ -1,3 +1,3 @@ 'use 6to6'; -export default 42; +module.exports = async function* hello() {} diff --git a/spec/git-repository-provider-spec.js b/spec/git-repository-provider-spec.js index 24993fe9b..5a4c4ba07 100644 --- a/spec/git-repository-provider-spec.js +++ b/spec/git-repository-provider-spec.js @@ -1,16 +1,19 @@ const path = require('path') const fs = require('fs-plus') const temp = require('temp').track() -const {Directory} = require('pathwatcher') +const { Directory } = require('pathwatcher') const GitRepository = require('../src/git-repository') const GitRepositoryProvider = require('../src/git-repository-provider') -const {it, fit, ffit, fffit, beforeEach} = require('./async-spec-helpers') describe('GitRepositoryProvider', () => { let provider beforeEach(() => { - provider = new GitRepositoryProvider(atom.project, atom.config, atom.confirm) + provider = new GitRepositoryProvider( + atom.project, + atom.config, + atom.confirm + ) }) afterEach(() => { @@ -24,7 +27,9 @@ describe('GitRepositoryProvider', () => { describe('.repositoryForDirectory(directory)', () => { describe('when specified a Directory with a Git repository', () => { it('resolves with a GitRepository', async () => { - const directory = new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git')) + const directory = new Directory( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) const result = await provider.repositoryForDirectory(directory) expect(result).toBeInstanceOf(GitRepository) expect(provider.pathToRepository[result.getPath()]).toBeTruthy() @@ -39,7 +44,9 @@ describe('GitRepositoryProvider', () => { new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git')) ) const secondRepo = await provider.repositoryForDirectory( - new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects')) + new Directory( + path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects') + ) ) expect(firstRepo).toBeInstanceOf(GitRepository) @@ -72,7 +79,10 @@ describe('GitRepositoryProvider', () => { it('returns a Promise that resolves to a GitRepository', async () => { const gitDirPath = path.join(__dirname, 'fixtures', 'git', 'master.git') const workDirPath = temp.mkdirSync('git-workdir') - fs.writeFileSync(path.join(workDirPath, '.git'), `gitdir: ${gitDirPath}\n`) + fs.writeFileSync( + path.join(workDirPath, '.git'), + `gitdir: ${gitDirPath}\n` + ) const directory = new Directory(workDirPath) const result = await provider.repositoryForDirectory(directory) @@ -82,6 +92,90 @@ describe('GitRepositoryProvider', () => { }) }) + describe('when specified a Directory without exists()', () => { + let directory + + beforeEach(() => { + // An implementation of Directory that does not implement existsSync(). + const subdirectory = {} + directory = { + getSubdirectory () {}, + isRoot () { + return true + } + } + spyOn(directory, 'getSubdirectory').andReturn(subdirectory) + }) + + it('returns a Promise that resolves to null', async () => { + const repo = await provider.repositoryForDirectory(directory) + expect(repo).toBe(null) + expect(directory.getSubdirectory).toHaveBeenCalledWith('.git') + }) + }) + }) + + describe('.repositoryForDirectorySync(directory)', () => { + describe('when specified a Directory with a Git repository', () => { + it('resolves with a GitRepository', async () => { + const directory = new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git')) + const result = provider.repositoryForDirectorySync(directory) + expect(result).toBeInstanceOf(GitRepository) + expect(provider.pathToRepository[result.getPath()]).toBeTruthy() + expect(result.getType()).toBe('git') + + // Refresh should be started + await new Promise(resolve => result.onDidChangeStatuses(resolve)) + }) + + it('resolves with the same GitRepository for different Directory objects in the same repo', () => { + const firstRepo = provider.repositoryForDirectorySync( + new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git')) + ) + const secondRepo = provider.repositoryForDirectorySync( + new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects')) + ) + + expect(firstRepo).toBeInstanceOf(GitRepository) + expect(firstRepo).toBe(secondRepo) + }) + }) + + describe('when specified a Directory without a Git repository', () => { + it('resolves with null', () => { + const directory = new Directory(temp.mkdirSync('dir')) + const repo = provider.repositoryForDirectorySync(directory) + expect(repo).toBe(null) + }) + }) + + describe('when specified a Directory with an invalid Git repository', () => { + it('resolves with null', () => { + const dirPath = temp.mkdirSync('dir') + fs.writeFileSync(path.join(dirPath, '.git', 'objects'), '') + fs.writeFileSync(path.join(dirPath, '.git', 'HEAD'), '') + fs.writeFileSync(path.join(dirPath, '.git', 'refs'), '') + + const directory = new Directory(dirPath) + const repo = provider.repositoryForDirectorySync(directory) + expect(repo).toBe(null) + }) + }) + + describe('when specified a Directory with a valid gitfile-linked repository', () => { + it('returns a Promise that resolves to a GitRepository', () => { + const gitDirPath = path.join(__dirname, 'fixtures', 'git', 'master.git') + const workDirPath = temp.mkdirSync('git-workdir') + fs.writeFileSync(path.join(workDirPath, '.git'), `gitdir: ${gitDirPath}\n`) + + const directory = new Directory(workDirPath) + const result = provider.repositoryForDirectorySync(directory) + expect(result).toBeInstanceOf(GitRepository) + expect(provider.pathToRepository[result.getPath()]).toBeTruthy() + expect(result.getType()).toBe('git') + }) + }) + describe('when specified a Directory without existsSync()', () => { let directory @@ -100,12 +194,6 @@ describe('GitRepositoryProvider', () => { expect(repo).toBe(null) expect(directory.getSubdirectory).toHaveBeenCalledWith('.git') }) - - it('returns a Promise that resolves to null for the async implementation', async () => { - const repo = await provider.repositoryForDirectory(directory) - expect(repo).toBe(null) - expect(directory.getSubdirectory).toHaveBeenCalledWith('.git') - }) }) }) }) diff --git a/spec/git-repository-spec.js b/spec/git-repository-spec.js index 65548fb3b..ed5699843 100644 --- a/spec/git-repository-spec.js +++ b/spec/git-repository-spec.js @@ -1,4 +1,3 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') const path = require('path') const fs = require('fs-plus') const temp = require('temp').track() @@ -25,30 +24,44 @@ describe('GitRepository', () => { describe('new GitRepository(path)', () => { it('throws an exception when no repository is found', () => { - expect(() => new GitRepository(path.join(temp.dir, 'nogit.txt'))).toThrow() + expect( + () => new GitRepository(path.join(temp.dir, 'nogit.txt')) + ).toThrow() }) }) describe('.getPath()', () => { it('returns the repository path for a .git directory path with a directory', () => { - repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects')) - expect(repo.getPath()).toBe(path.join(__dirname, 'fixtures', 'git', 'master.git')) + repo = new GitRepository( + path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects') + ) + expect(repo.getPath()).toBe( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) }) it('returns the repository path for a repository path', () => { - repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git')) - expect(repo.getPath()).toBe(path.join(__dirname, 'fixtures', 'git', 'master.git')) + repo = new GitRepository( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) + expect(repo.getPath()).toBe( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) }) }) describe('.isPathIgnored(path)', () => { it('returns true for an ignored path', () => { - repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'ignore.git')) + repo = new GitRepository( + path.join(__dirname, 'fixtures', 'git', 'ignore.git') + ) expect(repo.isPathIgnored('a.txt')).toBeTruthy() }) it('returns false for a non-ignored path', () => { - repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'ignore.git')) + repo = new GitRepository( + path.join(__dirname, 'fixtures', 'git', 'ignore.git') + ) expect(repo.isPathIgnored('b.txt')).toBeFalsy() }) }) @@ -136,7 +149,10 @@ describe('GitRepository', () => { repo.onDidChangeStatus(statusHandler) repo.checkoutHead(filePath) expect(statusHandler.callCount).toBe(1) - expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: 0}) + expect(statusHandler.argsForCall[0][0]).toEqual({ + path: filePath, + pathStatus: 0 + }) repo.checkoutHead(filePath) expect(statusHandler.callCount).toBe(1) @@ -150,7 +166,11 @@ describe('GitRepository', () => { spyOn(atom, 'confirm') const workingDirPath = copyRepository() - repo = new GitRepository(workingDirPath, {project: atom.project, config: atom.config, confirm: atom.confirm}) + repo = new GitRepository(workingDirPath, { + project: atom.project, + config: atom.config, + confirm: atom.confirm + }) filePath = path.join(workingDirPath, 'a.txt') fs.writeFileSync(filePath, 'ch ch changes') @@ -161,7 +181,7 @@ describe('GitRepository', () => { // Permissions issues with this test on Windows if (process.platform === 'win32') return - atom.confirm.andCallFake(({buttons}) => buttons.OK()) + atom.confirm.andCallFake(({ buttons }) => buttons.OK()) atom.config.set('editor.confirmCheckoutHeadRevision', true) repo.checkoutHeadForEditor(editor) @@ -183,7 +203,9 @@ describe('GitRepository', () => { describe('.destroy()', () => { it('throws an exception when any method is called after it is called', () => { - repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git')) + repo = new GitRepository( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) repo.destroy() expect(() => repo.getShortHead()).toThrow() }) @@ -204,7 +226,10 @@ describe('GitRepository', () => { fs.writeFileSync(filePath, '') let status = repo.getPathStatus(filePath) expect(statusHandler.callCount).toBe(1) - expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: status}) + expect(statusHandler.argsForCall[0][0]).toEqual({ + path: filePath, + pathStatus: status + }) fs.writeFileSync(filePath, 'abc') status = repo.getPathStatus(filePath) @@ -223,10 +248,14 @@ describe('GitRepository', () => { }) it('gets the status based on the files inside the directory', () => { - expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe(false) + expect( + repo.isStatusModified(repo.getDirectoryStatus(directoryPath)) + ).toBe(false) fs.writeFileSync(filePath, 'abc') repo.getPathStatus(filePath) - expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe(true) + expect( + repo.isStatusModified(repo.getDirectoryStatus(directoryPath)) + ).toBe(true) }) }) @@ -235,7 +264,10 @@ describe('GitRepository', () => { beforeEach(() => { workingDirectory = copyRepository() - repo = new GitRepository(workingDirectory, {project: atom.project, config: atom.config}) + repo = new GitRepository(workingDirectory, { + project: atom.project, + config: atom.config + }) modifiedPath = path.join(workingDirectory, 'file.txt') newPath = path.join(workingDirectory, 'untracked.txt') cleanPath = path.join(workingDirectory, 'other.txt') @@ -252,8 +284,10 @@ describe('GitRepository', () => { await repo.refreshStatus() expect(statusHandler.callCount).toBe(1) expect(repo.getCachedPathStatus(cleanPath)).toBeUndefined() - expect(repo.isStatusNew(repo.getCachedPathStatus(newPath) )).toBeTruthy() - expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy() + expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy() + expect( + repo.isStatusModified(repo.getCachedPathStatus(modifiedPath)) + ).toBeTruthy() }) it('caches the proper statuses when a subdir is open', async () => { @@ -278,7 +312,9 @@ describe('GitRepository', () => { await repo.refreshStatus() expect(repo.getCachedPathStatus(cleanPath)).toBeUndefined() expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy() - expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy() + expect( + repo.isStatusModified(repo.getCachedPathStatus(modifiedPath)) + ).toBeTruthy() }) it('caches statuses that were looked up synchronously', async () => { @@ -288,7 +324,9 @@ describe('GitRepository', () => { fs.writeFileSync(modifiedPath, originalContent) await repo.refreshStatus() - expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeFalsy() + expect( + repo.isStatusModified(repo.getCachedPathStatus(modifiedPath)) + ).toBeFalsy() }) }) @@ -297,7 +335,9 @@ describe('GitRepository', () => { beforeEach(async () => { atom.project.setPaths([copyRepository()]) - const refreshPromise = new Promise(resolve => atom.project.getRepositories()[0].onDidChangeStatuses(resolve)) + const refreshPromise = new Promise(resolve => + atom.project.getRepositories()[0].onDidChangeStatuses(resolve) + ) editor = await atom.workspace.open('other.txt') await refreshPromise }) @@ -310,7 +350,10 @@ describe('GitRepository', () => { await editor.save() expect(statusHandler.callCount).toBe(1) - expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256}) + expect(statusHandler).toHaveBeenCalledWith({ + path: editor.getPath(), + pathStatus: 256 + }) }) it('emits a status-changed event when a buffer is reloaded', async () => { @@ -321,7 +364,10 @@ describe('GitRepository', () => { await editor.getBuffer().reload() expect(statusHandler.callCount).toBe(1) - expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256}) + expect(statusHandler).toHaveBeenCalledWith({ + path: editor.getPath(), + pathStatus: 256 + }) await editor.getBuffer().reload() expect(statusHandler.callCount).toBe(1) @@ -334,7 +380,10 @@ describe('GitRepository', () => { atom.project.getRepositories()[0].onDidChangeStatus(statusHandler) editor.getBuffer().emitter.emit('did-change-path') expect(statusHandler.callCount).toBe(1) - expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256}) + expect(statusHandler).toHaveBeenCalledWith({ + path: editor.getPath(), + pathStatus: 256 + }) editor.getBuffer().emitter.emit('did-change-path') expect(statusHandler.callCount).toBe(1) }) @@ -364,11 +413,9 @@ describe('GitRepository', () => { grammarRegistry: atom.grammars, applicationDelegate: atom.applicationDelegate }) - await project2.deserialize(atom.project.serialize({isUnloading: false})) + await project2.deserialize(atom.project.serialize({ isUnloading: false })) buffer = project2.getBuffers()[0] - - const originalContent = buffer.getText() buffer.append('changes') statusHandler = jasmine.createSpy('statusHandler') @@ -376,14 +423,23 @@ describe('GitRepository', () => { await buffer.save() expect(statusHandler.callCount).toBe(1) - expect(statusHandler).toHaveBeenCalledWith({path: buffer.getPath(), pathStatus: 256}) + expect(statusHandler).toHaveBeenCalledWith({ + path: buffer.getPath(), + pathStatus: 256 + }) }) }) }) function copyRepository () { const workingDirPath = temp.mkdirSync('atom-spec-git') - fs.copySync(path.join(__dirname, 'fixtures', 'git', 'working-dir'), workingDirPath) - fs.renameSync(path.join(workingDirPath, 'git.git'), path.join(workingDirPath, '.git')) + fs.copySync( + path.join(__dirname, 'fixtures', 'git', 'working-dir'), + workingDirPath + ) + fs.renameSync( + path.join(workingDirPath, 'git.git'), + path.join(workingDirPath, '.git') + ) return workingDirPath } diff --git a/spec/grammar-registry-spec.js b/spec/grammar-registry-spec.js index b6b314ac3..34cea04b5 100644 --- a/spec/grammar-registry-spec.js +++ b/spec/grammar-registry-spec.js @@ -1,5 +1,3 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') - const dedent = require('dedent') const path = require('path') const fs = require('fs-plus') @@ -13,14 +11,18 @@ describe('GrammarRegistry', () => { let grammarRegistry beforeEach(() => { - grammarRegistry = new GrammarRegistry({config: atom.config}) + grammarRegistry = new GrammarRegistry({ config: atom.config }) expect(subscriptionCount(grammarRegistry)).toBe(1) }) describe('.assignLanguageMode(buffer, languageId)', () => { it('assigns to the buffer a language mode with the given language id', async () => { - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-css/grammars/css.cson') + ) const buffer = new TextBuffer() expect(grammarRegistry.assignLanguageMode(buffer, 'source.js')).toBe(true) @@ -31,7 +33,9 @@ describe('GrammarRegistry', () => { expect(grammarRegistry.assignLanguageMode(buffer, 'source.js')).toBe(true) // Language names are not case-sensitive - expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true) + expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe( + true + ) expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css') // Returns false if no language is found @@ -41,14 +45,20 @@ describe('GrammarRegistry', () => { describe('when no languageId is passed', () => { it('makes the buffer use the null grammar', () => { - grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-css/grammars/css.cson') + ) const buffer = new TextBuffer() - expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true) + expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe( + true + ) expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css') expect(grammarRegistry.assignLanguageMode(buffer, null)).toBe(true) - expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar') + expect(buffer.getLanguageMode().getLanguageId()).toBe( + 'text.plain.null-grammar' + ) expect(grammarRegistry.getAssignedLanguageId(buffer)).toBe(null) }) }) @@ -56,10 +66,18 @@ describe('GrammarRegistry', () => { describe('.grammarForId(languageId)', () => { it('returns a text-mate grammar when `core.useTreeSitterParsers` is false', () => { - atom.config.set('core.useTreeSitterParsers', false, {scopeSelector: '.source.js'}) + atom.config.set('core.useTreeSitterParsers', false, { + scopeSelector: '.source.js' + }) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) const grammar = grammarRegistry.grammarForId('source.js') expect(grammar instanceof FirstMate.Grammar).toBe(true) @@ -72,26 +90,40 @@ describe('GrammarRegistry', () => { it('returns a tree-sitter grammar when `core.useTreeSitterParsers` is true', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) const grammar = grammarRegistry.grammarForId('source.js') expect(grammar instanceof TreeSitterGrammar).toBe(true) expect(grammar.scopeName).toBe('source.js') grammarRegistry.removeGrammar(grammar) - expect(grammarRegistry.grammarForId('source.js') instanceof FirstMate.Grammar).toBe(true) + expect( + grammarRegistry.grammarForId('source.js') instanceof FirstMate.Grammar + ).toBe(true) }) }) describe('.autoAssignLanguageMode(buffer)', () => { it('assigns to the buffer a language mode based on the best available grammar', () => { - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-css/grammars/css.cson') + ) const buffer = new TextBuffer() buffer.setPath('foo.js') - expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true) + expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe( + true + ) expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css') grammarRegistry.autoAssignLanguageMode(buffer) @@ -103,8 +135,12 @@ describe('GrammarRegistry', () => { it('assigns a grammar to the buffer based on its path', async () => { const buffer = new TextBuffer() - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/c.cson') + ) buffer.setPath('test.js') grammarRegistry.maintainLanguageMode(buffer) @@ -114,7 +150,7 @@ describe('GrammarRegistry', () => { expect(buffer.getLanguageMode().getLanguageId()).toBe('source.c') }) - it('updates the buffer\'s grammar when a more appropriate text-mate grammar is added for its path', async () => { + it("updates the buffer's grammar when a more appropriate text-mate grammar is added for its path", async () => { atom.config.set('core.useTreeSitterParsers', false) const buffer = new TextBuffer() @@ -123,14 +159,20 @@ describe('GrammarRegistry', () => { buffer.setPath('test.js') grammarRegistry.maintainLanguageMode(buffer) - const textMateGrammar = grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + const textMateGrammar = grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) expect(buffer.getLanguageMode().grammar).toBe(textMateGrammar) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) expect(buffer.getLanguageMode().grammar).toBe(textMateGrammar) }) - it('updates the buffer\'s grammar when a more appropriate tree-sitter grammar is added for its path', async () => { + it("updates the buffer's grammar when a more appropriate tree-sitter grammar is added for its path", async () => { atom.config.set('core.useTreeSitterParsers', true) const buffer = new TextBuffer() @@ -139,10 +181,16 @@ describe('GrammarRegistry', () => { buffer.setPath('test.js') grammarRegistry.maintainLanguageMode(buffer) - const treeSitterGrammar = grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + const treeSitterGrammar = grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) expect(buffer.getLanguageMode().grammar).toBe(treeSitterGrammar) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) expect(buffer.getLanguageMode().grammar).toBe(treeSitterGrammar) }) @@ -152,41 +200,59 @@ describe('GrammarRegistry', () => { buffer.setPath('test.js') grammarRegistry.maintainLanguageMode(buffer) - grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson')) - expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true) + grammarRegistry.loadGrammarSync( + require.resolve('language-css/grammars/css.cson') + ) + expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe( + true + ) expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css') - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css') }) it('returns a disposable that can be used to stop the registry from updating the buffer', async () => { const buffer = new TextBuffer() - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) const previousSubscriptionCount = buffer.emitter.getTotalListenerCount() const disposable = grammarRegistry.maintainLanguageMode(buffer) - expect(buffer.emitter.getTotalListenerCount()).toBeGreaterThan(previousSubscriptionCount) + expect(buffer.emitter.getTotalListenerCount()).toBeGreaterThan( + previousSubscriptionCount + ) expect(retainedBufferCount(grammarRegistry)).toBe(1) buffer.setPath('test.js') expect(buffer.getLanguageMode().getLanguageId()).toBe('source.js') buffer.setPath('test.txt') - expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar') + expect(buffer.getLanguageMode().getLanguageId()).toBe( + 'text.plain.null-grammar' + ) disposable.dispose() - expect(buffer.emitter.getTotalListenerCount()).toBe(previousSubscriptionCount) + expect(buffer.emitter.getTotalListenerCount()).toBe( + previousSubscriptionCount + ) expect(retainedBufferCount(grammarRegistry)).toBe(0) buffer.setPath('test.js') - expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar') + expect(buffer.getLanguageMode().getLanguageId()).toBe( + 'text.plain.null-grammar' + ) expect(retainedBufferCount(grammarRegistry)).toBe(0) }) - it('doesn\'t do anything when called a second time with the same buffer', async () => { + it("doesn't do anything when called a second time with the same buffer", async () => { const buffer = new TextBuffer() - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) const disposable1 = grammarRegistry.maintainLanguageMode(buffer) const disposable2 = grammarRegistry.maintainLanguageMode(buffer) @@ -195,16 +261,22 @@ describe('GrammarRegistry', () => { disposable2.dispose() buffer.setPath('test.txt') - expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar') + expect(buffer.getLanguageMode().getLanguageId()).toBe( + 'text.plain.null-grammar' + ) disposable1.dispose() buffer.setPath('test.js') - expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar') + expect(buffer.getLanguageMode().getLanguageId()).toBe( + 'text.plain.null-grammar' + ) }) it('does not retain the buffer after the buffer is destroyed', () => { const buffer = new TextBuffer() - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) const disposable = grammarRegistry.maintainLanguageMode(buffer) expect(retainedBufferCount(grammarRegistry)).toBe(1) @@ -222,9 +294,11 @@ describe('GrammarRegistry', () => { it('does not retain the buffer when the grammar registry is destroyed', () => { const buffer = new TextBuffer() - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) - const disposable = grammarRegistry.maintainLanguageMode(buffer) + grammarRegistry.maintainLanguageMode(buffer) expect(retainedBufferCount(grammarRegistry)).toBe(1) expect(subscriptionCount(grammarRegistry)).toBe(3) @@ -238,19 +312,25 @@ describe('GrammarRegistry', () => { describe('.selectGrammar(filePath)', () => { it('always returns a grammar', () => { - const registry = new GrammarRegistry({config: atom.config}) + const registry = new GrammarRegistry({ config: atom.config }) expect(registry.selectGrammar().scopeName).toBe('text.plain.null-grammar') }) it('selects the text.plain grammar over the null grammar', async () => { await atom.packages.activatePackage('language-text') - expect(atom.grammars.selectGrammar('test.txt').scopeName).toBe('text.plain') + expect(atom.grammars.selectGrammar('test.txt').scopeName).toBe( + 'text.plain' + ) }) it('selects a grammar based on the file path case insensitively', async () => { await atom.packages.activatePackage('language-coffee-script') - expect(atom.grammars.selectGrammar('/tmp/source.coffee').scopeName).toBe('source.coffee') - expect(atom.grammars.selectGrammar('/tmp/source.COFFEE').scopeName).toBe('source.coffee') + expect(atom.grammars.selectGrammar('/tmp/source.coffee').scopeName).toBe( + 'source.coffee' + ) + expect(atom.grammars.selectGrammar('/tmp/source.COFFEE').scopeName).toBe( + 'source.coffee' + ) }) describe('on Windows', () => { @@ -258,16 +338,18 @@ describe('GrammarRegistry', () => { beforeEach(() => { originalPlatform = process.platform - Object.defineProperty(process, 'platform', {value: 'win32'}) + Object.defineProperty(process, 'platform', { value: 'win32' }) }) afterEach(() => { - Object.defineProperty(process, 'platform', {value: originalPlatform}) + Object.defineProperty(process, 'platform', { value: originalPlatform }) }) it('normalizes back slashes to forward slashes when matching the fileTypes', async () => { await atom.packages.activatePackage('language-git') - expect(atom.grammars.selectGrammar('something\\.git\\config').scopeName).toBe('source.git-config') + expect( + atom.grammars.selectGrammar('something\\.git\\config').scopeName + ).toBe('source.git-config') }) }) @@ -277,10 +359,14 @@ describe('GrammarRegistry', () => { await atom.packages.activatePackage('language-ruby') expect(atom.grammars.selectGrammar('file.js').name).toBe('JavaScript') // based on extension (.js) - expect(atom.grammars.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe('Git Config') // based on end of the path (.git/config) + expect( + atom.grammars.selectGrammar(path.join(temp.dir, '.git', 'config')).name + ).toBe('Git Config') // based on end of the path (.git/config) expect(atom.grammars.selectGrammar('Rakefile').name).toBe('Ruby') // based on the file's basename (Rakefile) expect(atom.grammars.selectGrammar('curb').name).toBe('Null Grammar') - expect(atom.grammars.selectGrammar('/hu.git/config').name).toBe('Null Grammar') + expect(atom.grammars.selectGrammar('/hu.git/config').name).toBe( + 'Null Grammar' + ) }) it("uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", async () => { @@ -296,13 +382,20 @@ describe('GrammarRegistry', () => { await atom.packages.activatePackage('language-coffee-script') let fileContent = 'first-line\n' - expect(atom.grammars.selectGrammar('dummy.coffee', fileContent).name).toBe('CoffeeScript') + expect( + atom.grammars.selectGrammar('dummy.coffee', fileContent).name + ).toBe('CoffeeScript') fileContent = '' - expect(atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name).toBe('Null Grammar') + expect( + atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name + ).toBe('Null Grammar') - fileContent += '\n' - expect(atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name).toBe('Property List (XML)') + fileContent += + '\n' + expect( + atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name + ).toBe('Property List (XML)') }) it("doesn't read the file when the file contents are specified", async () => { @@ -311,28 +404,36 @@ describe('GrammarRegistry', () => { const filePath = require.resolve('./fixtures/shebang') const filePathContents = fs.readFileSync(filePath, 'utf8') spyOn(fs, 'read').andCallThrough() - expect(atom.grammars.selectGrammar(filePath, filePathContents).name).toBe('Ruby') + expect(atom.grammars.selectGrammar(filePath, filePathContents).name).toBe( + 'Ruby' + ) expect(fs.read).not.toHaveBeenCalled() }) describe('when multiple grammars have matching fileTypes', () => { it('selects the grammar with the longest fileType match', () => { - const grammarPath1 = temp.path({suffix: '.json'}) - fs.writeFileSync(grammarPath1, JSON.stringify({ - name: 'test1', - scopeName: 'source1', - fileTypes: ['test'] - })) + const grammarPath1 = temp.path({ suffix: '.json' }) + fs.writeFileSync( + grammarPath1, + JSON.stringify({ + name: 'test1', + scopeName: 'source1', + fileTypes: ['test'] + }) + ) const grammar1 = atom.grammars.loadGrammarSync(grammarPath1) expect(atom.grammars.selectGrammar('more.test', '')).toBe(grammar1) fs.removeSync(grammarPath1) - const grammarPath2 = temp.path({suffix: '.json'}) - fs.writeFileSync(grammarPath2, JSON.stringify({ - name: 'test2', - scopeName: 'source2', - fileTypes: ['test', 'more.test'] - })) + const grammarPath2 = temp.path({ suffix: '.json' }) + fs.writeFileSync( + grammarPath2, + JSON.stringify({ + name: 'test2', + scopeName: 'source2', + fileTypes: ['test', 'more.test'] + }) + ) const grammar2 = atom.grammars.loadGrammarSync(grammarPath2) expect(atom.grammars.selectGrammar('more.test', '')).toBe(grammar2) return fs.removeSync(grammarPath2) @@ -341,19 +442,28 @@ describe('GrammarRegistry', () => { it('favors non-bundled packages when breaking scoring ties', async () => { await atom.packages.activatePackage('language-ruby') - await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'packages', 'package-with-rb-filetype')) + await atom.packages.activatePackage( + path.join(__dirname, 'fixtures', 'packages', 'package-with-rb-filetype') + ) atom.grammars.grammarForScopeName('source.ruby').bundledPackage = true atom.grammars.grammarForScopeName('test.rb').bundledPackage = false - expect(atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env ruby').scopeName).toBe('source.ruby') - expect(atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env testruby').scopeName).toBe('test.rb') + expect( + atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env ruby').scopeName + ).toBe('source.ruby') + expect( + atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env testruby') + .scopeName + ).toBe('test.rb') expect(atom.grammars.selectGrammar('test.rb').scopeName).toBe('test.rb') }) describe('when there is no file path', () => { it('does not throw an exception (regression)', () => { - expect(() => atom.grammars.selectGrammar(null, '#!/usr/bin/ruby')).not.toThrow() + expect(() => + atom.grammars.selectGrammar(null, '#!/usr/bin/ruby') + ).not.toThrow() expect(() => atom.grammars.selectGrammar(null, '')).not.toThrow() expect(() => atom.grammars.selectGrammar(null, null)).not.toThrow() }) @@ -362,8 +472,11 @@ describe('GrammarRegistry', () => { describe('when the user has custom grammar file types', () => { it('considers the custom file types as well as those defined in the grammar', async () => { await atom.packages.activatePackage('language-ruby') - atom.config.set('core.customFileTypes', {'source.ruby': ['Cheffile']}) - expect(atom.grammars.selectGrammar('build/Cheffile', 'cookbook "postgres"').scopeName).toBe('source.ruby') + atom.config.set('core.customFileTypes', { 'source.ruby': ['Cheffile'] }) + expect( + atom.grammars.selectGrammar('build/Cheffile', 'cookbook "postgres"') + .scopeName + ).toBe('source.ruby') }) it('favors user-defined file types over built-in ones of equal length', async () => { @@ -374,31 +487,50 @@ describe('GrammarRegistry', () => { 'source.coffee': ['Rakefile'], 'source.ruby': ['Cakefile'] }) - expect(atom.grammars.selectGrammar('Rakefile', '').scopeName).toBe('source.coffee') - expect(atom.grammars.selectGrammar('Cakefile', '').scopeName).toBe('source.ruby') + expect(atom.grammars.selectGrammar('Rakefile', '').scopeName).toBe( + 'source.coffee' + ) + expect(atom.grammars.selectGrammar('Cakefile', '').scopeName).toBe( + 'source.ruby' + ) }) it('favors user-defined file types over grammars with matching first-line-regexps', async () => { await atom.packages.activatePackage('language-ruby') await atom.packages.activatePackage('language-javascript') - atom.config.set('core.customFileTypes', {'source.ruby': ['bootstrap']}) - expect(atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node').scopeName).toBe('source.ruby') + atom.config.set('core.customFileTypes', { + 'source.ruby': ['bootstrap'] + }) + expect( + atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node') + .scopeName + ).toBe('source.ruby') }) }) it('favors a grammar with a matching file type over one with m matching first line pattern', async () => { await atom.packages.activatePackage('language-ruby') await atom.packages.activatePackage('language-javascript') - expect(atom.grammars.selectGrammar('foo.rb', '#!/usr/bin/env node').scopeName).toBe('source.ruby') + expect( + atom.grammars.selectGrammar('foo.rb', '#!/usr/bin/env node').scopeName + ).toBe('source.ruby') }) describe('tree-sitter vs text-mate', () => { it('favors a text-mate grammar over a tree-sitter grammar when `core.useTreeSitterParsers` is false', () => { - atom.config.set('core.useTreeSitterParsers', false, {scopeSelector: '.source.js'}) + atom.config.set('core.useTreeSitterParsers', false, { + scopeSelector: '.source.js' + }) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) const grammar = grammarRegistry.selectGrammar('test.js') expect(grammar.scopeName).toBe('source.js') @@ -408,8 +540,14 @@ describe('GrammarRegistry', () => { it('favors a tree-sitter grammar over a text-mate grammar when `core.useTreeSitterParsers` is true', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) const grammar = grammarRegistry.selectGrammar('test.js') expect(grammar instanceof TreeSitterGrammar).toBe(true) @@ -417,7 +555,11 @@ describe('GrammarRegistry', () => { it('only favors a tree-sitter grammar if it actually matches in some way (regression)', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) const grammar = grammarRegistry.selectGrammar('test', '') expect(grammar.name).toBe('Null Grammar') @@ -427,110 +569,164 @@ describe('GrammarRegistry', () => { describe('tree-sitter grammars with content regexes', () => { it('recognizes C++ header files', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-coffee-script/grammars/coffeescript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/tree-sitter-c.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/tree-sitter-cpp.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-coffee-script/grammars/coffeescript.cson') + ) - let grammar = grammarRegistry.selectGrammar('test.h', dedent ` + let grammar = grammarRegistry.selectGrammar( + 'test.h', + dedent` #include typedef struct { void verb(); } Noun; - `) + ` + ) expect(grammar.name).toBe('C') - grammar = grammarRegistry.selectGrammar('test.h', dedent ` + grammar = grammarRegistry.selectGrammar( + 'test.h', + dedent` #include class Noun { public: void verb(); }; - `) + ` + ) expect(grammar.name).toBe('C++') // The word `class` only indicates C++ in `.h` files, not in all files. - grammar = grammarRegistry.selectGrammar('test.coffee', dedent ` + grammar = grammarRegistry.selectGrammar( + 'test.coffee', + dedent` module.exports = class Noun verb: -> true - `) + ` + ) expect(grammar.name).toBe('CoffeeScript') }) it('recognizes C++ files that do not match the content regex (regression)', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c++.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/tree-sitter-c.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/c++.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/tree-sitter-cpp.cson') + ) - let grammar = grammarRegistry.selectGrammar('test.cc', dedent ` + let grammar = grammarRegistry.selectGrammar( + 'test.cc', + dedent` int a(); - `) + ` + ) expect(grammar.name).toBe('C++') }) it('does not apply content regexes from grammars without filetype or first line matches', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/tree-sitter-cpp.cson') + ) - let grammar = grammarRegistry.selectGrammar('', dedent ` + let grammar = grammarRegistry.selectGrammar( + '', + dedent` class Foo # this is ruby, not C++ end - `) + ` + ) expect(grammar.name).toBe('Null Grammar') }) it('recognizes shell scripts with shebang lines', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/shell-unix-bash.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/tree-sitter-bash.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-shellscript/grammars/shell-unix-bash.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-shellscript/grammars/tree-sitter-bash.cson') + ) - let grammar = grammarRegistry.selectGrammar('test.h', dedent ` + let grammar = grammarRegistry.selectGrammar( + 'test.h', + dedent` #!/bin/bash echo "hi" - `) + ` + ) expect(grammar.name).toBe('Shell Script') expect(grammar instanceof TreeSitterGrammar).toBeTruthy() - grammar = grammarRegistry.selectGrammar('test.h', dedent ` + grammar = grammarRegistry.selectGrammar( + 'test.h', + dedent` # vim: set ft=bash echo "hi" - `) + ` + ) expect(grammar.name).toBe('Shell Script') expect(grammar instanceof TreeSitterGrammar).toBeTruthy() atom.config.set('core.useTreeSitterParsers', false) - grammar = grammarRegistry.selectGrammar('test.h', dedent ` + grammar = grammarRegistry.selectGrammar( + 'test.h', + dedent` #!/bin/bash echo "hi" - `) + ` + ) expect(grammar.name).toBe('Shell Script') expect(grammar instanceof TreeSitterGrammar).toBeFalsy() }) it('recognizes JavaScript files that use Flow', () => { atom.config.set('core.useTreeSitterParsers', true) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-typescript/grammars/tree-sitter-flow.cson')) + grammarRegistry.loadGrammarSync( + require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' + ) + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-typescript/grammars/tree-sitter-flow.cson') + ) - let grammar = grammarRegistry.selectGrammar('test.js', dedent` + let grammar = grammarRegistry.selectGrammar( + 'test.js', + dedent` // Copyright something // @flow module.exports = function () { return 1 + 1 } - `) + ` + ) expect(grammar.name).toBe('Flow JavaScript') - grammar = grammarRegistry.selectGrammar('test.js', dedent` + grammar = grammarRegistry.selectGrammar( + 'test.js', + dedent` module.exports = function () { return 1 + 1 } - `) + ` + ) expect(grammar.name).toBe('JavaScript') }) }) @@ -548,8 +744,12 @@ describe('GrammarRegistry', () => { describe('.addInjectionPoint(languageId, {type, language, content})', () => { const injectionPoint = { type: 'some_node_type', - language() { return 'some_language_name' }, - content(node) { return node } + language () { + return 'some_language_name' + }, + content (node) { + return node + } } beforeEach(() => { @@ -574,13 +774,19 @@ describe('GrammarRegistry', () => { }) describe('serialization', () => { - it('persists editors\' grammar overrides', async () => { + it("persists editors' grammar overrides", async () => { const buffer1 = new TextBuffer() const buffer2 = new TextBuffer() - grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-html/grammars/html.cson')) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistry.loadGrammarSync( + require.resolve('language-c/grammars/c.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-html/grammars/html.cson') + ) + grammarRegistry.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) grammarRegistry.maintainLanguageMode(buffer1) grammarRegistry.maintainLanguageMode(buffer2) @@ -590,11 +796,17 @@ describe('GrammarRegistry', () => { const buffer1Copy = await TextBuffer.deserialize(buffer1.serialize()) const buffer2Copy = await TextBuffer.deserialize(buffer2.serialize()) - const grammarRegistryCopy = new GrammarRegistry({config: atom.config}) - grammarRegistryCopy.deserialize(JSON.parse(JSON.stringify(grammarRegistry.serialize()))) + const grammarRegistryCopy = new GrammarRegistry({ config: atom.config }) + grammarRegistryCopy.deserialize( + JSON.parse(JSON.stringify(grammarRegistry.serialize())) + ) - grammarRegistryCopy.loadGrammarSync(require.resolve('language-c/grammars/c.cson')) - grammarRegistryCopy.loadGrammarSync(require.resolve('language-html/grammars/html.cson')) + grammarRegistryCopy.loadGrammarSync( + require.resolve('language-c/grammars/c.cson') + ) + grammarRegistryCopy.loadGrammarSync( + require.resolve('language-html/grammars/html.cson') + ) expect(buffer1Copy.getLanguageMode().getLanguageId()).toBe(null) expect(buffer2Copy.getLanguageMode().getLanguageId()).toBe(null) @@ -604,7 +816,9 @@ describe('GrammarRegistry', () => { expect(buffer1Copy.getLanguageMode().getLanguageId()).toBe('source.c') expect(buffer2Copy.getLanguageMode().getLanguageId()).toBe(null) - grammarRegistryCopy.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + grammarRegistryCopy.loadGrammarSync( + require.resolve('language-javascript/grammars/javascript.cson') + ) expect(buffer1Copy.getLanguageMode().getLanguageId()).toBe('source.c') expect(buffer2Copy.getLanguageMode().getLanguageId()).toBe('source.js') }) diff --git a/spec/gutter-container-spec.js b/spec/gutter-container-spec.js index f41f1d220..50451ecfb 100644 --- a/spec/gutter-container-spec.js +++ b/spec/gutter-container-spec.js @@ -14,26 +14,28 @@ describe('GutterContainer', () => { describe('when initialized', () => it('it has no gutters', () => { expect(gutterContainer.getGutters().length).toBe(0) - }) - ) + })) describe('::addGutter', () => { it('creates a new gutter', () => { - const newGutter = gutterContainer.addGutter({'test-gutter': 'test-gutter', priority: 1}) + const newGutter = gutterContainer.addGutter({ + 'test-gutter': 'test-gutter', + priority: 1 + }) expect(gutterContainer.getGutters()).toEqual([newGutter]) expect(newGutter.priority).toBe(1) }) it('throws an error if the provided gutter name is already in use', () => { const name = 'test-gutter' - gutterContainer.addGutter({name}) - expect(gutterContainer.addGutter.bind(null, {name})).toThrow() + gutterContainer.addGutter({ name }) + expect(gutterContainer.addGutter.bind(null, { name })).toThrow() }) it('keeps added gutters sorted by ascending priority', () => { - const gutter1 = gutterContainer.addGutter({name: 'first', priority: 1}) - const gutter3 = gutterContainer.addGutter({name: 'third', priority: 3}) - const gutter2 = gutterContainer.addGutter({name: 'second', priority: 2}) + const gutter1 = gutterContainer.addGutter({ name: 'first', priority: 1 }) + const gutter3 = gutterContainer.addGutter({ name: 'third', priority: 3 }) + const gutter2 = gutterContainer.addGutter({ name: 'second', priority: 2 }) expect(gutterContainer.getGutters()).toEqual([gutter1, gutter2, gutter3]) }) }) @@ -44,11 +46,13 @@ describe('GutterContainer', () => { beforeEach(function () { gutterContainer = new GutterContainer(fakeTextEditor) removedGutters = [] - gutterContainer.onDidRemoveGutter(gutterName => removedGutters.push(gutterName)) + gutterContainer.onDidRemoveGutter(gutterName => + removedGutters.push(gutterName) + ) }) it('removes the gutter if it is contained by this GutterContainer', () => { - const gutter = gutterContainer.addGutter({'test-gutter': 'test-gutter'}) + const gutter = gutterContainer.addGutter({ 'test-gutter': 'test-gutter' }) expect(gutterContainer.getGutters()).toEqual([gutter]) gutterContainer.removeGutter(gutter) expect(gutterContainer.getGutters().length).toBe(0) @@ -65,13 +69,15 @@ describe('GutterContainer', () => { describe('::destroy', () => it('clears its array of gutters and destroys custom gutters', () => { - const newGutter = gutterContainer.addGutter({'test-gutter': 'test-gutter', priority: 1}) + const newGutter = gutterContainer.addGutter({ + 'test-gutter': 'test-gutter', + priority: 1 + }) const newGutterSpy = jasmine.createSpy() newGutter.onDidDestroy(newGutterSpy) gutterContainer.destroy() expect(newGutterSpy).toHaveBeenCalled() expect(gutterContainer.getGutters()).toEqual([]) - }) -) + })) }) diff --git a/spec/gutter-spec.js b/spec/gutter-spec.js index 4ae23db3e..c5051ea22 100644 --- a/spec/gutter-spec.js +++ b/spec/gutter-spec.js @@ -24,8 +24,7 @@ describe('Gutter', () => { expect(gutter.isVisible()).toBe(false) // An event should only be emitted when the visibility changes. expect(events.length).toBe(1) - }) - ) + })) describe('::show', () => it('shows the gutter if it is hidden.', () => { @@ -45,8 +44,7 @@ describe('Gutter', () => { expect(gutter.isVisible()).toBe(true) // An event should only be emitted when the visibility changes. expect(events.length).toBe(1) - }) - ) + })) describe('::destroy', () => { let mockGutterContainer, mockGutterContainerRemovedGutters @@ -61,21 +59,23 @@ describe('Gutter', () => { }) it('removes the gutter from its container.', () => { - const gutter = new Gutter(mockGutterContainer, {name}) + const gutter = new Gutter(mockGutterContainer, { name }) gutter.destroy() expect(mockGutterContainerRemovedGutters).toEqual([gutter]) }) it('calls all callbacks registered on ::onDidDestroy.', () => { - const gutter = new Gutter(mockGutterContainer, {name}) + const gutter = new Gutter(mockGutterContainer, { name }) let didDestroy = false - gutter.onDidDestroy(() => { didDestroy = true }) + gutter.onDidDestroy(() => { + didDestroy = true + }) gutter.destroy() expect(didDestroy).toBe(true) }) it('does not allow destroying the line-number gutter', () => { - const gutter = new Gutter(mockGutterContainer, {name: 'line-number'}) + const gutter = new Gutter(mockGutterContainer, { name: 'line-number' }) expect(gutter.destroy).toThrow() }) }) diff --git a/spec/helpers/random.js b/spec/helpers/random.js index 62f0e1920..3a2ff9159 100644 --- a/spec/helpers/random.js +++ b/spec/helpers/random.js @@ -1,5 +1,5 @@ const WORDS = require('./words') -const {Point, Range} = require('text-buffer') +const { Point, Range } = require('text-buffer') exports.getRandomBufferRange = function getRandomBufferRange (random, buffer) { const endRow = random(buffer.getLineCount()) diff --git a/spec/history-manager-spec.js b/spec/history-manager-spec.js index cc2a20058..bdf670425 100644 --- a/spec/history-manager-spec.js +++ b/spec/history-manager-spec.js @@ -1,10 +1,7 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') -const {Emitter, Disposable, CompositeDisposable} = require('event-kit') - -const {HistoryManager, HistoryProject} = require('../src/history-manager') +const { HistoryManager, HistoryProject } = require('../src/history-manager') const StateStore = require('../src/state-store') -describe("HistoryManager", () => { +describe('HistoryManager', () => { let historyManager, commandRegistry, project, stateStore let commandDisposable, projectDisposable @@ -16,19 +13,26 @@ describe("HistoryManager", () => { stateStore = new StateStore('history-manager-test', 1) await stateStore.save('history-manager', { projects: [ - {paths: ['/1', 'c:\\2'], lastOpened: new Date(2016, 9, 17, 17, 16, 23)}, - {paths: ['/test'], lastOpened: new Date(2016, 9, 17, 11, 12, 13)} + { + paths: ['/1', 'c:\\2'], + lastOpened: new Date(2016, 9, 17, 17, 16, 23) + }, + { paths: ['/test'], lastOpened: new Date(2016, 9, 17, 11, 12, 13) } ] }) projectDisposable = jasmine.createSpyObj('Disposable', ['dispose']) project = jasmine.createSpyObj('Project', ['onDidChangePaths']) - project.onDidChangePaths.andCallFake((f) => { + project.onDidChangePaths.andCallFake(f => { project.didChangePathsListener = f return projectDisposable }) - historyManager = new HistoryManager({stateStore, project, commands: commandRegistry}) + historyManager = new HistoryManager({ + stateStore, + project, + commands: commandRegistry + }) await historyManager.loadState() }) @@ -36,24 +40,29 @@ describe("HistoryManager", () => { await stateStore.clear() }) - describe("constructor", () => { + describe('constructor', () => { it("registers the 'clear-project-history' command function", () => { expect(commandRegistry.add).toHaveBeenCalled() const cmdCall = commandRegistry.add.calls[0] expect(cmdCall.args.length).toBe(3) expect(cmdCall.args[0]).toBe('atom-workspace') - expect(typeof cmdCall.args[1]['application:clear-project-history']).toBe('function') + expect(typeof cmdCall.args[1]['application:clear-project-history']).toBe( + 'function' + ) }) - describe("getProjects", () => { - it("returns an array of HistoryProjects", () => { + describe('getProjects', () => { + it('returns an array of HistoryProjects', () => { expect(historyManager.getProjects()).toEqual([ - new HistoryProject(['/1', 'c:\\2'], new Date(2016, 9, 17, 17, 16, 23)), + new HistoryProject( + ['/1', 'c:\\2'], + new Date(2016, 9, 17, 17, 16, 23) + ), new HistoryProject(['/test'], new Date(2016, 9, 17, 11, 12, 13)) ]) }) - it("returns an array of HistoryProjects that is not mutable state", () => { + it('returns an array of HistoryProjects that is not mutable state', () => { const firstProjects = historyManager.getProjects() firstProjects.pop() firstProjects[0].path = 'modified' @@ -64,21 +73,25 @@ describe("HistoryManager", () => { }) }) - describe("clearProjects", () => { - it("clears the list of projects", async () => { + describe('clearProjects', () => { + it('clears the list of projects', async () => { expect(historyManager.getProjects().length).not.toBe(0) await historyManager.clearProjects() expect(historyManager.getProjects().length).toBe(0) }) - it("saves the state", async () => { + it('saves the state', async () => { await historyManager.clearProjects() - const historyManager2 = new HistoryManager({stateStore, project, commands: commandRegistry}) + const historyManager2 = new HistoryManager({ + stateStore, + project, + commands: commandRegistry + }) await historyManager2.loadState() expect(historyManager.getProjects().length).toBe(0) }) - it("fires the onDidChangeProjects event", async () => { + it('fires the onDidChangeProjects event', async () => { const didChangeSpy = jasmine.createSpy() historyManager.onDidChangeProjects(didChangeSpy) await historyManager.clearProjects() @@ -87,7 +100,7 @@ describe("HistoryManager", () => { }) }) - it("listens to project.onDidChangePaths adding a new project", () => { + it('listens to project.onDidChangePaths adding a new project', () => { const start = new Date() project.didChangePathsListener(['/a/new', '/path/or/two']) const projects = historyManager.getProjects() @@ -96,7 +109,7 @@ describe("HistoryManager", () => { expect(projects[0].lastOpened).not.toBeLessThan(start) }) - it("listens to project.onDidChangePaths updating an existing project", () => { + it('listens to project.onDidChangePaths updating an existing project', () => { const start = new Date() project.didChangePathsListener(['/test']) const projects = historyManager.getProjects() @@ -106,22 +119,22 @@ describe("HistoryManager", () => { }) }) - describe("loadState", () => { - it("defaults to an empty array if no state", async () => { + describe('loadState', () => { + it('defaults to an empty array if no state', async () => { await stateStore.clear() await historyManager.loadState() expect(historyManager.getProjects()).toEqual([]) }) - it("defaults to an empty array if no projects", async () => { + it('defaults to an empty array if no projects', async () => { await stateStore.save('history-manager', {}) await historyManager.loadState() expect(historyManager.getProjects()).toEqual([]) }) }) - describe("addProject", () => { - it("adds a new project to the end", async () => { + describe('addProject', () => { + it('adds a new project to the end', async () => { const date = new Date(2010, 10, 9, 8, 7, 6) await historyManager.addProject(['/a/b'], date) const projects = historyManager.getProjects() @@ -130,7 +143,7 @@ describe("HistoryManager", () => { expect(projects[2].lastOpened).toBe(date) }) - it("adds a new project to the start", async () => { + it('adds a new project to the start', async () => { const date = new Date() await historyManager.addProject(['/so/new'], date) const projects = historyManager.getProjects() @@ -139,7 +152,7 @@ describe("HistoryManager", () => { expect(projects[0].lastOpened).toBe(date) }) - it("updates an existing project and moves it to the start", async () => { + it('updates an existing project and moves it to the start', async () => { const date = new Date() await historyManager.addProject(['/test'], date) const projects = historyManager.getProjects() @@ -148,7 +161,7 @@ describe("HistoryManager", () => { expect(projects[0].lastOpened).toBe(date) }) - it("fires the onDidChangeProjects event when adding a project", async () => { + it('fires the onDidChangeProjects event when adding a project', async () => { const didChangeSpy = jasmine.createSpy() const beforeCount = historyManager.getProjects().length historyManager.onDidChangeProjects(didChangeSpy) @@ -157,7 +170,7 @@ describe("HistoryManager", () => { expect(historyManager.getProjects().length).toBe(beforeCount + 1) }) - it("fires the onDidChangeProjects event when updating a project", async () => { + it('fires the onDidChangeProjects event when updating a project', async () => { const didChangeSpy = jasmine.createSpy() const beforeCount = historyManager.getProjects().length historyManager.onDidChangeProjects(didChangeSpy) @@ -167,8 +180,8 @@ describe("HistoryManager", () => { }) }) - describe("getProject", () => { - it("returns a project that matches the paths", () => { + describe('getProject', () => { + it('returns a project that matches the paths', () => { const project = historyManager.getProject(['/1', 'c:\\2']) expect(project).not.toBeNull() expect(project.paths).toEqual(['/1', 'c:\\2']) @@ -180,7 +193,7 @@ describe("HistoryManager", () => { }) }) - describe("saveState", () => { + describe('saveState', () => { let savedHistory beforeEach(() => { // historyManager.saveState is spied on globally to prevent specs from @@ -195,11 +208,17 @@ describe("HistoryManager", () => { }) }) - it("saves the state", async () => { - await historyManager.addProject(["/save/state"]) + it('saves the state', async () => { + await historyManager.addProject(['/save/state']) await historyManager.saveState() - const historyManager2 = new HistoryManager({stateStore, project, commands: commandRegistry}) - spyOn(historyManager2.stateStore, 'load').andCallFake(name => Promise.resolve(savedHistory)) + const historyManager2 = new HistoryManager({ + stateStore, + project, + commands: commandRegistry + }) + spyOn(historyManager2.stateStore, 'load').andCallFake(name => + Promise.resolve(savedHistory) + ) await historyManager2.loadState() expect(historyManager2.getProjects()[0].paths).toEqual(['/save/state']) }) diff --git a/spec/jasmine-junit-reporter.js b/spec/jasmine-junit-reporter.js new file mode 100644 index 000000000..14a4969b7 --- /dev/null +++ b/spec/jasmine-junit-reporter.js @@ -0,0 +1,21 @@ +require('jasmine-reporters') + +class JasmineJUnitReporter extends jasmine.JUnitXmlReporter { + fullDescription (spec) { + let fullDescription = spec.description + let currentSuite = spec.suite + while (currentSuite) { + fullDescription = currentSuite.description + ' ' + fullDescription + currentSuite = currentSuite.parentSuite + } + + return fullDescription + } + + reportSpecResults (spec) { + spec.description = this.fullDescription(spec) + return super.reportSpecResults(spec) + } +} + +module.exports = { JasmineJUnitReporter } diff --git a/spec/jasmine-list-reporter.js b/spec/jasmine-list-reporter.js index b6976b6ca..ffe8b0858 100644 --- a/spec/jasmine-list-reporter.js +++ b/spec/jasmine-list-reporter.js @@ -1,4 +1,4 @@ -const {TerminalReporter} = require('jasmine-tagged') +const { TerminalReporter } = require('jasmine-tagged') class JasmineListReporter extends TerminalReporter { fullDescription (spec) { diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee index 4e52ebf4e..8bf1d3ede 100644 --- a/spec/jasmine-test-runner.coffee +++ b/spec/jasmine-test-runner.coffee @@ -10,9 +10,15 @@ module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> window[key] = value for key, value of require '../vendor/jasmine' require 'jasmine-tagged' - if process.env.TEST_JUNIT_XML_PATH - require 'jasmine-reporters' - jasmine.getEnv().addReporter new jasmine.JUnitXmlReporter(process.env.TEST_JUNIT_XML_PATH, true, true) + # Rewrite global jasmine functions to have support for async tests. + # This way packages can create async specs without having to import these from the + # async-spec-helpers file. + global.it = asyncifyJasmineFn global.it, 1 + global.fit = asyncifyJasmineFn global.fit, 1 + global.ffit = asyncifyJasmineFn global.ffit, 1 + global.fffit = asyncifyJasmineFn global.fffit, 1 + global.beforeEach = asyncifyJasmineFn global.beforeEach, 0 + global.afterEach = asyncifyJasmineFn global.afterEach, 0 # Allow document.title to be assigned in specs without screwing up spec window title documentTitle = null @@ -44,6 +50,15 @@ module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> jasmineEnv.addReporter(buildReporter({logFile, headless, resolveWithExitCode})) TimeReporter = require './time-reporter' jasmineEnv.addReporter(new TimeReporter()) + + if process.env.TEST_JUNIT_XML_PATH + {JasmineJUnitReporter} = require './jasmine-junit-reporter' + process.stdout.write "Outputting JUnit XML to <#{process.env.TEST_JUNIT_XML_PATH}>\n" + outputDir = path.dirname(process.env.TEST_JUNIT_XML_PATH) + fileBase = path.basename(process.env.TEST_JUNIT_XML_PATH, '.xml') + + jasmineEnv.addReporter new JasmineJUnitReporter(outputDir, true, false, fileBase, true) + jasmineEnv.setIncludedTags([process.platform]) jasmineContent = document.createElement('div') @@ -54,6 +69,28 @@ module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) -> jasmineEnv.execute() promise +asyncifyJasmineFn = (fn, callbackPosition) -> + (args...) -> + if typeof args[callbackPosition] is 'function' + callback = args[callbackPosition] + + args[callbackPosition] = (args...) -> + result = callback.apply this, args + if result instanceof Promise + waitsForPromise(-> result) + + fn.apply this, args + +waitsForPromise = (fn) -> + promise = fn() + + global.waitsFor('spec promise to resolve', (done) -> + promise.then(done, (error) -> + jasmine.getEnv().currentSpec.fail error + done() + ) + ) + disableFocusMethods = -> ['fdescribe', 'ffdescribe', 'fffdescribe', 'fit', 'ffit', 'fffit'].forEach (methodName) -> focusMethod = window[methodName] @@ -119,4 +156,4 @@ buildTerminalReporter = (logFile, resolveWithExitCode) -> new JasmineListReporter(options) else {TerminalReporter} = require 'jasmine-tagged' - new TerminalReporter(options) + new TerminalReporter(options) \ No newline at end of file diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index c271608c4..83feac157 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -9,14 +9,21 @@ const path = require('path') const sinon = require('sinon') const AtomApplication = require('../../src/main-process/atom-application') const parseCommandLine = require('../../src/main-process/parse-command-line') -const {timeoutPromise, conditionPromise, emitterEventPromise} = require('../async-spec-helpers') +const { + timeoutPromise, + conditionPromise, + emitterEventPromise +} = require('../async-spec-helpers') const ATOM_RESOURCE_PATH = path.resolve(__dirname, '..', '..') describe('AtomApplication', function () { this.timeout(60 * 1000) - let originalAppQuit, originalShowMessageBox, originalAtomHome, atomApplicationsToDestroy + let originalAppQuit, + originalShowMessageBox, + originalAtomHome, + atomApplicationsToDestroy beforeEach(() => { originalAppQuit = electron.app.quit @@ -25,11 +32,15 @@ describe('AtomApplication', function () { originalAtomHome = process.env.ATOM_HOME process.env.ATOM_HOME = makeTempDir('atom-home') // Symlinking the compile cache into the temporary home dir makes the windows load much faster - fs.symlinkSync(path.join(originalAtomHome, 'compile-cache'), path.join(process.env.ATOM_HOME, 'compile-cache'), 'junction') + fs.symlinkSync( + path.join(originalAtomHome, 'compile-cache'), + path.join(process.env.ATOM_HOME, 'compile-cache'), + 'junction' + ) season.writeFileSync(path.join(process.env.ATOM_HOME, 'config.cson'), { '*': { - welcome: {showOnStartup: false}, - core: {telemetryConsent: 'no'} + welcome: { showOnStartup: false }, + core: { telemetryConsent: 'no' } } }) atomApplicationsToDestroy = [] @@ -54,10 +65,14 @@ describe('AtomApplication', function () { const tempDirPath2 = makeTempDir() const atomApplication1 = buildAtomApplication() - const [app1Window1] = await atomApplication1.launch(parseCommandLine([tempDirPath1])) + const [app1Window1] = await atomApplication1.launch( + parseCommandLine([tempDirPath1]) + ) await emitterEventPromise(app1Window1, 'window:locations-opened') - const [app1Window2] = await atomApplication1.launch(parseCommandLine([tempDirPath2])) + const [app1Window2] = await atomApplication1.launch( + parseCommandLine([tempDirPath2]) + ) await emitterEventPromise(app1Window2, 'window:locations-opened') await Promise.all([ @@ -66,30 +81,46 @@ describe('AtomApplication', function () { ]) const atomApplication2 = buildAtomApplication() - const [app2Window1, app2Window2] = await atomApplication2.launch(parseCommandLine([])) + const [app2Window1, app2Window2] = await atomApplication2.launch( + parseCommandLine([]) + ) await Promise.all([ emitterEventPromise(app2Window1, 'window:locations-opened'), emitterEventPromise(app2Window2, 'window:locations-opened') ]) - assert.deepEqual(await getTreeViewRootDirectories(app2Window1), [tempDirPath1]) - assert.deepEqual(await getTreeViewRootDirectories(app2Window2), [tempDirPath2]) + assert.deepEqual(await getTreeViewRootDirectories(app2Window1), [ + tempDirPath1 + ]) + assert.deepEqual(await getTreeViewRootDirectories(app2Window2), [ + tempDirPath2 + ]) }) it('when windows already exist, opens a new window with a single untitled buffer', async () => { const atomApplication = buildAtomApplication() const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) - const window1EditorTitle = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { - sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle()) - }) + const window1EditorTitle = await evalInWebContents( + window1.browserWindow.webContents, + sendBackToMainProcess => { + sendBackToMainProcess( + atom.workspace.getActiveTextEditor().getTitle() + ) + } + ) assert.equal(window1EditorTitle, 'untitled') const window2 = atomApplication.openWithOptions(parseCommandLine([])) await window2.loadedPromise - const window2EditorTitle = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { - sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle()) - }) + const window2EditorTitle = await evalInWebContents( + window1.browserWindow.webContents, + sendBackToMainProcess => { + sendBackToMainProcess( + atom.workspace.getActiveTextEditor().getTitle() + ) + } + ) assert.equal(window2EditorTitle, 'untitled') assert.deepEqual(atomApplication.getAllWindows(), [window2, window1]) @@ -101,10 +132,14 @@ describe('AtomApplication', function () { const tempDirPath2 = makeTempDir() const atomApplication1 = buildAtomApplication() - const [app1Window1] = await atomApplication1.launch(parseCommandLine([tempDirPath1])) + const [app1Window1] = await atomApplication1.launch( + parseCommandLine([tempDirPath1]) + ) await emitterEventPromise(app1Window1, 'window:locations-opened') - const [app1Window2] = await atomApplication1.launch(parseCommandLine([tempDirPath2])) + const [app1Window2] = await atomApplication1.launch( + parseCommandLine([tempDirPath2]) + ) await emitterEventPromise(app1Window2, 'window:locations-opened') await Promise.all([ @@ -114,13 +149,20 @@ describe('AtomApplication', function () { // Launch with --new-window const atomApplication2 = buildAtomApplication() - const appWindows2 = await atomApplication2.launch(parseCommandLine(['--new-window'])) + const appWindows2 = await atomApplication2.launch( + parseCommandLine(['--new-window']) + ) assert.lengthOf(appWindows2, 1) const [appWindow2] = appWindows2 await appWindow2.loadedPromise - const window2EditorTitle = await evalInWebContents(appWindow2.browserWindow.webContents, sendBackToMainProcess => { - sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle()) - }) + const window2EditorTitle = await evalInWebContents( + appWindow2.browserWindow.webContents, + sendBackToMainProcess => { + sendBackToMainProcess( + atom.workspace.getActiveTextEditor().getTitle() + ) + } + ) assert.equal(window2EditorTitle, 'untitled') }) @@ -135,12 +177,17 @@ describe('AtomApplication', function () { const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) - // wait a bit just to make sure we don't pass due to querying the render process before it loads + // wait a bit just to make sure we don't pass due to querying the render process before it loads await timeoutPromise(1000) - const itemCount = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { - sendBackToMainProcess(atom.workspace.getActivePane().getItems().length) - }) + const itemCount = await evalInWebContents( + window1.browserWindow.webContents, + sendBackToMainProcess => { + sendBackToMainProcess( + atom.workspace.getActivePane().getItems().length + ) + } + ) assert.equal(itemCount, 0) }) }) @@ -153,11 +200,18 @@ describe('AtomApplication', function () { fs.mkdirSync(dirBSubdirPath) const atomApplication = buildAtomApplication() - const [window1] = await atomApplication.launch(parseCommandLine([dirAPath, dirBPath])) + const [window1] = await atomApplication.launch( + parseCommandLine([dirAPath, dirBPath]) + ) await focusWindow(window1) - await conditionPromise(async () => (await getTreeViewRootDirectories(window1)).length === 2) - assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirBPath]) + await conditionPromise( + async () => (await getTreeViewRootDirectories(window1)).length === 2 + ) + assert.deepEqual(await getTreeViewRootDirectories(window1), [ + dirAPath, + dirBPath + ]) }) it('can open to a specific line number of a file', async () => { @@ -165,14 +219,19 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const [window] = await atomApplication.launch(parseCommandLine([filePath + ':3'])) + const [window] = await atomApplication.launch( + parseCommandLine([filePath + ':3']) + ) await focusWindow(window) - const cursorRow = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.observeTextEditors(textEditor => { - sendBackToMainProcess(textEditor.getCursorBufferPosition().row) - }) - }) + const cursorRow = await evalInWebContents( + window.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.observeTextEditors(textEditor => { + sendBackToMainProcess(textEditor.getCursorBufferPosition().row) + }) + } + ) assert.equal(cursorRow, 2) }) @@ -182,16 +241,21 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const [window] = await atomApplication.launch(parseCommandLine([filePath + ':2:2'])) + const [window] = await atomApplication.launch( + parseCommandLine([filePath + ':2:2']) + ) await focusWindow(window) - const cursorPosition = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.observeTextEditors(textEditor => { - sendBackToMainProcess(textEditor.getCursorBufferPosition()) - }) - }) + const cursorPosition = await evalInWebContents( + window.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.observeTextEditors(textEditor => { + sendBackToMainProcess(textEditor.getCursorBufferPosition()) + }) + } + ) - assert.deepEqual(cursorPosition, {row: 1, column: 1}) + assert.deepEqual(cursorPosition, { row: 1, column: 1 }) }) it('removes all trailing whitespace and colons from the specified path', async () => { @@ -199,31 +263,44 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const [window] = await atomApplication.launch(parseCommandLine([filePath + ':: '])) + const [window] = await atomApplication.launch( + parseCommandLine([filePath + ':: ']) + ) await focusWindow(window) - const openedPath = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.observeTextEditors(textEditor => { - sendBackToMainProcess(textEditor.getPath()) - }) - }) + const openedPath = await evalInWebContents( + window.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.observeTextEditors(textEditor => { + sendBackToMainProcess(textEditor.getPath()) + }) + } + ) assert.equal(openedPath, filePath) }) it('opens an empty text editor when launched with a new file path', async () => { // Choosing "Don't save" - mockElectronShowMessageBox({response: 2}) + mockElectronShowMessageBox({ response: 2 }) const atomApplication = buildAtomApplication() const newFilePath = path.join(makeTempDir(), 'new-file') - const [window] = await atomApplication.launch(parseCommandLine([newFilePath])) + const [window] = await atomApplication.launch( + parseCommandLine([newFilePath]) + ) await focusWindow(window) - const {editorTitle, editorText} = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.observeTextEditors(editor => { - sendBackToMainProcess({editorTitle: editor.getTitle(), editorText: editor.getText()}) - }) - }) + const { editorTitle, editorText } = await evalInWebContents( + window.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.observeTextEditors(editor => { + sendBackToMainProcess({ + editorTitle: editor.getTitle(), + editorText: editor.getText() + }) + }) + } + ) assert.equal(editorTitle, path.basename(newFilePath)) assert.equal(editorText, '') assert.deepEqual(await getTreeViewRootDirectories(window), []) @@ -239,32 +316,51 @@ describe('AtomApplication', function () { fs.writeFileSync(existingDirCFilePath, 'this is an existing file') const atomApplication = buildAtomApplication() - const [window1] = await atomApplication.launch(parseCommandLine([dirAPath])) + const [window1] = await atomApplication.launch( + parseCommandLine([dirAPath]) + ) await focusWindow(window1) - await conditionPromise(async () => (await getTreeViewRootDirectories(window1)).length === 1) + await conditionPromise( + async () => (await getTreeViewRootDirectories(window1)).length === 1 + ) assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath]) // When opening *files* with --add, reuses an existing window - let [reusedWindow] = await atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add'])) + let [reusedWindow] = await atomApplication.launch( + parseCommandLine([existingDirCFilePath, '--add']) + ) assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) - let activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { - const subscription = atom.workspace.onDidChangeActivePaneItem(textEditor => { - sendBackToMainProcess(textEditor.getPath()) - subscription.dispose() - }) - }) + let activeEditorPath = await evalInWebContents( + window1.browserWindow.webContents, + sendBackToMainProcess => { + const subscription = atom.workspace.onDidChangeActivePaneItem( + textEditor => { + sendBackToMainProcess(textEditor.getPath()) + subscription.dispose() + } + ) + } + ) assert.equal(activeEditorPath, existingDirCFilePath) assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath]) // When opening *directories* with --add, reuses an existing window and adds the directory to the project - reusedWindow = (await atomApplication.launch(parseCommandLine([dirBPath, '-a'])))[0] + reusedWindow = (await atomApplication.launch( + parseCommandLine([dirBPath, '-a']) + ))[0] assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) - await conditionPromise(async () => (await getTreeViewRootDirectories(reusedWindow)).length === 2) - assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirBPath]) + await conditionPromise( + async () => + (await getTreeViewRootDirectories(reusedWindow)).length === 2 + ) + assert.deepEqual(await getTreeViewRootDirectories(window1), [ + dirAPath, + dirBPath + ]) }) }) @@ -272,11 +368,15 @@ describe('AtomApplication', function () { it('positions new windows at an offset distance from the previous window', async () => { const atomApplication = buildAtomApplication() - const [window1] = await atomApplication.launch(parseCommandLine([makeTempDir()])) + const [window1] = await atomApplication.launch( + parseCommandLine([makeTempDir()]) + ) await focusWindow(window1) - window1.browserWindow.setBounds({width: 400, height: 400, x: 0, y: 0}) + window1.browserWindow.setBounds({ width: 400, height: 400, x: 0, y: 0 }) - const [window2] = await atomApplication.launch(parseCommandLine([makeTempDir()])) + const [window2] = await atomApplication.launch( + parseCommandLine([makeTempDir()]) + ) await focusWindow(window2) assert.notEqual(window1, window2) @@ -289,70 +389,109 @@ describe('AtomApplication', function () { it('persists window state based on the project directories', async () => { // Choosing "Don't save" - mockElectronShowMessageBox({response: 2}) + mockElectronShowMessageBox({ response: 2 }) const tempDirPath = makeTempDir() const atomApplication = buildAtomApplication() const nonExistentFilePath = path.join(tempDirPath, 'new-file') - const [window1] = await atomApplication.launch(parseCommandLine([tempDirPath, nonExistentFilePath])) - await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.observeTextEditors(textEditor => { - textEditor.insertText('Hello World!') - sendBackToMainProcess(null) - }) - }) + const [window1] = await atomApplication.launch( + parseCommandLine([tempDirPath, nonExistentFilePath]) + ) + await evalInWebContents( + window1.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.observeTextEditors(textEditor => { + textEditor.insertText('Hello World!') + sendBackToMainProcess(null) + }) + } + ) await window1.prepareToUnload() window1.close() await window1.closedPromise // Restore unsaved state when opening the same project directory - const [window2] = await atomApplication.launch(parseCommandLine([tempDirPath])) + const [window2] = await atomApplication.launch( + parseCommandLine([tempDirPath]) + ) await window2.loadedPromise - const window2Text = await evalInWebContents(window2.browserWindow.webContents, sendBackToMainProcess => { - const textEditor = atom.workspace.getActiveTextEditor() - textEditor.moveToBottom() - textEditor.insertText(' How are you?') - sendBackToMainProcess(textEditor.getText()) - }) + const window2Text = await evalInWebContents( + window2.browserWindow.webContents, + sendBackToMainProcess => { + const textEditor = atom.workspace.getActiveTextEditor() + textEditor.moveToBottom() + textEditor.insertText(' How are you?') + sendBackToMainProcess(textEditor.getText()) + } + ) assert.equal(window2Text, 'Hello World! How are you?') }) it('adds a remote directory to the project when launched with a remote directory', async () => { - const packagePath = path.join(__dirname, '..', 'fixtures', 'packages', 'package-with-directory-provider') + const packagePath = path.join( + __dirname, + '..', + 'fixtures', + 'packages', + 'package-with-directory-provider' + ) const packagesDirPath = path.join(process.env.ATOM_HOME, 'packages') fs.mkdirSync(packagesDirPath) - fs.symlinkSync(packagePath, path.join(packagesDirPath, 'package-with-directory-provider'), 'junction') + fs.symlinkSync( + packagePath, + path.join(packagesDirPath, 'package-with-directory-provider'), + 'junction' + ) const atomApplication = buildAtomApplication() atomApplication.config.set('core.disabledPackages', ['fuzzy-finder']) const remotePath = 'remote://server:3437/some/directory/path' - let [window] = await atomApplication.launch(parseCommandLine([remotePath])) + let [window] = await atomApplication.launch( + parseCommandLine([remotePath]) + ) await focusWindow(window) - await conditionPromise(async () => (await getProjectDirectories()).length > 0) + await conditionPromise( + async () => (await getProjectDirectories()).length > 0 + ) let directories = await getProjectDirectories() - assert.deepEqual(directories, [{type: 'FakeRemoteDirectory', path: remotePath}]) + assert.deepEqual(directories, [ + { type: 'FakeRemoteDirectory', path: remotePath } + ]) await window.reload() await focusWindow(window) directories = await getProjectDirectories() - assert.deepEqual(directories, [{type: 'FakeRemoteDirectory', path: remotePath}]) + assert.deepEqual(directories, [ + { type: 'FakeRemoteDirectory', path: remotePath } + ]) function getProjectDirectories () { - return evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { - sendBackToMainProcess(atom.project.getDirectories().map(d => ({ type: d.constructor.name, path: d.getPath() }))) - }) + return evalInWebContents( + window.browserWindow.webContents, + sendBackToMainProcess => { + sendBackToMainProcess( + atom.project + .getDirectories() + .map(d => ({ type: d.constructor.name, path: d.getPath() })) + ) + } + ) } }) it('does not reopen any previously opened windows when launched with no path and `core.restorePreviousWindowsOnStart` is no', async () => { const atomApplication1 = buildAtomApplication() - const [app1Window1] = await atomApplication1.launch(parseCommandLine([makeTempDir()])) + const [app1Window1] = await atomApplication1.launch( + parseCommandLine([makeTempDir()]) + ) await focusWindow(app1Window1) - const [app1Window2] = await atomApplication1.launch(parseCommandLine([makeTempDir()])) + const [app1Window2] = await atomApplication1.launch( + parseCommandLine([makeTempDir()]) + ) await focusWindow(app1Window2) const configPath = path.join(process.env.ATOM_HOME, 'config.cson') @@ -382,19 +521,27 @@ describe('AtomApplication', function () { }) it('kills the specified pid after a newly-opened window is closed', async () => { - const [window1] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) + const [window1] = await atomApplication.launch( + parseCommandLine(['--wait', '--pid', '101']) + ) await focusWindow(window1) - const [window2] = await atomApplication.launch(parseCommandLine(['--new-window', '--wait', '--pid', '102'])) + const [window2] = await atomApplication.launch( + parseCommandLine(['--new-window', '--wait', '--pid', '102']) + ) await focusWindow(window2) assert.deepEqual(killedPids, []) - let processKillPromise = new Promise(resolve => { onDidKillProcess = resolve }) + let processKillPromise = new Promise(resolve => { + onDidKillProcess = resolve + }) window1.close() await processKillPromise assert.deepEqual(killedPids, [101]) - processKillPromise = new Promise(resolve => { onDidKillProcess = resolve }) + processKillPromise = new Promise(resolve => { + onDidKillProcess = resolve + }) window2.close() await processKillPromise assert.deepEqual(killedPids, [101, 102]) @@ -407,18 +554,34 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath1, 'File 1') fs.writeFileSync(filePath2, 'File 2') - const [window] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101', projectDir])) + const [window] = await atomApplication.launch( + parseCommandLine(['--wait', '--pid', '101', projectDir]) + ) await focusWindow(window) - const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--add', '--wait', '--pid', '102', filePath1, filePath2])) + const [reusedWindow] = await atomApplication.launch( + parseCommandLine([ + '--add', + '--wait', + '--pid', + '102', + filePath1, + filePath2 + ]) + ) assert.equal(reusedWindow, window) - const activeEditorPath = await evalInWebContents(window.browserWindow.webContents, send => { - const subscription = atom.workspace.onDidChangeActivePaneItem(editor => { - send(editor.getPath()) - subscription.dispose() - }) - }) + const activeEditorPath = await evalInWebContents( + window.browserWindow.webContents, + send => { + const subscription = atom.workspace.onDidChangeActivePaneItem( + editor => { + send(editor.getPath()) + subscription.dispose() + } + ) + } + ) assert([filePath1, filePath2].includes(activeEditorPath)) assert.deepEqual(killedPids, []) @@ -430,7 +593,9 @@ describe('AtomApplication', function () { await timeoutPromise(100) assert.deepEqual(killedPids, []) - let processKillPromise = new Promise(resolve => { onDidKillProcess = resolve }) + let processKillPromise = new Promise(resolve => { + onDidKillProcess = resolve + }) await evalInWebContents(window.browserWindow.webContents, send => { atom.workspace.getActivePaneItem().destroy() send() @@ -438,7 +603,9 @@ describe('AtomApplication', function () { await processKillPromise assert.deepEqual(killedPids, [102]) - processKillPromise = new Promise(resolve => { onDidKillProcess = resolve }) + processKillPromise = new Promise(resolve => { + onDidKillProcess = resolve + }) window.close() await processKillPromise assert.deepEqual(killedPids, [102, 101]) @@ -449,25 +616,40 @@ describe('AtomApplication', function () { await focusWindow(window) const dirPath1 = makeTempDir() - const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--add', '--wait', '--pid', '101', dirPath1])) + const [reusedWindow] = await atomApplication.launch( + parseCommandLine(['--add', '--wait', '--pid', '101', dirPath1]) + ) assert.equal(reusedWindow, window) - await conditionPromise(async () => (await getTreeViewRootDirectories(window)).length === 1) + await conditionPromise( + async () => (await getTreeViewRootDirectories(window)).length === 1 + ) assert.deepEqual(await getTreeViewRootDirectories(window), [dirPath1]) assert.deepEqual(killedPids, []) const dirPath2 = makeTempDir() - await evalInWebContents(window.browserWindow.webContents, (send, dirPath1, dirPath2) => { - atom.project.setPaths([dirPath1, dirPath2]) - send() - }, dirPath1, dirPath2) + await evalInWebContents( + window.browserWindow.webContents, + (send, dirPath1, dirPath2) => { + atom.project.setPaths([dirPath1, dirPath2]) + send() + }, + dirPath1, + dirPath2 + ) await timeoutPromise(100) assert.deepEqual(killedPids, []) - let processKillPromise = new Promise(resolve => { onDidKillProcess = resolve }) - await evalInWebContents(window.browserWindow.webContents, (send, dirPath2) => { - atom.project.setPaths([dirPath2]) - send() - }, dirPath2) + let processKillPromise = new Promise(resolve => { + onDidKillProcess = resolve + }) + await evalInWebContents( + window.browserWindow.webContents, + (send, dirPath2) => { + atom.project.setPaths([dirPath2]) + send() + }, + dirPath2 + ) await processKillPromise assert.deepEqual(killedPids, [101]) }) @@ -477,7 +659,9 @@ describe('AtomApplication', function () { if (process.platform === 'linux' || process.platform === 'win32') { it('quits the application', async () => { const atomApplication = buildAtomApplication() - const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir('a'), 'file-a')])) + const [window] = await atomApplication.launch( + parseCommandLine([path.join(makeTempDir('a'), 'file-a')]) + ) await focusWindow(window) window.close() await window.closedPromise @@ -487,7 +671,9 @@ describe('AtomApplication', function () { } else if (process.platform === 'darwin') { it('leaves the application open', async () => { const atomApplication = buildAtomApplication() - const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir('a'), 'file-a')])) + const [window] = await atomApplication.launch( + parseCommandLine([path.join(makeTempDir('a'), 'file-a')]) + ) await focusWindow(window) window.close() await window.closedPromise @@ -503,16 +689,29 @@ describe('AtomApplication', function () { const dirB = makeTempDir() const atomApplication = buildAtomApplication() - const [window0] = await atomApplication.launch(parseCommandLine([dirA, dirB])) + const [window0] = await atomApplication.launch( + parseCommandLine([dirA, dirB]) + ) await focusWindow(window0) - await conditionPromise(async () => (await getTreeViewRootDirectories(window0)).length === 2) - assert.deepEqual(await getTreeViewRootDirectories(window0), [dirA, dirB]) + await conditionPromise( + async () => (await getTreeViewRootDirectories(window0)).length === 2 + ) + assert.deepEqual(await getTreeViewRootDirectories(window0), [ + dirA, + dirB + ]) - const saveStatePromise = emitterEventPromise(atomApplication, 'application:did-save-state') - await evalInWebContents(window0.browserWindow.webContents, (sendBackToMainProcess) => { - atom.project.removePath(atom.project.getPaths()[0]) - sendBackToMainProcess(null) - }) + const saveStatePromise = emitterEventPromise( + atomApplication, + 'application:did-save-state' + ) + await evalInWebContents( + window0.browserWindow.webContents, + sendBackToMainProcess => { + atom.project.removePath(atom.project.getPaths()[0]) + sendBackToMainProcess(null) + } + ) assert.deepEqual(await getTreeViewRootDirectories(window0), [dirB]) await saveStatePromise @@ -520,17 +719,29 @@ describe('AtomApplication', function () { const atomApplication2 = buildAtomApplication() const [window2] = await atomApplication2.launch(parseCommandLine([])) await focusWindow(window2) - await conditionPromise(async () => (await getTreeViewRootDirectories(window2)).length === 1) + await conditionPromise( + async () => (await getTreeViewRootDirectories(window2)).length === 1 + ) assert.deepEqual(await getTreeViewRootDirectories(window2), [dirB]) }) }) describe('when opening atom:// URLs', () => { it('loads the urlMain file in a new window', async () => { - const packagePath = path.join(__dirname, '..', 'fixtures', 'packages', 'package-with-url-main') + const packagePath = path.join( + __dirname, + '..', + 'fixtures', + 'packages', + 'package-with-url-main' + ) const packagesDirPath = path.join(process.env.ATOM_HOME, 'packages') fs.mkdirSync(packagesDirPath) - fs.symlinkSync(packagePath, path.join(packagesDirPath, 'package-with-url-main'), 'junction') + fs.symlinkSync( + packagePath, + path.join(packagesDirPath, 'package-with-url-main'), + 'junction' + ) const atomApplication = buildAtomApplication() const launchOptions = parseCommandLine([]) @@ -538,9 +749,12 @@ describe('AtomApplication', function () { let [windows] = await atomApplication.launch(launchOptions) await windows[0].loadedPromise - let reached = await evalInWebContents(windows[0].browserWindow.webContents, sendBackToMainProcess => { - sendBackToMainProcess(global.reachedUrlMain) - }) + let reached = await evalInWebContents( + windows[0].browserWindow.webContents, + sendBackToMainProcess => { + sendBackToMainProcess(global.reachedUrlMain) + } + ) assert.isTrue(reached) windows[0].close() }) @@ -550,9 +764,13 @@ describe('AtomApplication', function () { const dirBPath = makeTempDir('b') const atomApplication = buildAtomApplication() - const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath)])) + const [window1] = await atomApplication.launch( + parseCommandLine([path.join(dirAPath)]) + ) await focusWindow(window1) - const [window2] = await atomApplication.launch(parseCommandLine([path.join(dirBPath)])) + const [window2] = await atomApplication.launch( + parseCommandLine([path.join(dirBPath)]) + ) await focusWindow(window2) const fileA = path.join(dirAPath, 'file-a') @@ -564,10 +782,16 @@ describe('AtomApplication', function () { sinon.spy(window2, 'sendURIMessage') atomApplication.launch(parseCommandLine(['--uri-handler', uriA])) - await conditionPromise(() => window1.sendURIMessage.calledWith(uriA), `window1 to be focused from ${fileA}`) + await conditionPromise( + () => window1.sendURIMessage.calledWith(uriA), + `window1 to be focused from ${fileA}` + ) atomApplication.launch(parseCommandLine(['--uri-handler', uriB])) - await conditionPromise(() => window2.sendURIMessage.calledWith(uriB), `window2 to be focused from ${fileB}`) + await conditionPromise( + () => window2.sendURIMessage.calledWith(uriB), + `window2 to be focused from ${fileB}` + ) }) }) }) @@ -584,7 +808,10 @@ describe('AtomApplication', function () { await new Promise(process.nextTick) assert(!electron.app.didQuit()) - await Promise.all([window1.lastPrepareToUnloadPromise, window2.lastPrepareToUnloadPromise]) + await Promise.all([ + window1.lastPrepareToUnloadPromise, + window2.lastPrepareToUnloadPromise + ]) assert(!electron.app.didQuit()) await atomApplication.lastBeforeQuitPromise await new Promise(process.nextTick) @@ -596,20 +823,23 @@ describe('AtomApplication', function () { const [window1] = await atomApplication.launch(parseCommandLine([])) const [window2] = await atomApplication.launch(parseCommandLine([])) await Promise.all([window1.loadedPromise, window2.loadedPromise]) - await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.getActiveTextEditor().insertText('unsaved text') - sendBackToMainProcess() - }) + await evalInWebContents( + window1.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.getActiveTextEditor().insertText('unsaved text') + sendBackToMainProcess() + } + ) // Choosing "Cancel" - mockElectronShowMessageBox({response: 1}) + mockElectronShowMessageBox({ response: 1 }) electron.app.quit() await atomApplication.lastBeforeQuitPromise assert(!electron.app.didQuit()) assert.equal(electron.app.quit.callCount, 1) // Ensure choosing "Cancel" doesn't try to quit the electron app more than once (regression) // Choosing "Don't save" - mockElectronShowMessageBox({response: 2}) + mockElectronShowMessageBox({ response: 2 }) electron.app.quit() await atomApplication.lastBeforeQuitPromise assert(electron.app.didQuit()) @@ -620,29 +850,68 @@ describe('AtomApplication', function () { const [window1] = await atomApplication.launch(parseCommandLine([])) const [window2] = await atomApplication.launch(parseCommandLine([])) await Promise.all([window1.loadedPromise, window2.loadedPromise]) - await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.getActiveTextEditor().insertText('unsaved text') - sendBackToMainProcess() - }) + await evalInWebContents( + window1.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.getActiveTextEditor().insertText('unsaved text') + sendBackToMainProcess() + } + ) // Choosing "Cancel" - mockElectronShowMessageBox({response: 1}) + mockElectronShowMessageBox({ response: 1 }) electron.app.quit() await atomApplication.lastBeforeQuitPromise assert(atomApplication.getAllWindows().length === 1) // Choosing "Don't save" - mockElectronShowMessageBox({response: 2}) + mockElectronShowMessageBox({ response: 2 }) electron.app.quit() await atomApplication.lastBeforeQuitPromise assert(atomApplication.getAllWindows().length === 0) }) + if (process.platform === 'darwin') { + it('allows opening a new folder after all windows are closed', async () => { + const atomApplication = buildAtomApplication() + sinon.stub(atomApplication, 'promptForPathToOpen') + + // Open a window and then close it, leaving the app running + const [window] = await atomApplication.launch(parseCommandLine([])) + await focusWindow(window) + window.close() + await window.closedPromise + + atomApplication.emit('application:open') + await conditionPromise(() => + atomApplication.promptForPathToOpen.calledWith('all') + ) + atomApplication.promptForPathToOpen.reset() + + atomApplication.emit('application:open-file') + await conditionPromise(() => + atomApplication.promptForPathToOpen.calledWith('file') + ) + atomApplication.promptForPathToOpen.reset() + + atomApplication.emit('application:open-folder') + await conditionPromise(() => + atomApplication.promptForPathToOpen.calledWith('folder') + ) + atomApplication.promptForPathToOpen.reset() + }) + } + function buildAtomApplication (params = {}) { - const atomApplication = new AtomApplication(Object.assign({ - resourcePath: ATOM_RESOURCE_PATH, - atomHomeDirPath: process.env.ATOM_HOME - }, params)) + const atomApplication = new AtomApplication( + Object.assign( + { + resourcePath: ATOM_RESOURCE_PATH, + atomHomeDirPath: process.env.ATOM_HOME + }, + params + ) + ) atomApplicationsToDestroy.push(atomApplication) return atomApplication } @@ -650,7 +919,9 @@ describe('AtomApplication', function () { async function focusWindow (window) { window.focus() await window.loadedPromise - await conditionPromise(() => window.atomApplication.getLastFocusedWindow() === window) + await conditionPromise( + () => window.atomApplication.getLastFocusedWindow() === window + ) } function mockElectronAppQuit () { @@ -659,7 +930,11 @@ describe('AtomApplication', function () { electron.app.quit = function () { this.quit.callCount++ let defaultPrevented = false - this.emit('before-quit', {preventDefault () { defaultPrevented = true }}) + this.emit('before-quit', { + preventDefault () { + defaultPrevented = true + } + }) if (!defaultPrevented) didQuit = true } @@ -668,7 +943,7 @@ describe('AtomApplication', function () { electron.app.didQuit = () => didQuit } - function mockElectronShowMessageBox ({response}) { + function mockElectronShowMessageBox ({ response }) { electron.dialog.showMessageBox = (window, options, callback) => { callback(response) } @@ -693,7 +968,9 @@ describe('AtomApplication', function () { function sendBackToMainProcess (result) { require('electron').ipcRenderer.send('${channelId}', result) } - (${source})(sendBackToMainProcess, ${args.map(JSON.stringify).join(', ')}) + (${source})(sendBackToMainProcess, ${args + .map(JSON.stringify) + .join(', ')}) ` // console.log(`about to execute:\n${js}`) @@ -702,19 +979,24 @@ describe('AtomApplication', function () { } function getTreeViewRootDirectories (atomWindow) { - return evalInWebContents(atomWindow.browserWindow.webContents, sendBackToMainProcess => { - atom.workspace.getLeftDock().observeActivePaneItem((treeView) => { - if (treeView) { - sendBackToMainProcess( - Array - .from(treeView.element.querySelectorAll('.project-root > .header .name')) - .map(element => element.dataset.path) - ) - } else { - sendBackToMainProcess([]) - } - }) - }) + return evalInWebContents( + atomWindow.browserWindow.webContents, + sendBackToMainProcess => { + atom.workspace.getLeftDock().observeActivePaneItem(treeView => { + if (treeView) { + sendBackToMainProcess( + Array.from( + treeView.element.querySelectorAll( + '.project-root > .header .name' + ) + ).map(element => element.dataset.path) + ) + } else { + sendBackToMainProcess([]) + } + }) + } + ) } function clearElectronSession () { diff --git a/spec/main-process/file-recovery-service.test.js b/spec/main-process/file-recovery-service.test.js index 45c10c25b..c47ffb944 100644 --- a/spec/main-process/file-recovery-service.test.js +++ b/spec/main-process/file-recovery-service.test.js @@ -1,13 +1,14 @@ -const {dialog} = require('electron') +const { dialog } = require('electron') const FileRecoveryService = require('../../src/main-process/file-recovery-service') const fs = require('fs-plus') const fsreal = require('fs') const EventEmitter = require('events').EventEmitter +const { assert } = require('chai') const sinon = require('sinon') -const {escapeRegExp} = require('underscore-plus') +const { escapeRegExp } = require('underscore-plus') const temp = require('temp').track() -describe("FileRecoveryService", () => { +describe('FileRecoveryService', () => { let recoveryService, recoveryDirectory, spies beforeEach(() => { @@ -25,90 +26,90 @@ describe("FileRecoveryService", () => { } }) - describe("when no crash happens during a save", () => { - it("creates a recovery file and deletes it after saving", async () => { + describe('when no crash happens during a save', () => { + it('creates a recovery file and deletes it after saving', async () => { const mockWindow = {} const filePath = temp.path() - fs.writeFileSync(filePath, "some content") + fs.writeFileSync(filePath, 'some content') await recoveryService.willSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - fs.writeFileSync(filePath, "changed") + fs.writeFileSync(filePath, 'changed') await recoveryService.didSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") + assert.equal(fs.readFileSync(filePath, 'utf8'), 'changed') fs.removeSync(filePath) }) - it("creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it", async () => { + it('creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it', async () => { const mockWindow = {} const anotherMockWindow = {} const filePath = temp.path() - fs.writeFileSync(filePath, "some content") + fs.writeFileSync(filePath, 'some content') await recoveryService.willSavePath(mockWindow, filePath) await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - fs.writeFileSync(filePath, "changed") + fs.writeFileSync(filePath, 'changed') await recoveryService.didSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") + assert.equal(fs.readFileSync(filePath, 'utf8'), 'changed') await recoveryService.didSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") + assert.equal(fs.readFileSync(filePath, 'utf8'), 'changed') fs.removeSync(filePath) }) }) - describe("when a crash happens during a save", () => { - it("restores the created recovery file and deletes it", async () => { + describe('when a crash happens during a save', () => { + it('restores the created recovery file and deletes it', async () => { const mockWindow = {} const filePath = temp.path() - fs.writeFileSync(filePath, "some content") + fs.writeFileSync(filePath, 'some content') await recoveryService.willSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - fs.writeFileSync(filePath, "changed") + fs.writeFileSync(filePath, 'changed') await recoveryService.didCrashWindow(mockWindow) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - assert.equal(fs.readFileSync(filePath, 'utf8'), "some content") + assert.equal(fs.readFileSync(filePath, 'utf8'), 'some content') fs.removeSync(filePath) }) - it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", async () => { + it('restores the created recovery file when many windows attempt to save the same file and one of them crashes', async () => { const mockWindow = {} const anotherMockWindow = {} const filePath = temp.path() - fs.writeFileSync(filePath, "A") + fs.writeFileSync(filePath, 'A') await recoveryService.willSavePath(mockWindow, filePath) - fs.writeFileSync(filePath, "B") + fs.writeFileSync(filePath, 'B') await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - fs.writeFileSync(filePath, "C") + fs.writeFileSync(filePath, 'C') await recoveryService.didCrashWindow(mockWindow) - assert.equal(fs.readFileSync(filePath, 'utf8'), "A") + assert.equal(fs.readFileSync(filePath, 'utf8'), 'A') assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - fs.writeFileSync(filePath, "D") + fs.writeFileSync(filePath, 'D') await recoveryService.willSavePath(mockWindow, filePath) - fs.writeFileSync(filePath, "E") + fs.writeFileSync(filePath, 'E') await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) - fs.writeFileSync(filePath, "F") + fs.writeFileSync(filePath, 'F') await recoveryService.didCrashWindow(anotherMockWindow) - assert.equal(fs.readFileSync(filePath, 'utf8'), "D") + assert.equal(fs.readFileSync(filePath, 'utf8'), 'D') assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) fs.removeSync(filePath) @@ -117,10 +118,10 @@ describe("FileRecoveryService", () => { it("emits a warning when a file can't be recovered", async () => { const mockWindow = {} const filePath = temp.path() - fs.writeFileSync(filePath, "content") + fs.writeFileSync(filePath, 'content') let logs = [] - spies.stub(console, 'log', (message) => logs.push(message)) + spies.stub(console, 'log', message => logs.push(message)) spies.stub(dialog, 'showMessageBox') // Copy files to be recovered before mocking fs.createWriteStream @@ -130,9 +131,15 @@ describe("FileRecoveryService", () => { // attempting to copy the recovered file to its original location var fakeEmitter = new EventEmitter() var onStub = spies.stub(fakeEmitter, 'on') - onStub.withArgs('error').yields(new Error('Nope')).returns(fakeEmitter) + onStub + .withArgs('error') + .yields(new Error('Nope')) + .returns(fakeEmitter) onStub.withArgs('open').returns(fakeEmitter) - spies.stub(fsreal, 'createWriteStream').withArgs(filePath).returns(fakeEmitter) + spies + .stub(fsreal, 'createWriteStream') + .withArgs(filePath) + .returns(fakeEmitter) await recoveryService.didCrashWindow(mockWindow) let recoveryFiles = fs.listTreeSync(recoveryDirectory) @@ -148,10 +155,10 @@ describe("FileRecoveryService", () => { it("doesn't create a recovery file when the file that's being saved doesn't exist yet", async () => { const mockWindow = {} - await recoveryService.willSavePath(mockWindow, "a-file-that-doesnt-exist") + await recoveryService.willSavePath(mockWindow, 'a-file-that-doesnt-exist') assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - await recoveryService.didSavePath(mockWindow, "a-file-that-doesnt-exist") + await recoveryService.didSavePath(mockWindow, 'a-file-that-doesnt-exist') assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) }) }) diff --git a/spec/main-process/mocha-test-runner.js b/spec/main-process/mocha-test-runner.js index 61d533417..ef8ef69ef 100644 --- a/spec/main-process/mocha-test-runner.js +++ b/spec/main-process/mocha-test-runner.js @@ -1,9 +1,8 @@ const Mocha = require('mocha') const fs = require('fs-plus') -const {assert} = require('chai') +const { assert } = require('chai') -module.exports = -function (testPaths) { +module.exports = function (testPaths) { global.assert = assert let reporterOptions = { diff --git a/spec/main-process/parse-command-line.test.js b/spec/main-process/parse-command-line.test.js index bb5d625a9..6b5ac3ba5 100644 --- a/spec/main-process/parse-command-line.test.js +++ b/spec/main-process/parse-command-line.test.js @@ -1,9 +1,17 @@ +const { assert } = require('chai') const parseCommandLine = require('../../src/main-process/parse-command-line') describe('parseCommandLine', () => { describe('when --uri-handler is not passed', () => { it('parses arguments as normal', () => { - const args = parseCommandLine(['-d', '--safe', '--test', '/some/path', 'atom://test/url', 'atom://other/url']) + const args = parseCommandLine([ + '-d', + '--safe', + '--test', + '/some/path', + 'atom://test/url', + 'atom://other/url' + ]) assert.isTrue(args.devMode) assert.isTrue(args.safeMode) assert.isTrue(args.test) @@ -14,7 +22,15 @@ describe('parseCommandLine', () => { describe('when --uri-handler is passed', () => { it('ignores other arguments and limits to one URL', () => { - const args = parseCommandLine(['-d', '--uri-handler', '--safe', '--test', '/some/path', 'atom://test/url', 'atom://other/url']) + const args = parseCommandLine([ + '-d', + '--uri-handler', + '--safe', + '--test', + '/some/path', + 'atom://test/url', + 'atom://other/url' + ]) assert.isUndefined(args.devMode) assert.isUndefined(args.safeMode) assert.isUndefined(args.test) diff --git a/spec/menu-sort-helpers-spec.js b/spec/menu-sort-helpers-spec.js index 86f00b37e..0e63824ff 100644 --- a/spec/menu-sort-helpers-spec.js +++ b/spec/menu-sort-helpers-spec.js @@ -1,4 +1,4 @@ -const {sortMenuItems} = require('../src/menu-sort-helpers') +const { sortMenuItems } = require('../src/menu-sort-helpers') describe('contextMenu', () => { describe('dedupes separators', () => { @@ -18,7 +18,8 @@ describe('contextMenu', () => { it('preserves separators at the begining of set two', () => { const items = [ { command: 'core:one' }, - { type: 'separator' }, { command: 'core:two' } + { type: 'separator' }, + { command: 'core:two' } ] const expected = [ { command: 'core:one' }, @@ -36,8 +37,10 @@ describe('contextMenu', () => { it('removes duplicate separators across sets', () => { const items = [ - { command: 'core:one' }, { type: 'separator' }, - { type: 'separator' }, { command: 'core:two' } + { command: 'core:one' }, + { type: 'separator' }, + { type: 'separator' }, + { command: 'core:two' } ] const expected = [ { command: 'core:one' }, diff --git a/spec/native-watcher-registry-spec.js b/spec/native-watcher-registry-spec.js index bc657f496..89f08d79e 100644 --- a/spec/native-watcher-registry-spec.js +++ b/spec/native-watcher-registry-spec.js @@ -1,11 +1,9 @@ /** @babel */ -import {it, beforeEach} from './async-spec-helpers' - import path from 'path' -import {Emitter} from 'event-kit' +import { Emitter } from 'event-kit' -import {NativeWatcherRegistry} from '../src/native-watcher-registry' +import { NativeWatcherRegistry } from '../src/native-watcher-registry' function findRootDirectory () { let current = process.cwd() @@ -42,7 +40,9 @@ class MockWatcher { attachToNative (native, nativePath) { if (this.normalizedPath.startsWith(nativePath)) { if (this.native) { - this.native.attached = this.native.attached.filter(each => each !== this) + this.native.attached = this.native.attached.filter( + each => each !== this + ) } this.native = native this.native.attached.push(this) @@ -84,7 +84,9 @@ describe('NativeWatcherRegistry', function () { let createNative, registry beforeEach(function () { - registry = new NativeWatcherRegistry(normalizedPath => createNative(normalizedPath)) + registry = new NativeWatcherRegistry(normalizedPath => + createNative(normalizedPath) + ) }) it('attaches a Watcher to a newly created NativeWatcher for a new directory', async function () { @@ -201,9 +203,20 @@ describe('NativeWatcherRegistry', function () { const RUNNING = new MockNative('running') const stoppedPath = absolute('watcher', 'that', 'will', 'be', 'stopped') - const stoppedPathParts = stoppedPath.split(path.sep).filter(part => part.length > 0) - const runningPath = absolute('watcher', 'that', 'will', 'continue', 'to', 'exist') - const runningPathParts = runningPath.split(path.sep).filter(part => part.length > 0) + const stoppedPathParts = stoppedPath + .split(path.sep) + .filter(part => part.length > 0) + const runningPath = absolute( + 'watcher', + 'that', + 'will', + 'continue', + 'to', + 'exist' + ) + const runningPathParts = runningPath + .split(path.sep) + .filter(part => part.length > 0) createNative = dir => { if (dir === stoppedPath) { @@ -281,23 +294,29 @@ describe('NativeWatcherRegistry', function () { expect(childWatcher0.native).toBe(CHILD0) expect(childWatcher1.native).toBe(CHILD1) - expect(registry.tree.root.lookup(parts(parentDir)).when({ - parent: () => false, - missing: () => false, - children: () => true - })).toBe(true) + expect( + registry.tree.root.lookup(parts(parentDir)).when({ + parent: () => false, + missing: () => false, + children: () => true + }) + ).toBe(true) - expect(registry.tree.root.lookup(parts(childDir0)).when({ - parent: () => true, - missing: () => false, - children: () => false - })).toBe(true) + expect( + registry.tree.root.lookup(parts(childDir0)).when({ + parent: () => true, + missing: () => false, + children: () => false + }) + ).toBe(true) - expect(registry.tree.root.lookup(parts(childDir1)).when({ - parent: () => true, - missing: () => false, - children: () => false - })).toBe(true) + expect( + registry.tree.root.lookup(parts(childDir1)).when({ + parent: () => true, + missing: () => false, + children: () => false + }) + ).toBe(true) }) it('consolidates children when splitting a parent watcher', async function () { @@ -340,23 +359,29 @@ describe('NativeWatcherRegistry', function () { expect(childWatcher0.native).toBe(CHILD0) expect(childWatcher1.native).toBe(CHILD0) - expect(registry.tree.root.lookup(parts(parentDir)).when({ - parent: () => false, - missing: () => false, - children: () => true - })).toBe(true) + expect( + registry.tree.root.lookup(parts(parentDir)).when({ + parent: () => false, + missing: () => false, + children: () => true + }) + ).toBe(true) - expect(registry.tree.root.lookup(parts(childDir0)).when({ - parent: () => true, - missing: () => false, - children: () => false - })).toBe(true) + expect( + registry.tree.root.lookup(parts(childDir0)).when({ + parent: () => true, + missing: () => false, + children: () => false + }) + ).toBe(true) - expect(registry.tree.root.lookup(parts(childDir1)).when({ - parent: () => true, - missing: () => false, - children: () => false - })).toBe(true) + expect( + registry.tree.root.lookup(parts(childDir1)).when({ + parent: () => true, + missing: () => false, + children: () => false + }) + ).toBe(true) }) }) }) diff --git a/spec/notification-manager-spec.js b/spec/notification-manager-spec.js index 3a8544d4e..3fd91d0ca 100644 --- a/spec/notification-manager-spec.js +++ b/spec/notification-manager-spec.js @@ -10,8 +10,7 @@ describe('NotificationManager', () => { describe('the atom global', () => it('has a notifications instance', () => { expect(atom.notifications instanceof NotificationManager).toBe(true) - }) - ) + })) describe('adding events', () => { let addSpy @@ -22,7 +21,7 @@ describe('NotificationManager', () => { }) it('emits an event when a notification has been added', () => { - manager.add('error', 'Some error!', {icon: 'someIcon'}) + manager.add('error', 'Some error!', { icon: 'someIcon' }) expect(addSpy).toHaveBeenCalled() const notification = addSpy.mostRecentCall.args[0] @@ -32,35 +31,35 @@ describe('NotificationManager', () => { }) it('emits a fatal error when ::addFatalError has been called', () => { - manager.addFatalError('Some error!', {icon: 'someIcon'}) + manager.addFatalError('Some error!', { icon: 'someIcon' }) expect(addSpy).toHaveBeenCalled() const notification = addSpy.mostRecentCall.args[0] expect(notification.getType()).toBe('fatal') }) it('emits an error when ::addError has been called', () => { - manager.addError('Some error!', {icon: 'someIcon'}) + manager.addError('Some error!', { icon: 'someIcon' }) expect(addSpy).toHaveBeenCalled() const notification = addSpy.mostRecentCall.args[0] expect(notification.getType()).toBe('error') }) it('emits a warning notification when ::addWarning has been called', () => { - manager.addWarning('Something!', {icon: 'someIcon'}) + manager.addWarning('Something!', { icon: 'someIcon' }) expect(addSpy).toHaveBeenCalled() const notification = addSpy.mostRecentCall.args[0] expect(notification.getType()).toBe('warning') }) it('emits an info notification when ::addInfo has been called', () => { - manager.addInfo('Something!', {icon: 'someIcon'}) + manager.addInfo('Something!', { icon: 'someIcon' }) expect(addSpy).toHaveBeenCalled() const notification = addSpy.mostRecentCall.args[0] expect(notification.getType()).toBe('info') }) it('emits a success notification when ::addSuccess has been called', () => { - manager.addSuccess('Something!', {icon: 'someIcon'}) + manager.addSuccess('Something!', { icon: 'someIcon' }) expect(addSpy).toHaveBeenCalled() const notification = addSpy.mostRecentCall.args[0] expect(notification.getType()).toBe('success') diff --git a/spec/notification-spec.js b/spec/notification-spec.js index 4702cd13d..179f70950 100644 --- a/spec/notification-spec.js +++ b/spec/notification-spec.js @@ -20,8 +20,7 @@ describe('Notification', () => { it('returns a Date object', () => { const notification = new Notification('error', 'message!') expect(notification.getTimestamp() instanceof Date).toBe(true) - }) - ) + })) describe('::getIcon()', () => { it('returns a default when no icon specified', () => { @@ -30,7 +29,9 @@ describe('Notification', () => { }) it('returns the icon specified', () => { - const notification = new Notification('error', 'message!', {icon: 'my-icon'}) + const notification = new Notification('error', 'message!', { + icon: 'my-icon' + }) expect(notification.getIcon()).toBe('my-icon') }) }) @@ -39,7 +40,9 @@ describe('Notification', () => { describe('when the notfication is dismissable', () => it('calls a callback when the notification is dismissed', () => { const dismissedSpy = jasmine.createSpy() - const notification = new Notification('error', 'message', {dismissable: true}) + const notification = new Notification('error', 'message', { + dismissable: true + }) notification.onDidDismiss(dismissedSpy) expect(notification.isDismissable()).toBe(true) @@ -49,8 +52,7 @@ describe('Notification', () => { expect(dismissedSpy).toHaveBeenCalled() expect(notification.isDismissed()).toBe(true) - }) - ) + })) describe('when the notfication is not dismissable', () => it('does nothing when ::dismiss() is called', () => { @@ -65,7 +67,6 @@ describe('Notification', () => { expect(dismissedSpy).not.toHaveBeenCalled() expect(notification.isDismissed()).toBe(true) - }) - ) + })) }) }) diff --git a/spec/package-manager-spec.js b/spec/package-manager-spec.js index dd87f85fa..599fb0ff9 100644 --- a/spec/package-manager-spec.js +++ b/spec/package-manager-spec.js @@ -4,11 +4,10 @@ const Package = require('../src/package') const PackageManager = require('../src/package-manager') const temp = require('temp').track() const fs = require('fs-plus') -const {Disposable} = require('atom') -const {buildKeydownEvent} = require('../src/keymap-extensions') -const {mockLocalStorage} = require('./spec-helper') +const { Disposable } = require('atom') +const { buildKeydownEvent } = require('../src/keymap-extensions') +const { mockLocalStorage } = require('./spec-helper') const ModuleCache = require('../src/module-cache') -const {it, fit, ffit, beforeEach, afterEach} = require('./async-spec-helpers') describe('PackageManager', () => { function createTestElement (className) { @@ -25,20 +24,28 @@ describe('PackageManager', () => { it('adds regular package path', () => { const packageManger = new PackageManager({}) const configDirPath = path.join('~', 'someConfig') - packageManger.initialize({configDirPath}) + packageManger.initialize({ configDirPath }) expect(packageManger.packageDirPaths.length).toBe(1) - expect(packageManger.packageDirPaths[0]).toBe(path.join(configDirPath, 'packages')) + expect(packageManger.packageDirPaths[0]).toBe( + path.join(configDirPath, 'packages') + ) }) it('adds regular package path, dev package path, and Atom repo package path in dev mode and dev resource path is set', () => { const packageManger = new PackageManager({}) const configDirPath = path.join('~', 'someConfig') const resourcePath = path.join('~', '/atom') - packageManger.initialize({configDirPath, resourcePath, devMode: true}) + packageManger.initialize({ configDirPath, resourcePath, devMode: true }) expect(packageManger.packageDirPaths.length).toBe(3) - expect(packageManger.packageDirPaths).toContain(path.join(configDirPath, 'packages')) - expect(packageManger.packageDirPaths).toContain(path.join(configDirPath, 'dev', 'packages')) - expect(packageManger.packageDirPaths).toContain(path.join(resourcePath, 'packages')) + expect(packageManger.packageDirPaths).toContain( + path.join(configDirPath, 'packages') + ) + expect(packageManger.packageDirPaths).toContain( + path.join(configDirPath, 'dev', 'packages') + ) + expect(packageManger.packageDirPaths).toContain( + path.join(resourcePath, 'packages') + ) }) }) @@ -102,43 +109,62 @@ describe('PackageManager', () => { atom.notifications.onDidAddNotification(addErrorHandler) expect(() => pack.reloadStylesheets()).not.toThrow() expect(addErrorHandler.callCount).toBe(2) - expect(addErrorHandler.argsForCall[1][0].message).toContain('Failed to reload the package-with-invalid-styles package stylesheets') - expect(addErrorHandler.argsForCall[1][0].options.packageName).toEqual('package-with-invalid-styles') + expect(addErrorHandler.argsForCall[1][0].message).toContain( + 'Failed to reload the package-with-invalid-styles package stylesheets' + ) + expect(addErrorHandler.argsForCall[1][0].options.packageName).toEqual( + 'package-with-invalid-styles' + ) }) it('returns null if the package has an invalid package.json', () => { spyOn(atom, 'inSpecMode').andReturn(false) const addErrorHandler = jasmine.createSpy() atom.notifications.onDidAddNotification(addErrorHandler) - expect(atom.packages.loadPackage('package-with-broken-package-json')).toBeNull() + expect( + atom.packages.loadPackage('package-with-broken-package-json') + ).toBeNull() expect(addErrorHandler.callCount).toBe(1) - expect(addErrorHandler.argsForCall[0][0].message).toContain('Failed to load the package-with-broken-package-json package') - expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual('package-with-broken-package-json') + expect(addErrorHandler.argsForCall[0][0].message).toContain( + 'Failed to load the package-with-broken-package-json package' + ) + expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual( + 'package-with-broken-package-json' + ) }) it('returns null if the package name or path starts with a dot', () => { - expect(atom.packages.loadPackage('/Users/user/.atom/packages/.git')).toBeNull() + expect( + atom.packages.loadPackage('/Users/user/.atom/packages/.git') + ).toBeNull() }) it('normalizes short repository urls in package.json', () => { - let {metadata} = atom.packages.loadPackage('package-with-short-url-package-json') + let { metadata } = atom.packages.loadPackage( + 'package-with-short-url-package-json' + ) expect(metadata.repository.type).toBe('git') - expect(metadata.repository.url).toBe('https://github.com/example/repo'); - - ({metadata} = atom.packages.loadPackage('package-with-invalid-url-package-json')) + expect(metadata.repository.url).toBe('https://github.com/example/repo') + ;({ metadata } = atom.packages.loadPackage( + 'package-with-invalid-url-package-json' + )) expect(metadata.repository.type).toBe('git') expect(metadata.repository.url).toBe('foo') }) it('trims git+ from the beginning and .git from the end of repository URLs, even if npm already normalized them ', () => { - const {metadata} = atom.packages.loadPackage('package-with-prefixed-and-suffixed-repo-url') + const { metadata } = atom.packages.loadPackage( + 'package-with-prefixed-and-suffixed-repo-url' + ) expect(metadata.repository.type).toBe('git') expect(metadata.repository.url).toBe('https://github.com/example/repo') }) it('returns null if the package is not found in any package directory', () => { spyOn(console, 'warn') - expect(atom.packages.loadPackage('this-package-cannot-be-found')).toBeNull() + expect( + atom.packages.loadPackage('this-package-cannot-be-found') + ).toBeNull() expect(console.warn.callCount).toBe(1) expect(console.warn.argsForCall[0][0]).toContain('Could not resolve') }) @@ -146,11 +172,23 @@ describe('PackageManager', () => { describe('when the package is deprecated', () => { it('returns null', () => { spyOn(console, 'warn') - expect(atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'packages', 'wordcount'))).toBeNull() - expect(atom.packages.isDeprecatedPackage('wordcount', '2.1.9')).toBe(true) - expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.0')).toBe(true) - expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.1')).toBe(false) - expect(atom.packages.getDeprecatedPackageMetadata('wordcount').version).toBe('<=2.2.0') + expect( + atom.packages.loadPackage( + path.join(__dirname, 'fixtures', 'packages', 'wordcount') + ) + ).toBeNull() + expect(atom.packages.isDeprecatedPackage('wordcount', '2.1.9')).toBe( + true + ) + expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.0')).toBe( + true + ) + expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.1')).toBe( + false + ) + expect( + atom.packages.getDeprecatedPackageMetadata('wordcount').version + ).toBe('<=2.2.0') }) }) @@ -169,13 +207,13 @@ describe('PackageManager', () => { it("registers any deserializers specified in the package's package.json", () => { atom.packages.loadPackage('package-with-deserializers') - const state1 = {deserializer: 'Deserializer1', a: 'b'} + const state1 = { deserializer: 'Deserializer1', a: 'b' } expect(atom.deserializers.deserialize(state1)).toEqual({ wasDeserializedBy: 'deserializeMethod1', state: state1 }) - const state2 = {deserializer: 'Deserializer2', c: 'd'} + const state2 = { deserializer: 'Deserializer2', c: 'd' } expect(atom.deserializers.deserialize(state2)).toEqual({ wasDeserializedBy: 'deserializeMethod2', state: state2 @@ -186,15 +224,21 @@ describe('PackageManager', () => { jasmine.useRealClock() const providers = [] - atom.packages.serviceHub.consume('atom.directory-provider', '^0.1.0', provider => providers.push(provider)) + atom.packages.serviceHub.consume( + 'atom.directory-provider', + '^0.1.0', + provider => providers.push(provider) + ) atom.packages.loadPackage('package-with-directory-provider') - expect(providers.map(p => p.name)).toEqual(['directory provider from package-with-directory-provider']) + expect(providers.map(p => p.name)).toEqual([ + 'directory provider from package-with-directory-provider' + ]) }) describe("when there are view providers specified in the package's package.json", () => { - const model1 = {worksWithViewProvider1: true} - const model2 = {worksWithViewProvider2: true} + const model1 = { worksWithViewProvider1: true } + const model2 = { worksWithViewProvider2: true } afterEach(async () => { await atom.packages.deactivatePackage('package-with-view-providers') @@ -254,8 +298,8 @@ describe('PackageManager', () => { expect(atom.config.getSchema('package-with-json-config-schema')).toEqual({ type: 'object', properties: { - a: {type: 'number', default: 5}, - b: {type: 'string', default: 'five'} + a: { type: 'number', default: 5 }, + b: { type: 'string', default: 'five' } } }) @@ -268,8 +312,8 @@ describe('PackageManager', () => { expect(atom.config.getSchema('package-with-json-config-schema')).toEqual({ type: 'object', properties: { - a: {type: 'number', default: 5}, - b: {type: 'string', default: 'five'} + a: { type: 'number', default: 5 }, + b: { type: 'string', default: 'five' } } }) }) @@ -288,12 +332,16 @@ describe('PackageManager', () => { }) it("does not defer loading the package's main module if the package previously used Atom APIs when its main module was required", () => { - const pack1 = atom.packages.loadPackage('package-with-eval-time-api-calls') + const pack1 = atom.packages.loadPackage( + 'package-with-eval-time-api-calls' + ) expect(pack1.mainModule).toBeDefined() atom.packages.unloadPackage('package-with-eval-time-api-calls') - const pack2 = atom.packages.loadPackage('package-with-eval-time-api-calls') + const pack2 = atom.packages.loadPackage( + 'package-with-eval-time-api-calls' + ) expect(pack2.mainModule).not.toBeNull() }) }) @@ -302,35 +350,49 @@ describe('PackageManager', () => { describe('::loadAvailablePackage(availablePackage)', () => { describe('if the package was preloaded', () => { it('adds the package path to the module cache', () => { - const availablePackage = atom.packages.getAvailablePackages().find(p => p.name === 'spell-check') + const availablePackage = atom.packages + .getAvailablePackages() + .find(p => p.name === 'spell-check') availablePackage.isBundled = true - expect(atom.packages.preloadedPackages[availablePackage.name]).toBeUndefined() + expect( + atom.packages.preloadedPackages[availablePackage.name] + ).toBeUndefined() expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false) const metadata = atom.packages.loadPackageMetadata(availablePackage) - atom.packages.preloadPackage( - availablePackage.name, - { - rootDirPath: path.relative(atom.packages.resourcePath, availablePackage.path), - metadata - } - ) + atom.packages.preloadPackage(availablePackage.name, { + rootDirPath: path.relative( + atom.packages.resourcePath, + availablePackage.path + ), + metadata + }) atom.packages.loadAvailablePackage(availablePackage) expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(true) - expect(ModuleCache.add).toHaveBeenCalledWith(availablePackage.path, metadata) + expect(ModuleCache.add).toHaveBeenCalledWith( + availablePackage.path, + metadata + ) }) it('deactivates it if it had been disabled', () => { - const availablePackage = atom.packages.getAvailablePackages().find(p => p.name === 'spell-check') + const availablePackage = atom.packages + .getAvailablePackages() + .find(p => p.name === 'spell-check') availablePackage.isBundled = true - expect(atom.packages.preloadedPackages[availablePackage.name]).toBeUndefined() + expect( + atom.packages.preloadedPackages[availablePackage.name] + ).toBeUndefined() expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false) const metadata = atom.packages.loadPackageMetadata(availablePackage) const preloadedPackage = atom.packages.preloadPackage( availablePackage.name, { - rootDirPath: path.relative(atom.packages.resourcePath, availablePackage.path), + rootDirPath: path.relative( + atom.packages.resourcePath, + availablePackage.path + ), metadata } ) @@ -338,7 +400,10 @@ describe('PackageManager', () => { expect(preloadedPackage.settingsActivated).toBe(true) expect(preloadedPackage.menusActivated).toBe(true) - atom.packages.loadAvailablePackage(availablePackage, new Set([availablePackage.name])) + atom.packages.loadAvailablePackage( + availablePackage, + new Set([availablePackage.name]) + ) expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false) expect(preloadedPackage.keymapActivated).toBe(false) expect(preloadedPackage.settingsActivated).toBe(false) @@ -346,16 +411,23 @@ describe('PackageManager', () => { }) it('deactivates it and reloads the new one if trying to load the same package outside of the bundle', () => { - const availablePackage = atom.packages.getAvailablePackages().find(p => p.name === 'spell-check') + const availablePackage = atom.packages + .getAvailablePackages() + .find(p => p.name === 'spell-check') availablePackage.isBundled = true - expect(atom.packages.preloadedPackages[availablePackage.name]).toBeUndefined() + expect( + atom.packages.preloadedPackages[availablePackage.name] + ).toBeUndefined() expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false) const metadata = atom.packages.loadPackageMetadata(availablePackage) const preloadedPackage = atom.packages.preloadPackage( availablePackage.name, { - rootDirPath: path.relative(atom.packages.resourcePath, availablePackage.path), + rootDirPath: path.relative( + atom.packages.resourcePath, + availablePackage.path + ), metadata } ) @@ -374,21 +446,30 @@ describe('PackageManager', () => { describe('if the package was not preloaded', () => { it('adds the package path to the module cache', () => { - const availablePackage = atom.packages.getAvailablePackages().find(p => p.name === 'spell-check') + const availablePackage = atom.packages + .getAvailablePackages() + .find(p => p.name === 'spell-check') availablePackage.isBundled = true const metadata = atom.packages.loadPackageMetadata(availablePackage) atom.packages.loadAvailablePackage(availablePackage) - expect(ModuleCache.add).toHaveBeenCalledWith(availablePackage.path, metadata) + expect(ModuleCache.add).toHaveBeenCalledWith( + availablePackage.path, + metadata + ) }) }) }) describe('preloading', () => { it('requires the main module, loads the config schema and activates keymaps, menus and settings without reactivating them during package activation', () => { - const availablePackage = atom.packages.getAvailablePackages().find(p => p.name === 'spell-check') + const availablePackage = atom.packages + .getAvailablePackages() + .find(p => p.name === 'spell-check') availablePackage.isBundled = true const metadata = atom.packages.loadPackageMetadata(availablePackage) - expect(atom.packages.preloadedPackages[availablePackage.name]).toBeUndefined() + expect( + atom.packages.preloadedPackages[availablePackage.name] + ).toBeUndefined() expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false) atom.packages.packagesCache = {} @@ -399,7 +480,10 @@ describe('PackageManager', () => { const preloadedPackage = atom.packages.preloadPackage( availablePackage.name, { - rootDirPath: path.relative(atom.packages.resourcePath, availablePackage.path), + rootDirPath: path.relative( + atom.packages.resourcePath, + availablePackage.path + ), metadata } ) @@ -415,7 +499,9 @@ describe('PackageManager', () => { spyOn(atom.config, 'setSchema') atom.packages.loadAvailablePackage(availablePackage) - expect(preloadedPackage.getMainModulePath()).toBe(path.join(availablePackage.path, metadata.main)) + expect(preloadedPackage.getMainModulePath()).toBe( + path.join(availablePackage.path, metadata.main) + ) atom.packages.activatePackage(availablePackage.name) expect(atom.keymaps.add).not.toHaveBeenCalled() @@ -430,10 +516,14 @@ describe('PackageManager', () => { }) it('deactivates disabled keymaps during package activation', () => { - const availablePackage = atom.packages.getAvailablePackages().find(p => p.name === 'spell-check') + const availablePackage = atom.packages + .getAvailablePackages() + .find(p => p.name === 'spell-check') availablePackage.isBundled = true const metadata = atom.packages.loadPackageMetadata(availablePackage) - expect(atom.packages.preloadedPackages[availablePackage.name]).toBeUndefined() + expect( + atom.packages.preloadedPackages[availablePackage.name] + ).toBeUndefined() expect(atom.packages.isPackageLoaded(availablePackage.name)).toBe(false) atom.packages.packagesCache = {} @@ -444,7 +534,10 @@ describe('PackageManager', () => { const preloadedPackage = atom.packages.preloadPackage( availablePackage.name, { - rootDirPath: path.relative(atom.packages.resourcePath, availablePackage.path), + rootDirPath: path.relative( + atom.packages.resourcePath, + availablePackage.path + ), metadata } ) @@ -453,7 +546,9 @@ describe('PackageManager', () => { expect(preloadedPackage.menusActivated).toBe(true) atom.packages.loadAvailablePackage(availablePackage) - atom.config.set('core.packagesWithKeymapsDisabled', [availablePackage.name]) + atom.config.set('core.packagesWithKeymapsDisabled', [ + availablePackage.name + ]) atom.packages.activatePackage(availablePackage.name) expect(preloadedPackage.keymapActivated).toBe(false) @@ -539,14 +634,26 @@ describe('PackageManager', () => { }) it('assigns config schema, including defaults when package contains a schema', async () => { - expect(atom.config.get('package-with-config-schema.numbers.one')).toBeUndefined() + expect( + atom.config.get('package-with-config-schema.numbers.one') + ).toBeUndefined() await atom.packages.activatePackage('package-with-config-schema') - expect(atom.config.get('package-with-config-schema.numbers.one')).toBe(1) - expect(atom.config.get('package-with-config-schema.numbers.two')).toBe(2) - expect(atom.config.set('package-with-config-schema.numbers.one', 'nope')).toBe(false) - expect(atom.config.set('package-with-config-schema.numbers.one', '10')).toBe(true) - expect(atom.config.get('package-with-config-schema.numbers.one')).toBe(10) + expect(atom.config.get('package-with-config-schema.numbers.one')).toBe( + 1 + ) + expect(atom.config.get('package-with-config-schema.numbers.two')).toBe( + 2 + ) + expect( + atom.config.set('package-with-config-schema.numbers.one', 'nope') + ).toBe(false) + expect( + atom.config.set('package-with-config-schema.numbers.one', '10') + ).toBe(true) + expect(atom.config.get('package-with-config-schema.numbers.one')).toBe( + 10 + ) }) describe('when the package metadata includes `activationCommands`', () => { @@ -559,10 +666,18 @@ describe('PackageManager', () => { spyOn(mainModule, 'activate').andCallThrough() spyOn(Package.prototype, 'requireMainModule').andCallThrough() - workspaceCommandListener = jasmine.createSpy('workspaceCommandListener') - registration = atom.commands.add('.workspace', 'activation-command', workspaceCommandListener) + workspaceCommandListener = jasmine.createSpy( + 'workspaceCommandListener' + ) + registration = atom.commands.add( + '.workspace', + 'activation-command', + workspaceCommandListener + ) - promise = atom.packages.activatePackage('package-with-activation-commands') + promise = atom.packages.activatePackage( + 'package-with-activation-commands' + ) }) afterEach(() => { @@ -575,7 +690,11 @@ describe('PackageManager', () => { it('defers requiring/activating the main module until an activation event bubbles to the root view', async () => { expect(Package.prototype.requireMainModule.callCount).toBe(0) - atom.workspace.getElement().dispatchEvent(new CustomEvent('activation-command', {bubbles: true})) + atom.workspace + .getElement() + .dispatchEvent( + new CustomEvent('activation-command', { bubbles: true }) + ) await promise expect(Package.prototype.requireMainModule.callCount).toBe(1) @@ -584,9 +703,17 @@ describe('PackageManager', () => { it('triggers the activation event on all handlers registered during activation', async () => { await atom.workspace.open() - const editorElement = atom.workspace.getActiveTextEditor().getElement() - const editorCommandListener = jasmine.createSpy('editorCommandListener') - atom.commands.add('atom-text-editor', 'activation-command', editorCommandListener) + const editorElement = atom.workspace + .getActiveTextEditor() + .getElement() + const editorCommandListener = jasmine.createSpy( + 'editorCommandListener' + ) + atom.commands.add( + 'atom-text-editor', + 'activation-command', + editorCommandListener + ) atom.commands.dispatch(editorElement, 'activation-command') expect(mainModule.activate.callCount).toBe(1) @@ -605,7 +732,9 @@ describe('PackageManager', () => { mainModule = require('./fixtures/packages/package-with-empty-activation-commands/index') spyOn(mainModule, 'activate').andCallThrough() - atom.packages.activatePackage('package-with-empty-activation-commands') + atom.packages.activatePackage( + 'package-with-empty-activation-commands' + ) expect(mainModule.activate.callCount).toBe(1) }) @@ -614,54 +743,80 @@ describe('PackageManager', () => { spyOn(atom, 'inSpecMode').andReturn(false) const addErrorHandler = jasmine.createSpy() atom.notifications.onDidAddNotification(addErrorHandler) - expect(() => atom.packages.activatePackage('package-with-invalid-activation-commands')).not.toThrow() + expect(() => + atom.packages.activatePackage( + 'package-with-invalid-activation-commands' + ) + ).not.toThrow() expect(addErrorHandler.callCount).toBe(1) - expect(addErrorHandler.argsForCall[0][0].message).toContain('Failed to activate the package-with-invalid-activation-commands package') - expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual('package-with-invalid-activation-commands') + expect(addErrorHandler.argsForCall[0][0].message).toContain( + 'Failed to activate the package-with-invalid-activation-commands package' + ) + expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual( + 'package-with-invalid-activation-commands' + ) }) it('adds a notification when the context menu is invalid', () => { spyOn(atom, 'inSpecMode').andReturn(false) const addErrorHandler = jasmine.createSpy() atom.notifications.onDidAddNotification(addErrorHandler) - expect(() => atom.packages.activatePackage('package-with-invalid-context-menu')).not.toThrow() + expect(() => + atom.packages.activatePackage('package-with-invalid-context-menu') + ).not.toThrow() expect(addErrorHandler.callCount).toBe(1) - expect(addErrorHandler.argsForCall[0][0].message).toContain('Failed to activate the package-with-invalid-context-menu package') - expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual('package-with-invalid-context-menu') + expect(addErrorHandler.argsForCall[0][0].message).toContain( + 'Failed to activate the package-with-invalid-context-menu package' + ) + expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual( + 'package-with-invalid-context-menu' + ) }) it('adds a notification when the grammar is invalid', async () => { let notificationEvent await new Promise(resolve => { - const subscription = atom.notifications.onDidAddNotification(event => { - notificationEvent = event - subscription.dispose() - resolve() - }) + const subscription = atom.notifications.onDidAddNotification( + event => { + notificationEvent = event + subscription.dispose() + resolve() + } + ) atom.packages.activatePackage('package-with-invalid-grammar') }) - expect(notificationEvent.message).toContain('Failed to load a package-with-invalid-grammar package grammar') - expect(notificationEvent.options.packageName).toEqual('package-with-invalid-grammar') + expect(notificationEvent.message).toContain( + 'Failed to load a package-with-invalid-grammar package grammar' + ) + expect(notificationEvent.options.packageName).toEqual( + 'package-with-invalid-grammar' + ) }) it('adds a notification when the settings are invalid', async () => { let notificationEvent await new Promise(resolve => { - const subscription = atom.notifications.onDidAddNotification(event => { - notificationEvent = event - subscription.dispose() - resolve() - }) + const subscription = atom.notifications.onDidAddNotification( + event => { + notificationEvent = event + subscription.dispose() + resolve() + } + ) atom.packages.activatePackage('package-with-invalid-settings') }) - expect(notificationEvent.message).toContain('Failed to load the package-with-invalid-settings package settings') - expect(notificationEvent.options.packageName).toEqual('package-with-invalid-settings') + expect(notificationEvent.message).toContain( + 'Failed to load the package-with-invalid-settings package settings' + ) + expect(notificationEvent.options.packageName).toEqual( + 'package-with-invalid-settings' + ) }) }) }) @@ -710,7 +865,9 @@ describe('PackageManager', () => { expect(Package.prototype.requireMainModule.callCount).toBe(0) - await atom.packages.activatePackage('package-with-empty-activation-hooks') + await atom.packages.activatePackage( + 'package-with-empty-activation-hooks' + ) expect(mainModule.activate.callCount).toBe(1) expect(Package.prototype.requireMainModule.callCount).toBe(1) }) @@ -729,7 +886,9 @@ describe('PackageManager', () => { it('does not throw an exception', () => { spyOn(console, 'error') spyOn(console, 'warn').andCallThrough() - expect(() => atom.packages.activatePackage('package-without-module')).not.toThrow() + expect(() => + atom.packages.activatePackage('package-without-module') + ).not.toThrow() expect(console.error).not.toHaveBeenCalled() expect(console.warn).not.toHaveBeenCalled() }) @@ -744,7 +903,9 @@ describe('PackageManager', () => { }) it("passes the activate method the package's previously serialized state if it exists", async () => { - const pack = await atom.packages.activatePackage('package-with-serialization') + const pack = await atom.packages.activatePackage( + 'package-with-serialization' + ) expect(pack.mainModule.someNumber).not.toBe(77) pack.mainModule.someNumber = 77 atom.packages.serializePackage('package-with-serialization') @@ -752,7 +913,7 @@ describe('PackageManager', () => { spyOn(pack.mainModule, 'activate').andCallThrough() await atom.packages.activatePackage('package-with-serialization') - expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77}) + expect(pack.mainModule.activate).toHaveBeenCalledWith({ someNumber: 77 }) }) it('invokes ::onDidActivatePackage listeners with the activated package', async () => { @@ -771,15 +932,23 @@ describe('PackageManager', () => { atom.config.set('core.disabledPackages', []) const addErrorHandler = jasmine.createSpy() atom.notifications.onDidAddNotification(addErrorHandler) - expect(() => atom.packages.activatePackage('package-that-throws-an-exception')).not.toThrow() + expect(() => + atom.packages.activatePackage('package-that-throws-an-exception') + ).not.toThrow() expect(addErrorHandler.callCount).toBe(1) - expect(addErrorHandler.argsForCall[0][0].message).toContain('Failed to load the package-that-throws-an-exception package') - expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual('package-that-throws-an-exception') + expect(addErrorHandler.argsForCall[0][0].message).toContain( + 'Failed to load the package-that-throws-an-exception package' + ) + expect(addErrorHandler.argsForCall[0][0].options.packageName).toEqual( + 'package-that-throws-an-exception' + ) }) it('re-throws the exception in test mode', () => { atom.config.set('core.disabledPackages', []) - expect(() => atom.packages.activatePackage('package-that-throws-an-exception')).toThrow('This package throws an exception') + expect(() => + atom.packages.activatePackage('package-that-throws-an-exception') + ).toThrow('This package throws an exception') }) }) @@ -793,7 +962,9 @@ describe('PackageManager', () => { expect('Error to be thrown').toBe('') } catch (error) { expect(console.warn.callCount).toBe(1) - expect(error.message).toContain("Failed to load package 'this-doesnt-exist'") + expect(error.message).toContain( + "Failed to load package 'this-doesnt-exist'" + ) } }) }) @@ -804,14 +975,44 @@ describe('PackageManager', () => { const element1 = createTestElement('test-1') const element2 = createTestElement('test-2') const element3 = createTestElement('test-3') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})).toHaveLength(0) - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element2})).toHaveLength(0) - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element3})).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + }) + ).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element2 + }) + ).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element3 + }) + ).toHaveLength(0) await atom.packages.activatePackage('package-with-keymaps') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})[0].command).toBe('test-1') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element2})[0].command).toBe('test-2') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element3})).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + })[0].command + ).toBe('test-1') + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element2 + })[0].command + ).toBe('test-2') + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element3 + }) + ).toHaveLength(0) }) }) @@ -819,30 +1020,64 @@ describe('PackageManager', () => { it('loads only the keymaps specified by the manifest, in the specified order', async () => { const element1 = createTestElement('test-1') const element3 = createTestElement('test-3') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + }) + ).toHaveLength(0) await atom.packages.activatePackage('package-with-keymaps-manifest') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})[0].command).toBe('keymap-1') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-n', target: element1})[0].command).toBe('keymap-2') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-y', target: element3})).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + })[0].command + ).toBe('keymap-1') + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-n', + target: element1 + })[0].command + ).toBe('keymap-2') + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-y', + target: element3 + }) + ).toHaveLength(0) }) }) describe('when the keymap file is empty', () => { it('does not throw an error on activation', async () => { await atom.packages.activatePackage('package-with-empty-keymap') - expect(atom.packages.isPackageActive('package-with-empty-keymap')).toBe(true) + expect( + atom.packages.isPackageActive('package-with-empty-keymap') + ).toBe(true) }) }) describe("when the package's keymaps have been disabled", () => { it('does not add the keymaps', async () => { const element1 = createTestElement('test-1') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + }) + ).toHaveLength(0) - atom.config.set('core.packagesWithKeymapsDisabled', ['package-with-keymaps-manifest']) + atom.config.set('core.packagesWithKeymapsDisabled', [ + 'package-with-keymaps-manifest' + ]) await atom.packages.activatePackage('package-with-keymaps-manifest') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + }) + ).toHaveLength(0) }) }) @@ -850,8 +1085,14 @@ describe('PackageManager', () => { it("ignores package names in the array that aren't loaded", () => { atom.packages.observePackagesWithKeymapsDisabled() - expect(() => atom.config.set('core.packagesWithKeymapsDisabled', ['package-does-not-exist'])).not.toThrow() - expect(() => atom.config.set('core.packagesWithKeymapsDisabled', [])).not.toThrow() + expect(() => + atom.config.set('core.packagesWithKeymapsDisabled', [ + 'package-does-not-exist' + ]) + ).not.toThrow() + expect(() => + atom.config.set('core.packagesWithKeymapsDisabled', []) + ).not.toThrow() }) }) @@ -862,11 +1103,23 @@ describe('PackageManager', () => { await atom.packages.activatePackage('package-with-keymaps-manifest') - atom.config.set('core.packagesWithKeymapsDisabled', ['package-with-keymaps-manifest']) - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})).toHaveLength(0) + atom.config.set('core.packagesWithKeymapsDisabled', [ + 'package-with-keymaps-manifest' + ]) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + }) + ).toHaveLength(0) atom.config.set('core.packagesWithKeymapsDisabled', []) - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: element1})[0].command).toBe('keymap-1') + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: element1 + })[0].command + ).toBe('keymap-1') }) }) @@ -896,17 +1149,24 @@ describe('PackageManager', () => { }) it("doesn't override user-defined keymaps", async () => { - fs.writeFileSync(userKeymapPath, `".test-1": {"ctrl-z": "user-command"}`) + fs.writeFileSync( + userKeymapPath, + `".test-1": {"ctrl-z": "user-command"}` + ) atom.keymaps.loadUserKeymap() await atom.packages.activatePackage('package-with-keymaps') - atom.keymaps.handleKeyboardEvent(buildKeydownEvent('z', {ctrl: true, target: element})) + atom.keymaps.handleKeyboardEvent( + buildKeydownEvent('z', { ctrl: true, target: element }) + ) expect(events.length).toBe(1) expect(events[0].type).toBe('user-command') await atom.packages.deactivatePackage('package-with-keymaps') await atom.packages.activatePackage('package-with-keymaps') - atom.keymaps.handleKeyboardEvent(buildKeydownEvent('z', {ctrl: true, target: element})) + atom.keymaps.handleKeyboardEvent( + buildKeydownEvent('z', { ctrl: true, target: element }) + ) expect(events.length).toBe(2) expect(events[1].type).toBe('user-command') }) @@ -928,9 +1188,15 @@ describe('PackageManager', () => { expect(atom.menu.template.length).toBe(2) expect(atom.menu.template[0].label).toBe('Second to Last') expect(atom.menu.template[1].label).toBe('Last') - expect(atom.contextMenu.templateForElement(element)[0].label).toBe('Menu item 1') - expect(atom.contextMenu.templateForElement(element)[1].label).toBe('Menu item 2') - expect(atom.contextMenu.templateForElement(element)[2].label).toBe('Menu item 3') + expect(atom.contextMenu.templateForElement(element)[0].label).toBe( + 'Menu item 1' + ) + expect(atom.contextMenu.templateForElement(element)[1].label).toBe( + 'Menu item 2' + ) + expect(atom.contextMenu.templateForElement(element)[2].label).toBe( + 'Menu item 3' + ) }) }) @@ -942,16 +1208,24 @@ describe('PackageManager', () => { await atom.packages.activatePackage('package-with-menus-manifest') expect(atom.menu.template[0].label).toBe('Second to Last') expect(atom.menu.template[1].label).toBe('Last') - expect(atom.contextMenu.templateForElement(element)[0].label).toBe('Menu item 2') - expect(atom.contextMenu.templateForElement(element)[1].label).toBe('Menu item 1') - expect(atom.contextMenu.templateForElement(element)[2]).toBeUndefined() + expect(atom.contextMenu.templateForElement(element)[0].label).toBe( + 'Menu item 2' + ) + expect(atom.contextMenu.templateForElement(element)[1].label).toBe( + 'Menu item 1' + ) + expect( + atom.contextMenu.templateForElement(element)[2] + ).toBeUndefined() }) }) describe('when the menu file is empty', () => { it('does not throw an error on activation', async () => { await atom.packages.activatePackage('package-with-empty-menu') - expect(atom.packages.isPackageActive('package-with-empty-menu')).toBe(true) + expect(atom.packages.isPackageActive('package-with-empty-menu')).toBe( + true + ) }) }) }) @@ -959,28 +1233,47 @@ describe('PackageManager', () => { describe('stylesheet loading', () => { describe("when the metadata contains a 'styleSheets' manifest", () => { it('loads style sheets from the styles directory as specified by the manifest', async () => { - const one = require.resolve('./fixtures/packages/package-with-style-sheets-manifest/styles/1.css') - const two = require.resolve('./fixtures/packages/package-with-style-sheets-manifest/styles/2.less') - const three = require.resolve('./fixtures/packages/package-with-style-sheets-manifest/styles/3.css') + const one = require.resolve( + './fixtures/packages/package-with-style-sheets-manifest/styles/1.css' + ) + const two = require.resolve( + './fixtures/packages/package-with-style-sheets-manifest/styles/2.less' + ) + const three = require.resolve( + './fixtures/packages/package-with-style-sheets-manifest/styles/3.css' + ) expect(atom.themes.stylesheetElementForId(one)).toBeNull() expect(atom.themes.stylesheetElementForId(two)).toBeNull() expect(atom.themes.stylesheetElementForId(three)).toBeNull() - await atom.packages.activatePackage('package-with-style-sheets-manifest') + await atom.packages.activatePackage( + 'package-with-style-sheets-manifest' + ) expect(atom.themes.stylesheetElementForId(one)).not.toBeNull() expect(atom.themes.stylesheetElementForId(two)).not.toBeNull() expect(atom.themes.stylesheetElementForId(three)).toBeNull() - expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe('1px') + expect( + getComputedStyle(document.querySelector('#jasmine-content')) + .fontSize + ).toBe('1px') }) }) describe("when the metadata does not contain a 'styleSheets' manifest", () => { it('loads all style sheets from the styles directory', async () => { - const one = require.resolve('./fixtures/packages/package-with-styles/styles/1.css') - const two = require.resolve('./fixtures/packages/package-with-styles/styles/2.less') - const three = require.resolve('./fixtures/packages/package-with-styles/styles/3.test-context.css') - const four = require.resolve('./fixtures/packages/package-with-styles/styles/4.css') + const one = require.resolve( + './fixtures/packages/package-with-styles/styles/1.css' + ) + const two = require.resolve( + './fixtures/packages/package-with-styles/styles/2.less' + ) + const three = require.resolve( + './fixtures/packages/package-with-styles/styles/3.test-context.css' + ) + const four = require.resolve( + './fixtures/packages/package-with-styles/styles/4.css' + ) expect(atom.themes.stylesheetElementForId(one)).toBeNull() expect(atom.themes.stylesheetElementForId(two)).toBeNull() @@ -992,7 +1285,10 @@ describe('PackageManager', () => { expect(atom.themes.stylesheetElementForId(two)).not.toBeNull() expect(atom.themes.stylesheetElementForId(three)).not.toBeNull() expect(atom.themes.stylesheetElementForId(four)).not.toBeNull() - expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe('3px') + expect( + getComputedStyle(document.querySelector('#jasmine-content')) + .fontSize + ).toBe('3px') }) }) @@ -1045,18 +1341,23 @@ describe('PackageManager', () => { describe('scoped-property loading', () => { it('loads the scoped properties', async () => { await atom.packages.activatePackage('package-with-settings') - expect(atom.config.get('editor.increaseIndentPattern', {scope: ['.source.omg']})).toBe('^a') + expect( + atom.config.get('editor.increaseIndentPattern', { + scope: ['.source.omg'] + }) + ).toBe('^a') }) }) - - describe("URI handler registration", () => { + describe('URI handler registration', () => { it("registers the package's specified URI handler", async () => { const uri = 'atom://package-with-uri-handler/some/url?with=args' const mod = require('./fixtures/packages/package-with-uri-handler') spyOn(mod, 'handleURI') spyOn(atom.packages, 'hasLoadedInitialPackages').andReturn(true) - const activationPromise = atom.packages.activatePackage('package-with-uri-handler') + const activationPromise = atom.packages.activatePackage( + 'package-with-uri-handler' + ) atom.dispatchURIMessage(uri) await activationPromise expect(mod.handleURI).toHaveBeenCalledWith(url.parse(uri, true), uri) @@ -1070,16 +1371,34 @@ describe('PackageManager', () => { let firstServiceV3Disposed = false let firstServiceV4Disposed = false let secondServiceDisposed = false - spyOn(consumerModule, 'consumeFirstServiceV3').andReturn(new Disposable(() => { firstServiceV3Disposed = true })) - spyOn(consumerModule, 'consumeFirstServiceV4').andReturn(new Disposable(() => { firstServiceV4Disposed = true })) - spyOn(consumerModule, 'consumeSecondService').andReturn(new Disposable(() => { secondServiceDisposed = true })) + spyOn(consumerModule, 'consumeFirstServiceV3').andReturn( + new Disposable(() => { + firstServiceV3Disposed = true + }) + ) + spyOn(consumerModule, 'consumeFirstServiceV4').andReturn( + new Disposable(() => { + firstServiceV4Disposed = true + }) + ) + spyOn(consumerModule, 'consumeSecondService').andReturn( + new Disposable(() => { + secondServiceDisposed = true + }) + ) await atom.packages.activatePackage('package-with-consumed-services') await atom.packages.activatePackage('package-with-provided-services') expect(consumerModule.consumeFirstServiceV3.callCount).toBe(1) - expect(consumerModule.consumeFirstServiceV3).toHaveBeenCalledWith('first-service-v3') - expect(consumerModule.consumeFirstServiceV4).toHaveBeenCalledWith('first-service-v4') - expect(consumerModule.consumeSecondService).toHaveBeenCalledWith('second-service') + expect(consumerModule.consumeFirstServiceV3).toHaveBeenCalledWith( + 'first-service-v3' + ) + expect(consumerModule.consumeFirstServiceV4).toHaveBeenCalledWith( + 'first-service-v4' + ) + expect(consumerModule.consumeSecondService).toHaveBeenCalledWith( + 'second-service' + ) consumerModule.consumeFirstServiceV3.reset() consumerModule.consumeFirstServiceV4.reset() @@ -1101,10 +1420,22 @@ describe('PackageManager', () => { const addErrorHandler = jasmine.createSpy() atom.notifications.onDidAddNotification(addErrorHandler) - await atom.packages.activatePackage('package-with-missing-consumed-services') - await atom.packages.activatePackage('package-with-missing-provided-services') - expect(atom.packages.isPackageActive('package-with-missing-consumed-services')).toBe(true) - expect(atom.packages.isPackageActive('package-with-missing-provided-services')).toBe(true) + await atom.packages.activatePackage( + 'package-with-missing-consumed-services' + ) + await atom.packages.activatePackage( + 'package-with-missing-provided-services' + ) + expect( + atom.packages.isPackageActive( + 'package-with-missing-consumed-services' + ) + ).toBe(true) + expect( + atom.packages.isPackageActive( + 'package-with-missing-provided-services' + ) + ).toBe(true) expect(addErrorHandler.callCount).toBe(0) }) }) @@ -1115,7 +1446,9 @@ describe('PackageManager', () => { spyOn(atom, 'inSpecMode').andReturn(false) spyOn(console, 'warn') - const badPack = await atom.packages.activatePackage('package-that-throws-on-activate') + const badPack = await atom.packages.activatePackage( + 'package-that-throws-on-activate' + ) spyOn(badPack.mainModule, 'serialize').andCallThrough() atom.packages.serialize() @@ -1128,16 +1461,24 @@ describe('PackageManager', () => { await atom.packages.activatePackage('package-with-serialize-error') await atom.packages.activatePackage('package-with-serialization') atom.packages.serialize() - expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined() - expect(atom.packages.packageStates['package-with-serialization']).toEqual({someNumber: 1}) + expect( + atom.packages.packageStates['package-with-serialize-error'] + ).toBeUndefined() + expect(atom.packages.packageStates['package-with-serialization']).toEqual( + { someNumber: 1 } + ) expect(console.error).toHaveBeenCalled() }) }) describe('::deactivatePackages()', () => { it('deactivates all packages but does not serialize them', async () => { - const pack1 = await atom.packages.activatePackage('package-with-deactivate') - const pack2 = await atom.packages.activatePackage('package-with-serialization') + const pack1 = await atom.packages.activatePackage( + 'package-with-deactivate' + ) + const pack2 = await atom.packages.activatePackage( + 'package-with-serialization' + ) spyOn(pack1.mainModule, 'deactivate') spyOn(pack2.mainModule, 'serialize') @@ -1153,8 +1494,12 @@ describe('PackageManager', () => { it("calls `deactivate` on the package's main module if activate was successful", async () => { spyOn(atom, 'inSpecMode').andReturn(false) - const pack = await atom.packages.activatePackage('package-with-deactivate') - expect(atom.packages.isPackageActive('package-with-deactivate')).toBeTruthy() + const pack = await atom.packages.activatePackage( + 'package-with-deactivate' + ) + expect( + atom.packages.isPackageActive('package-with-deactivate') + ).toBeTruthy() spyOn(pack.mainModule, 'deactivate').andCallThrough() await atom.packages.deactivatePackage('package-with-deactivate') @@ -1162,13 +1507,19 @@ describe('PackageManager', () => { expect(atom.packages.isPackageActive('package-with-module')).toBeFalsy() spyOn(console, 'warn') - const badPack = await atom.packages.activatePackage('package-that-throws-on-activate') - expect(atom.packages.isPackageActive('package-that-throws-on-activate')).toBeTruthy() + const badPack = await atom.packages.activatePackage( + 'package-that-throws-on-activate' + ) + expect( + atom.packages.isPackageActive('package-that-throws-on-activate') + ).toBeTruthy() spyOn(badPack.mainModule, 'deactivate').andCallThrough() await atom.packages.deactivatePackage('package-that-throws-on-activate') expect(badPack.mainModule.deactivate).not.toHaveBeenCalled() - expect(atom.packages.isPackageActive('package-that-throws-on-activate')).toBeFalsy() + expect( + atom.packages.isPackageActive('package-that-throws-on-activate') + ).toBeFalsy() }) it("absorbs exceptions that are thrown by the package module's deactivate method", async () => { @@ -1188,17 +1539,33 @@ describe('PackageManager', () => { it("removes the package's keymaps", async () => { await atom.packages.activatePackage('package-with-keymaps') await atom.packages.deactivatePackage('package-with-keymaps') - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: createTestElement('test-1')})).toHaveLength(0) - expect(atom.keymaps.findKeyBindings({keystrokes: 'ctrl-z', target: createTestElement('test-2')})).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: createTestElement('test-1') + }) + ).toHaveLength(0) + expect( + atom.keymaps.findKeyBindings({ + keystrokes: 'ctrl-z', + target: createTestElement('test-2') + }) + ).toHaveLength(0) }) it("removes the package's stylesheets", async () => { await atom.packages.activatePackage('package-with-styles') await atom.packages.deactivatePackage('package-with-styles') - const one = require.resolve('./fixtures/packages/package-with-style-sheets-manifest/styles/1.css') - const two = require.resolve('./fixtures/packages/package-with-style-sheets-manifest/styles/2.less') - const three = require.resolve('./fixtures/packages/package-with-style-sheets-manifest/styles/3.css') + const one = require.resolve( + './fixtures/packages/package-with-style-sheets-manifest/styles/1.css' + ) + const two = require.resolve( + './fixtures/packages/package-with-style-sheets-manifest/styles/2.less' + ) + const three = require.resolve( + './fixtures/packages/package-with-style-sheets-manifest/styles/3.css' + ) expect(atom.themes.stylesheetElementForId(one)).not.toExist() expect(atom.themes.stylesheetElementForId(two)).not.toExist() expect(atom.themes.stylesheetElementForId(three)).not.toExist() @@ -1206,10 +1573,18 @@ describe('PackageManager', () => { it("removes the package's scoped-properties", async () => { await atom.packages.activatePackage('package-with-settings') - expect(atom.config.get('editor.increaseIndentPattern', {scope: ['.source.omg']})).toBe('^a') + expect( + atom.config.get('editor.increaseIndentPattern', { + scope: ['.source.omg'] + }) + ).toBe('^a') await atom.packages.deactivatePackage('package-with-settings') - expect(atom.config.get('editor.increaseIndentPattern', {scope: ['.source.omg']})).toBeUndefined() + expect( + atom.config.get('editor.increaseIndentPattern', { + scope: ['.source.omg'] + }) + ).toBeUndefined() }) it('invokes ::onDidDeactivatePackage listeners with the deactivated package', async () => { @@ -1261,21 +1636,31 @@ describe('PackageManager', () => { expect(themeActivator).toHaveBeenCalled() const packages = packageActivator.mostRecentCall.args[0] - for (let pack of packages) { expect(['atom', 'textmate']).toContain(pack.getType()) } + for (let pack of packages) { + expect(['atom', 'textmate']).toContain(pack.getType()) + } const themes = themeActivator.mostRecentCall.args[0] - themes.map((theme) => expect(['theme']).toContain(theme.getType())) + themes.map(theme => expect(['theme']).toContain(theme.getType())) }) it('calls callbacks registered with ::onDidActivateInitialPackages', async () => { const package1 = atom.packages.loadPackage('package-with-main') const package2 = atom.packages.loadPackage('package-with-index') - const package3 = atom.packages.loadPackage('package-with-activation-commands') - spyOn(atom.packages, 'getLoadedPackages').andReturn([package1, package2, package3]) + const package3 = atom.packages.loadPackage( + 'package-with-activation-commands' + ) + spyOn(atom.packages, 'getLoadedPackages').andReturn([ + package1, + package2, + package3 + ]) spyOn(atom.themes, 'activatePackages') atom.packages.activate() - await new Promise(resolve => atom.packages.onDidActivateInitialPackages(resolve)) + await new Promise(resolve => + atom.packages.onDidActivateInitialPackages(resolve) + ) jasmine.unspy(atom.packages, 'getLoadedPackages') expect(atom.packages.getActivePackages().includes(package1)).toBe(true) @@ -1293,11 +1678,15 @@ describe('PackageManager', () => { expect(atom.config.get('core.disabledPackages')).toContain(packageName) const pack = atom.packages.enablePackage(packageName) - await new Promise(resolve => atom.packages.onDidActivatePackage(resolve)) + await new Promise(resolve => + atom.packages.onDidActivatePackage(resolve) + ) expect(atom.packages.getLoadedPackages()).toContain(pack) expect(atom.packages.getActivePackages()).toContain(pack) - expect(atom.config.get('core.disabledPackages')).not.toContain(packageName) + expect(atom.config.get('core.disabledPackages')).not.toContain( + packageName + ) }) it('disables an enabled package', async () => { @@ -1305,7 +1694,9 @@ describe('PackageManager', () => { const pack = await atom.packages.activatePackage(packageName) atom.packages.observeDisabledPackages() - expect(atom.config.get('core.disabledPackages')).not.toContain(packageName) + expect(atom.config.get('core.disabledPackages')).not.toContain( + packageName + ) await new Promise(resolve => { atom.packages.onDidDeactivatePackage(resolve) atom.packages.disablePackage(packageName) @@ -1328,7 +1719,9 @@ describe('PackageManager', () => { expect(atom.config.get('core.disabledPackages')).toContain(packageName) atom.packages.disablePackage(packageName) - const packagesDisabled = atom.config.get('core.disabledPackages').filter(pack => pack === packageName) + const packagesDisabled = atom.config + .get('core.disabledPackages') + .filter(pack => pack === packageName) expect(packagesDisabled.length).toEqual(1) }) }) @@ -1340,14 +1733,20 @@ describe('PackageManager', () => { it('enables and disables a theme', async () => { const packageName = 'theme-with-package-file' expect(atom.config.get('core.themes')).not.toContain(packageName) - expect(atom.config.get('core.disabledPackages')).not.toContain(packageName) + expect(atom.config.get('core.disabledPackages')).not.toContain( + packageName + ) // enabling of theme const pack = atom.packages.enablePackage(packageName) - await new Promise(resolve => atom.packages.onDidActivatePackage(resolve)) + await new Promise(resolve => + atom.packages.onDidActivatePackage(resolve) + ) expect(atom.packages.isPackageActive(packageName)).toBe(true) expect(atom.config.get('core.themes')).toContain(packageName) - expect(atom.config.get('core.disabledPackages')).not.toContain(packageName) + expect(atom.config.get('core.disabledPackages')).not.toContain( + packageName + ) await new Promise(resolve => { atom.themes.onDidChangeActiveThemes(resolve) @@ -1357,7 +1756,9 @@ describe('PackageManager', () => { expect(atom.packages.getActivePackages()).not.toContain(pack) expect(atom.config.get('core.themes')).not.toContain(packageName) expect(atom.config.get('core.themes')).not.toContain(packageName) - expect(atom.config.get('core.disabledPackages')).not.toContain(packageName) + expect(atom.config.get('core.disabledPackages')).not.toContain( + packageName + ) }) }) }) diff --git a/spec/package-transpilation-registry-spec.js b/spec/package-transpilation-registry-spec.js index 214f1177e..df2599db1 100644 --- a/spec/package-transpilation-registry-spec.js +++ b/spec/package-transpilation-registry-spec.js @@ -1,18 +1,15 @@ /** @babel */ -import fs from 'fs' import path from 'path' -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' - import PackageTranspilationRegistry from '../src/package-transpilation-registry' const originalCompiler = { getCachePath: (sourceCode, filePath) => { - return "orig-cache-path" + return 'orig-cache-path' }, compile: (sourceCode, filePath) => { - return sourceCode + "-original-compiler" + return sourceCode + '-original-compiler' }, shouldCompile: (sourceCode, filePath) => { @@ -20,7 +17,7 @@ const originalCompiler = { } } -describe("PackageTranspilationRegistry", () => { +describe('PackageTranspilationRegistry', () => { let registry let wrappedCompiler @@ -47,20 +44,36 @@ describe("PackageTranspilationRegistry", () => { const hitPath = path.join('/path/to/lib/file.js') const hitPathCoffee = path.join('/path/to/file2.coffee') const missPath = path.join('/path/other/file3.js') - const hitPathMissSubdir =path.join('/path/to/file4.js') + const hitPathMissSubdir = path.join('/path/to/file4.js') const hitPathMissExt = path.join('/path/to/file5.ts') const nodeModulesFolder = path.join('/path/to/lib/node_modules/file6.js') const hitNonStandardExt = path.join('/path/to/file7.omgwhatisthis') - const jsSpec = { glob: "lib/**/*.js", transpiler: './transpiler-js', options: { type: 'js' } } - const coffeeSpec = { glob: "*.coffee", transpiler: './transpiler-coffee', options: { type: 'coffee' } } - const omgSpec = { glob: "*.omgwhatisthis", transpiler: './transpiler-omg', options: { type: 'omg' } } + const jsSpec = { + glob: 'lib/**/*.js', + transpiler: './transpiler-js', + options: { type: 'js' } + } + const coffeeSpec = { + glob: '*.coffee', + transpiler: './transpiler-coffee', + options: { type: 'coffee' } + } + const omgSpec = { + glob: '*.omgwhatisthis', + transpiler: './transpiler-omg', + options: { type: 'omg' } + } - const expectedMeta = { name: 'my-package', path: path.join('/path/to'), meta: { some: 'metadata' } } + const expectedMeta = { + name: 'my-package', + path: path.join('/path/to'), + meta: { some: 'metadata' } + } const jsTranspiler = { transpile: (sourceCode, filePath, options) => { - return {code: sourceCode + "-transpiler-js"} + return { code: sourceCode + '-transpiler-js' } }, getCacheKeyData: (sourceCode, filePath, options) => { @@ -70,7 +83,7 @@ describe("PackageTranspilationRegistry", () => { const coffeeTranspiler = { transpile: (sourceCode, filePath, options) => { - return {code: sourceCode + "-transpiler-coffee"} + return { code: sourceCode + '-transpiler-coffee' } }, getCacheKeyData: (sourceCode, filePath, options) => { @@ -80,7 +93,7 @@ describe("PackageTranspilationRegistry", () => { const omgTranspiler = { transpile: (sourceCode, filePath, options) => { - return {code: sourceCode + "-transpiler-omg"} + return { code: sourceCode + '-transpiler-omg' } }, getCacheKeyData: (sourceCode, filePath, options) => { @@ -89,72 +102,135 @@ describe("PackageTranspilationRegistry", () => { } beforeEach(() => { - jsSpec._transpilerSource = "js-transpiler-source" - coffeeSpec._transpilerSource = "coffee-transpiler-source" - omgTranspiler._transpilerSource = "omg-transpiler-source" + jsSpec._transpilerSource = 'js-transpiler-source' + coffeeSpec._transpilerSource = 'coffee-transpiler-source' + omgTranspiler._transpilerSource = 'omg-transpiler-source' - spyOn(registry, "getTranspiler").andCallFake(spec => { + spyOn(registry, 'getTranspiler').andCallFake(spec => { if (spec.transpiler === './transpiler-js') return jsTranspiler if (spec.transpiler === './transpiler-coffee') return coffeeTranspiler if (spec.transpiler === './transpiler-omg') return omgTranspiler throw new Error('bad transpiler path ' + spec.transpiler) }) - registry.addTranspilerConfigForPath(path.join('/path/to'), 'my-package', { some: 'metadata' }, [ - jsSpec, coffeeSpec, omgSpec - ]) + registry.addTranspilerConfigForPath( + path.join('/path/to'), + 'my-package', + { some: 'metadata' }, + [jsSpec, coffeeSpec, omgSpec] + ) }) it('always returns true from shouldCompile for a file in that dir that match a glob', () => { spyOn(originalCompiler, 'shouldCompile').andReturn(false) expect(wrappedCompiler.shouldCompile('source', hitPath)).toBe(true) expect(wrappedCompiler.shouldCompile('source', hitPathCoffee)).toBe(true) - expect(wrappedCompiler.shouldCompile('source', hitNonStandardExt)).toBe(true) - expect(wrappedCompiler.shouldCompile('source', hitPathMissExt)).toBe(false) - expect(wrappedCompiler.shouldCompile('source', hitPathMissSubdir)).toBe(false) + expect(wrappedCompiler.shouldCompile('source', hitNonStandardExt)).toBe( + true + ) + expect(wrappedCompiler.shouldCompile('source', hitPathMissExt)).toBe( + false + ) + expect(wrappedCompiler.shouldCompile('source', hitPathMissSubdir)).toBe( + false + ) expect(wrappedCompiler.shouldCompile('source', missPath)).toBe(false) - expect(wrappedCompiler.shouldCompile('source', nodeModulesFolder)).toBe(false) + expect(wrappedCompiler.shouldCompile('source', nodeModulesFolder)).toBe( + false + ) }) it('calls getCacheKeyData on the transpiler to get additional cache key data', () => { - spyOn(registry, "getTranspilerPath").andReturn("./transpiler-js") + spyOn(registry, 'getTranspilerPath').andReturn('./transpiler-js') spyOn(jsTranspiler, 'getCacheKeyData').andCallThrough() wrappedCompiler.getCachePath('source', missPath, jsSpec) - expect(jsTranspiler.getCacheKeyData).not.toHaveBeenCalledWith('source', missPath, jsSpec.options, expectedMeta) + expect(jsTranspiler.getCacheKeyData).not.toHaveBeenCalledWith( + 'source', + missPath, + jsSpec.options, + expectedMeta + ) wrappedCompiler.getCachePath('source', hitPath, jsSpec) - expect(jsTranspiler.getCacheKeyData).toHaveBeenCalledWith('source', hitPath, jsSpec.options, expectedMeta) + expect(jsTranspiler.getCacheKeyData).toHaveBeenCalledWith( + 'source', + hitPath, + jsSpec.options, + expectedMeta + ) }) it('compiles files matching a glob with the associated transpiler, and the old one otherwise', () => { - spyOn(jsTranspiler, "transpile").andCallThrough() - spyOn(coffeeTranspiler, "transpile").andCallThrough() - spyOn(omgTranspiler, "transpile").andCallThrough() + spyOn(jsTranspiler, 'transpile').andCallThrough() + spyOn(coffeeTranspiler, 'transpile').andCallThrough() + spyOn(omgTranspiler, 'transpile').andCallThrough() - expect(wrappedCompiler.compile('source', hitPath)).toEqual('source-transpiler-js') - expect(jsTranspiler.transpile).toHaveBeenCalledWith('source', hitPath, jsSpec.options, expectedMeta) - expect(wrappedCompiler.compile('source', hitPathCoffee)).toEqual('source-transpiler-coffee') - expect(coffeeTranspiler.transpile).toHaveBeenCalledWith('source', hitPathCoffee, coffeeSpec.options, expectedMeta) - expect(wrappedCompiler.compile('source', hitNonStandardExt)).toEqual('source-transpiler-omg') - expect(omgTranspiler.transpile).toHaveBeenCalledWith('source', hitNonStandardExt, omgSpec.options, expectedMeta) + expect(wrappedCompiler.compile('source', hitPath)).toEqual( + 'source-transpiler-js' + ) + expect(jsTranspiler.transpile).toHaveBeenCalledWith( + 'source', + hitPath, + jsSpec.options, + expectedMeta + ) + expect(wrappedCompiler.compile('source', hitPathCoffee)).toEqual( + 'source-transpiler-coffee' + ) + expect(coffeeTranspiler.transpile).toHaveBeenCalledWith( + 'source', + hitPathCoffee, + coffeeSpec.options, + expectedMeta + ) + expect(wrappedCompiler.compile('source', hitNonStandardExt)).toEqual( + 'source-transpiler-omg' + ) + expect(omgTranspiler.transpile).toHaveBeenCalledWith( + 'source', + hitNonStandardExt, + omgSpec.options, + expectedMeta + ) - expect(wrappedCompiler.compile('source', missPath)).toEqual('source-original-compiler') - expect(wrappedCompiler.compile('source', hitPathMissExt)).toEqual('source-original-compiler') - expect(wrappedCompiler.compile('source', hitPathMissSubdir)).toEqual('source-original-compiler') - expect(wrappedCompiler.compile('source', nodeModulesFolder)).toEqual('source-original-compiler') + expect(wrappedCompiler.compile('source', missPath)).toEqual( + 'source-original-compiler' + ) + expect(wrappedCompiler.compile('source', hitPathMissExt)).toEqual( + 'source-original-compiler' + ) + expect(wrappedCompiler.compile('source', hitPathMissSubdir)).toEqual( + 'source-original-compiler' + ) + expect(wrappedCompiler.compile('source', nodeModulesFolder)).toEqual( + 'source-original-compiler' + ) }) - describe('when the packages root path contains node_modules', () =>{ + describe('when the packages root path contains node_modules', () => { beforeEach(() => { - registry.addTranspilerConfigForPath(path.join('/path/with/node_modules/in/root'), 'my-other-package', { some: 'metadata' }, [ - jsSpec - ]) + registry.addTranspilerConfigForPath( + path.join('/path/with/node_modules/in/root'), + 'my-other-package', + { some: 'metadata' }, + [jsSpec] + ) }) it('returns appropriate values from shouldCompile', () => { spyOn(originalCompiler, 'shouldCompile').andReturn(false) - expect(wrappedCompiler.shouldCompile('source', '/path/with/node_modules/in/root/lib/test.js')).toBe(true) - expect(wrappedCompiler.shouldCompile('source', '/path/with/node_modules/in/root/lib/node_modules/test.js')).toBe(false) + expect( + wrappedCompiler.shouldCompile( + 'source', + '/path/with/node_modules/in/root/lib/test.js' + ) + ).toBe(true) + expect( + wrappedCompiler.shouldCompile( + 'source', + '/path/with/node_modules/in/root/lib/node_modules/test.js' + ) + ).toBe(false) }) }) }) diff --git a/spec/pane-container-spec.js b/spec/pane-container-spec.js index 060808d0b..cd6da9939 100644 --- a/spec/pane-container-spec.js +++ b/spec/pane-container-spec.js @@ -1,11 +1,12 @@ const PaneContainer = require('../src/pane-container') -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') describe('PaneContainer', () => { let confirm, params beforeEach(() => { - confirm = spyOn(atom.applicationDelegate, 'confirm').andCallFake((options, callback) => callback(0)) + confirm = spyOn(atom.applicationDelegate, 'confirm').andCallFake( + (options, callback) => callback(0) + ) params = { location: 'center', config: atom.config, @@ -21,16 +22,20 @@ describe('PaneContainer', () => { beforeEach(() => { // This is a dummy item to prevent panes from being empty on deserialization class Item { - static deserialize () { return new (this)() } - serialize () { return {deserializer: 'Item'} } + static deserialize () { + return new this() + } + serialize () { + return { deserializer: 'Item' } + } } atom.deserializers.add(Item) containerA = new PaneContainer(params) pane1A = containerA.getActivePane() pane1A.addItem(new Item()) - pane2A = pane1A.splitRight({items: [new Item()]}) - pane3A = pane2A.splitDown({items: [new Item()]}) + pane2A = pane1A.splitRight({ items: [new Item()] }) + pane3A = pane2A.splitDown({ items: [new Item()] }) pane3A.focus() }) @@ -64,7 +69,7 @@ describe('PaneContainer', () => { describe('if there are empty panes after deserialization', () => { beforeEach(() => { - pane3A.getItems()[0].serialize = () => ({deserializer: 'Bogus'}) + pane3A.getItems()[0].serialize = () => ({ deserializer: 'Bogus' }) }) describe("if the 'core.destroyEmptyPanes' config option is false (the default)", () => @@ -78,8 +83,7 @@ describe('PaneContainer', () => { expect(leftPane.getItems().length).toBe(1) expect(topPane.getItems().length).toBe(1) expect(bottomPane.getItems().length).toBe(0) - }) - ) + })) describe("if the 'core.destroyEmptyPanes' config option is true", () => it('removes empty panes on deserialization', () => { @@ -92,8 +96,7 @@ describe('PaneContainer', () => { expect(leftPane.getItems().length).toBe(1) expect(rightPane.getItems().length).toBe(1) - }) - ) + })) }) }) @@ -144,8 +147,8 @@ describe('PaneContainer', () => { beforeEach(() => { container = new PaneContainer(params) container.getRoot().addItems([{}, {}]) - container.getRoot().splitRight({items: [{}, {}]}); - [pane1, pane2] = container.getPanes() + container.getRoot().splitRight({ items: [{}, {}] }) + ;[pane1, pane2] = container.getPanes() observed = [] container.onDidChangeActivePane(pane => observed.push(pane)) @@ -164,8 +167,8 @@ describe('PaneContainer', () => { beforeEach(() => { container = new PaneContainer(params) container.getRoot().addItems([{}, {}]) - container.getRoot().splitRight({items: [{}, {}]}); - [pane1, pane2] = container.getPanes() + container.getRoot().splitRight({ items: [{}, {}] }) + ;[pane1, pane2] = container.getPanes() observed = [] container.onDidChangeActivePaneItem(item => observed.push(item)) @@ -190,8 +193,8 @@ describe('PaneContainer', () => { beforeEach(() => { container = new PaneContainer(params) container.getRoot().addItems([{}, {}]) - container.getRoot().splitRight({items: [{}, {}]}); - [pane1, pane2] = container.getPanes() + container.getRoot().splitRight({ items: [{}, {}] }) + ;[pane1, pane2] = container.getPanes() observed = [] container.onDidStopChangingActivePaneItem(item => observed.push(item)) @@ -251,30 +254,33 @@ describe('PaneContainer', () => { it('invokes observers with all current and future pane items', () => { const container = new PaneContainer(params) container.getRoot().addItems([{}, {}]) - container.getRoot().splitRight({items: [{}]}) + container.getRoot().splitRight({ items: [{}] }) const pane2 = container.getPanes()[1] const observed = [] container.observePaneItems(pane => observed.push(pane)) - const pane3 = pane2.splitDown({items: [{}]}) + const pane3 = pane2.splitDown({ items: [{}] }) pane3.addItems([{}, {}]) expect(observed).toEqual(container.getPaneItems()) - }) - ) + })) describe('::confirmClose()', () => { let container, pane1, pane2 beforeEach(() => { class TestItem { - shouldPromptToSave () { return true } - getURI () { return 'test' } + shouldPromptToSave () { + return true + } + getURI () { + return 'test' + } } container = new PaneContainer(params) - container.getRoot().splitRight(); - [pane1, pane2] = container.getPanes() + container.getRoot().splitRight() + ;[pane1, pane2] = container.getPanes() pane1.addItem(new TestItem()) pane2.addItem(new TestItem()) }) @@ -298,7 +304,7 @@ describe('PaneContainer', () => { it('invokes the given callback when panes are added', () => { const container = new PaneContainer(params) const events = [] - container.onDidAddPane((event) => { + container.onDidAddPane(event => { expect(container.getPanes().includes(event.pane)).toBe(true) events.push(event) }) @@ -307,23 +313,31 @@ describe('PaneContainer', () => { const pane2 = pane1.splitRight() const pane3 = pane2.splitDown() - expect(events).toEqual([{pane: pane2}, {pane: pane3}]) + expect(events).toEqual([{ pane: pane2 }, { pane: pane3 }]) }) }) describe('::onWillDestroyPane(callback)', () => { it('invokes the given callback before panes or their items are destroyed', () => { class TestItem { - constructor () { this._isDestroyed = false } - destroy () { this._isDestroyed = true } - isDestroyed () { return this._isDestroyed } + constructor () { + this._isDestroyed = false + } + destroy () { + this._isDestroyed = true + } + isDestroyed () { + return this._isDestroyed + } } const container = new PaneContainer(params) const events = [] - container.onWillDestroyPane((event) => { - const itemsDestroyed = event.pane.getItems().map((item) => item.isDestroyed()) - events.push([event, {itemsDestroyed}]) + container.onWillDestroyPane(event => { + const itemsDestroyed = event.pane + .getItems() + .map(item => item.isDestroyed()) + events.push([event, { itemsDestroyed }]) }) const pane1 = container.getActivePane() @@ -332,7 +346,7 @@ describe('PaneContainer', () => { pane2.destroy() - expect(events).toEqual([[{pane: pane2}, {itemsDestroyed: [false]}]]) + expect(events).toEqual([[{ pane: pane2 }, { itemsDestroyed: [false] }]]) }) }) @@ -340,7 +354,7 @@ describe('PaneContainer', () => { it('invokes the given callback when panes are destroyed', () => { const container = new PaneContainer(params) const events = [] - container.onDidDestroyPane((event) => { + container.onDidDestroyPane(event => { expect(container.getPanes().includes(event.pane)).toBe(false) events.push(event) }) @@ -352,13 +366,13 @@ describe('PaneContainer', () => { pane2.destroy() pane3.destroy() - expect(events).toEqual([{pane: pane2}, {pane: pane3}]) + expect(events).toEqual([{ pane: pane2 }, { pane: pane3 }]) }) it('invokes the given callback when the container is destroyed', () => { const container = new PaneContainer(params) const events = [] - container.onDidDestroyPane((event) => { + container.onDidDestroyPane(event => { expect(container.getPanes().includes(event.pane)).toBe(false) events.push(event) }) @@ -369,7 +383,11 @@ describe('PaneContainer', () => { container.destroy() - expect(events).toEqual([{pane: pane1}, {pane: pane2}, {pane: pane3}]) + expect(events).toEqual([ + { pane: pane1 }, + { pane: pane2 }, + { pane: pane3 } + ]) }) }) @@ -385,19 +403,19 @@ describe('PaneContainer', () => { const events = [] container.onWillDestroyPaneItem(event => events.push(['will', event])) container.onDidDestroyPaneItem(event => events.push(['did', event])) - const pane2 = pane1.splitRight({items: [item2, item3]}) + const pane2 = pane1.splitRight({ items: [item2, item3] }) await pane1.destroyItem(item1) await pane2.destroyItem(item3) await pane2.destroyItem(item2) expect(events).toEqual([ - ['will', {item: item1, pane: pane1, index: 0}], - ['did', {item: item1, pane: pane1, index: 0}], - ['will', {item: item3, pane: pane2, index: 1}], - ['did', {item: item3, pane: pane2, index: 1}], - ['will', {item: item2, pane: pane2, index: 0}], - ['did', {item: item2, pane: pane2, index: 0}] + ['will', { item: item1, pane: pane1, index: 0 }], + ['did', { item: item1, pane: pane1, index: 0 }], + ['will', { item: item3, pane: pane2, index: 1 }], + ['did', { item: item3, pane: pane2, index: 1 }], + ['will', { item: item2, pane: pane2, index: 0 }], + ['did', { item: item2, pane: pane2, index: 0 }] ]) }) }) @@ -410,21 +428,39 @@ describe('PaneContainer', () => { const item1 = { saved: false, - getURI () { return '' }, - isModified () { return true }, - save () { this.saved = true } + getURI () { + return '' + }, + isModified () { + return true + }, + save () { + this.saved = true + } } const item2 = { saved: false, - getURI () { return '' }, - isModified () { return false }, - save () { this.saved = true } + getURI () { + return '' + }, + isModified () { + return false + }, + save () { + this.saved = true + } } const item3 = { saved: false, - getURI () { return '' }, - isModified () { return true }, - save () { this.saved = true } + getURI () { + return '' + }, + isModified () { + return true + }, + save () { + this.saved = true + } } pane1.addItem(item1) @@ -436,37 +472,38 @@ describe('PaneContainer', () => { expect(item1.saved).toBe(true) expect(item2.saved).toBe(false) expect(item3.saved).toBe(true) - }) - ) + })) describe('::moveActiveItemToPane(destPane) and ::copyActiveItemToPane(destPane)', () => { let container, pane1, pane2, item1 beforeEach(() => { class TestItem { - constructor (id) { this.id = id } - copy () { return new TestItem(this.id) } + constructor (id) { + this.id = id + } + copy () { + return new TestItem(this.id) + } } container = new PaneContainer(params) pane1 = container.getRoot() item1 = new TestItem('1') - pane2 = pane1.splitRight({items: [item1]}) + pane2 = pane1.splitRight({ items: [item1] }) }) describe('::::moveActiveItemToPane(destPane)', () => it('moves active item to given pane and focuses it', () => { container.moveActiveItemToPane(pane1) expect(pane1.getActiveItem()).toBe(item1) - }) - ) + })) describe('::::copyActiveItemToPane(destPane)', () => it('copies active item to given pane and focuses it', () => { container.copyActiveItemToPane(pane1) expect(container.paneForItem(item1)).toBe(pane2) expect(pane1.getActiveItem().id).toBe(item1.id) - }) - ) + })) }) }) diff --git a/spec/pane-spec.js b/spec/pane-spec.js index ddb92b96e..c9ca0fb4b 100644 --- a/spec/pane-spec.js +++ b/spec/pane-spec.js @@ -1,15 +1,15 @@ -const {extend} = require('underscore-plus') -const {Emitter} = require('event-kit') +const { extend } = require('underscore-plus') +const { Emitter } = require('event-kit') const Grim = require('grim') const Pane = require('../src/pane') const PaneContainer = require('../src/pane-container') -const {it, fit, ffit, fffit, beforeEach, conditionPromise, timeoutPromise} = require('./async-spec-helpers') +const { conditionPromise, timeoutPromise } = require('./async-spec-helpers') describe('Pane', () => { let confirm, showSaveDialog, deserializerDisposable class Item { - static deserialize ({name, uri}) { + static deserialize ({ name, uri }) { return new Item(name, uri) } @@ -20,14 +20,24 @@ describe('Pane', () => { this.destroyed = false } - getURI () { return this.uri } - getPath () { return this.path } - isEqual (other) { return this.name === (other && other.name) } - isPermanentDockItem () { return false } - isDestroyed () { return this.destroyed } + getURI () { + return this.uri + } + getPath () { + return this.path + } + isEqual (other) { + return this.name === (other && other.name) + } + isPermanentDockItem () { + return false + } + isDestroyed () { + return this.destroyed + } serialize () { - return {deserializer: 'Item', name: this.name, uri: this.uri} + return { deserializer: 'Item', name: this.name, uri: this.uri } } copy () { @@ -63,22 +73,29 @@ describe('Pane', () => { }) function paneParams (params) { - return extend({ - applicationDelegate: atom.applicationDelegate, - config: atom.config, - deserializerManager: atom.deserializers, - notificationManager: atom.notifications - }, params) + return extend( + { + applicationDelegate: atom.applicationDelegate, + config: atom.config, + deserializerManager: atom.deserializers, + notificationManager: atom.notifications + }, + params + ) } describe('construction', () => { it('sets the active item to the first item', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B')] }) + ) expect(pane.getActiveItem()).toBe(pane.itemAtIndex(0)) }) it('compacts the items array', () => { - const pane = new Pane(paneParams({items: [undefined, new Item('A'), null, new Item('B')]})) + const pane = new Pane( + paneParams({ items: [undefined, new Item('A'), null, new Item('B')] }) + ) expect(pane.getItems().length).toBe(2) expect(pane.getActiveItem()).toBe(pane.itemAtIndex(0)) }) @@ -136,15 +153,19 @@ describe('Pane', () => { describe('::addItem(item, index)', () => { it('adds the item at the given index', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B')] }) + ) const [item1, item2] = pane.getItems() const item3 = new Item('C') - pane.addItem(item3, {index: 1}) + pane.addItem(item3, { index: 1 }) expect(pane.getItems()).toEqual([item1, item3, item2]) }) it('adds the item after the active item if no index is provided', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) const [item1, item2, item3] = pane.getItems() pane.activateItem(item2) const item4 = new Item('D') @@ -160,18 +181,23 @@ describe('Pane', () => { }) it('invokes ::onDidAddItem() observers', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B')] }) + ) const events = [] pane.onDidAddItem(event => events.push(event)) const item = new Item('C') - pane.addItem(item, {index: 1}) - expect(events).toEqual([{item, index: 1, moved: false}]) + pane.addItem(item, { index: 1 }) + expect(events).toEqual([{ item, index: 1, moved: false }]) }) it('throws an exception if the item is already present on a pane', () => { const item = new Item('A') - const container = new PaneContainer({config: atom.config, applicationDelegate: atom.applicationDelegate}) + const container = new PaneContainer({ + config: atom.config, + applicationDelegate: atom.applicationDelegate + }) const pane1 = container.getActivePane() pane1.addItem(item) const pane2 = pane1.splitRight() @@ -179,36 +205,36 @@ describe('Pane', () => { }) it("throws an exception if the item isn't an object", () => { - const pane = new Pane(paneParams({items: []})) + const pane = new Pane(paneParams({ items: [] })) expect(() => pane.addItem(null)).toThrow() expect(() => pane.addItem('foo')).toThrow() expect(() => pane.addItem(1)).toThrow() }) it('destroys any existing pending item', () => { - const pane = new Pane(paneParams({items: []})) + const pane = new Pane(paneParams({ items: [] })) const itemA = new Item('A') const itemB = new Item('B') const itemC = new Item('C') - pane.addItem(itemA, {pending: false}) - pane.addItem(itemB, {pending: true}) - pane.addItem(itemC, {pending: false}) + pane.addItem(itemA, { pending: false }) + pane.addItem(itemB, { pending: true }) + pane.addItem(itemC, { pending: false }) expect(itemB.isDestroyed()).toBe(true) }) it('adds the new item before destroying any existing pending item', () => { const eventOrder = [] - const pane = new Pane(paneParams({items: []})) + const pane = new Pane(paneParams({ items: [] })) const itemA = new Item('A') const itemB = new Item('B') - pane.addItem(itemA, {pending: true}) + pane.addItem(itemA, { pending: true }) - pane.onDidAddItem(function ({item}) { + pane.onDidAddItem(function ({ item }) { if (item === itemB) eventOrder.push('add') }) - pane.onDidRemoveItem(function ({item}) { + pane.onDidRemoveItem(function ({ item }) { if (item === itemA) eventOrder.push('remove') }) @@ -221,9 +247,11 @@ describe('Pane', () => { it('subscribes to be notified when item terminates its pending state', () => { const fakeDisposable = { dispose: () => {} } - const spy = jasmine.createSpy('onDidTerminatePendingState').andReturn((fakeDisposable)) + const spy = jasmine + .createSpy('onDidTerminatePendingState') + .andReturn(fakeDisposable) - const pane = new Pane(paneParams({items: []})) + const pane = new Pane(paneParams({ items: [] })) const item = { getTitle: () => '', onDidTerminatePendingState: spy @@ -235,9 +263,9 @@ describe('Pane', () => { it('subscribes to be notified when item is destroyed', () => { const fakeDisposable = { dispose: () => {} } - const spy = jasmine.createSpy('onDidDestroy').andReturn((fakeDisposable)) + const spy = jasmine.createSpy('onDidDestroy').andReturn(fakeDisposable) - const pane = new Pane(paneParams({items: []})) + const pane = new Pane(paneParams({ items: [] })) const item = { getTitle: () => '', onDidDestroy: spy @@ -251,7 +279,7 @@ describe('Pane', () => { beforeEach(() => spyOn(Grim, 'deprecate')) it('supports the older public API', () => { - const pane = new Pane(paneParams({items: []})) + const pane = new Pane(paneParams({ items: [] })) const itemA = new Item('A') const itemB = new Item('B') const itemC = new Item('C') @@ -262,9 +290,11 @@ describe('Pane', () => { }) it('shows a deprecation warning', () => { - const pane = new Pane(paneParams({items: []})) + const pane = new Pane(paneParams({ items: [] })) pane.addItem(new Item(), 2) - expect(Grim.deprecate).toHaveBeenCalledWith('Pane::addItem(item, 2) is deprecated in favor of Pane::addItem(item, {index: 2})') + expect(Grim.deprecate).toHaveBeenCalledWith( + 'Pane::addItem(item, 2) is deprecated in favor of Pane::addItem(item, {index: 2})' + ) }) }) }) @@ -273,7 +303,7 @@ describe('Pane', () => { let pane = null beforeEach(() => { - pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + pane = new Pane(paneParams({ items: [new Item('A'), new Item('B')] })) }) it('changes the active item to the current item', () => { @@ -306,16 +336,16 @@ describe('Pane', () => { }) it('replaces the active item if it is pending', () => { - pane.activateItem(itemC, {pending: true}) + pane.activateItem(itemC, { pending: true }) expect(pane.getItems().map(item => item.name)).toEqual(['A', 'C', 'B']) - pane.activateItem(itemD, {pending: true}) + pane.activateItem(itemD, { pending: true }) expect(pane.getItems().map(item => item.name)).toEqual(['A', 'D', 'B']) }) it('adds the item after the active item if it is not pending', () => { - pane.activateItem(itemC, {pending: true}) + pane.activateItem(itemC, { pending: true }) pane.activateItemAtIndex(2) - pane.activateItem(itemD, {pending: true}) + pane.activateItem(itemD, { pending: true }) expect(pane.getItems().map(item => item.name)).toEqual(['A', 'B', 'D']) }) }) @@ -369,14 +399,14 @@ describe('Pane', () => { const pendingSpy = jasmine.createSpy('onItemDidTerminatePendingState') const destroySpy = jasmine.createSpy('onWillDestroyItem') - await atom.workspace.open('sample.txt', {pending: true}).then(() => { + await atom.workspace.open('sample.txt', { pending: true }).then(() => { pane = atom.workspace.getActivePane() }) pane.onItemDidTerminatePendingState(pendingSpy) pane.onWillDestroyItem(destroySpy) - await atom.workspace.open('sample.js', {pending: true}) + await atom.workspace.open('sample.js', { pending: true }) expect(destroySpy).toHaveBeenCalled() expect(pendingSpy).not.toHaveBeenCalled() @@ -385,7 +415,17 @@ describe('Pane', () => { describe('::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()', () => { it('sets the active item to the next/previous item in the itemStack, looping around at either end', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C'), new Item('D'), new Item('E')]})) + const pane = new Pane( + paneParams({ + items: [ + new Item('A'), + new Item('B'), + new Item('C'), + new Item('D'), + new Item('E') + ] + }) + ) const [item1, item2, item3, item4, item5] = pane.getItems() pane.itemStack = [item3, item1, item2, item5, item4] @@ -416,7 +456,9 @@ describe('Pane', () => { describe('::activateNextItem() and ::activatePreviousItem()', () => { it('sets the active item to the next/previous item, looping around at either end', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) const [item1, item2, item3] = pane.getItems() expect(pane.getActiveItem()).toBe(item1) @@ -433,8 +475,10 @@ describe('Pane', () => { describe('::activateLastItem()', () => { it('sets the active item to the last item', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) - const [item1,, item3] = pane.getItems() + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) + const [item1, , item3] = pane.getItems() expect(pane.getActiveItem()).toBe(item1) pane.activateLastItem() @@ -444,7 +488,9 @@ describe('Pane', () => { describe('::moveItemRight() and ::moveItemLeft()', () => { it('moves the active item to the right and left, without looping around at either end', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) const [item1, item2, item3] = pane.getItems() pane.activateItemAtIndex(0) @@ -464,7 +510,9 @@ describe('Pane', () => { describe('::activateItemAtIndex(index)', () => { it('activates the item at the given index', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) const [item1, item2, item3] = pane.getItems() pane.activateItemAtIndex(2) expect(pane.getActiveItem()).toBe(item3) @@ -485,7 +533,9 @@ describe('Pane', () => { let pane, item1, item2, item3 beforeEach(() => { - pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) + pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) ;[item1, item2, item3] = pane.getItems() }) @@ -521,17 +571,17 @@ describe('Pane', () => { it('invokes ::onWillDestroyItem() and PaneContainer::onWillDestroyPaneItem observers before destroying the item', async () => { jasmine.useRealClock() - pane.container = new PaneContainer({config: atom.config, confirm}) + pane.container = new PaneContainer({ config: atom.config, confirm }) const events = [] - pane.onWillDestroyItem(async (event) => { + pane.onWillDestroyItem(async event => { expect(item2.isDestroyed()).toBe(false) await timeoutPromise(50) expect(item2.isDestroyed()).toBe(false) events.push(['will-destroy-item', event]) }) - pane.container.onWillDestroyPaneItem(async (event) => { + pane.container.onWillDestroyPaneItem(async event => { expect(item2.isDestroyed()).toBe(false) await timeoutPromise(50) expect(item2.isDestroyed()).toBe(false) @@ -541,8 +591,8 @@ describe('Pane', () => { await pane.destroyItem(item2) expect(item2.isDestroyed()).toBe(true) expect(events).toEqual([ - ['will-destroy-item', {item: item2, index: 1}], - ['will-destroy-pane-item', {item: item2, index: 1, pane}] + ['will-destroy-item', { item: item2, index: 1 }], + ['will-destroy-pane-item', { item: item2, index: 1, pane }] ]) }) @@ -550,14 +600,18 @@ describe('Pane', () => { const events = [] pane.onWillRemoveItem(event => events.push(event)) pane.destroyItem(item2) - expect(events).toEqual([{item: item2, index: 1, moved: false, destroyed: true}]) + expect(events).toEqual([ + { item: item2, index: 1, moved: false, destroyed: true } + ]) }) it('invokes ::onDidRemoveItem() observers', () => { const events = [] pane.onDidRemoveItem(event => events.push(event)) pane.destroyItem(item2) - expect(events).toEqual([{item: item2, index: 1, moved: false, destroyed: true}]) + expect(events).toEqual([ + { item: item2, index: 1, moved: false, destroyed: true } + ]) }) describe('when the destroyed item is the active item and is the first item', () => { @@ -608,7 +662,9 @@ describe('Pane', () => { itemURI = null - showSaveDialog.andCallFake((options, callback) => callback('/selected/path')) + showSaveDialog.andCallFake((options, callback) => + callback('/selected/path') + ) confirm.andCallFake((options, callback) => callback(0)) const success = await pane.destroyItem(item1) @@ -631,7 +687,7 @@ describe('Pane', () => { expect(item1.save).not.toHaveBeenCalled() expect(pane.getItems().includes(item1)).toBe(false) expect(item1.isDestroyed()).toBe(true) - expect(success).toBe(true); + expect(success).toBe(true) }) }) @@ -662,7 +718,9 @@ describe('Pane', () => { describe("when the 'core.destroyEmptyPanes' config option is false (the default)", () => { it('does not destroy the pane, but leaves it in place with empty items', () => { expect(atom.config.get('core.destroyEmptyPanes')).toBe(false) - for (let item of pane.getItems()) { pane.destroyItem(item) } + for (let item of pane.getItems()) { + pane.destroyItem(item) + } expect(pane.isDestroyed()).toBe(false) expect(pane.getActiveItem()).toBeUndefined() expect(() => pane.saveActiveItem()).not.toThrow() @@ -687,7 +745,7 @@ describe('Pane', () => { const success = await pane.destroyItem(item1) expect(pane.getItems().includes(item1)).toBe(true) expect(item1.isDestroyed()).toBe(false) - expect(success).toBe(false); + expect(success).toBe(false) }) it('destroy the item if force=true', async () => { @@ -702,7 +760,9 @@ describe('Pane', () => { describe('::destroyActiveItem()', () => { it('destroys the active item', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B')] }) + ) const activeItem = pane.getActiveItem() pane.destroyActiveItem() expect(activeItem.isDestroyed()).toBe(true) @@ -717,7 +777,9 @@ describe('Pane', () => { describe('::destroyItems()', () => { it('destroys all items', async () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) const [item1, item2, item3] = pane.getItems() await pane.destroyItems() @@ -730,7 +792,7 @@ describe('Pane', () => { describe('::observeItems()', () => { it('invokes the observer with all current and future items', () => { - const pane = new Pane(paneParams({items: [new Item(), new Item()]})) + const pane = new Pane(paneParams({ items: [new Item(), new Item()] })) const [item1, item2] = pane.getItems() const observed = [] @@ -745,8 +807,10 @@ describe('Pane', () => { describe('when an item emits a destroyed event', () => { it('removes it from the list of items', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) - const [item1,, item3] = pane.getItems() + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) + const [item1, , item3] = pane.getItems() pane.itemAtIndex(1).destroy() expect(pane.getItems()).toEqual([item1, item3]) }) @@ -754,7 +818,9 @@ describe('Pane', () => { describe('::destroyInactiveItems()', () => { it('destroys all items but the active item', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] }) + ) const [, item2] = pane.getItems() pane.activateItem(item2) pane.destroyInactiveItems() @@ -766,8 +832,10 @@ describe('Pane', () => { let pane beforeEach(() => { - pane = new Pane(paneParams({items: [new Item('A')]})) - showSaveDialog.andCallFake((options, callback) => callback('/selected/path')) + pane = new Pane(paneParams({ items: [new Item('A')] })) + showSaveDialog.andCallFake((options, callback) => + callback('/selected/path') + ) }) describe('when the active item has a uri', () => { @@ -797,7 +865,9 @@ describe('Pane', () => { pane.getActiveItem().saveAs = jasmine.createSpy('saveAs') await pane.saveActiveItem() expect(showSaveDialog.mostRecentCall.args[0]).toEqual({}) - expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path') + expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith( + '/selected/path' + ) }) }) @@ -826,14 +896,16 @@ describe('Pane', () => { return Promise.reject(error) } - waitsFor((done) => { - const subscription = atom.notifications.onDidAddNotification(function (notification) { - expect(notification.getType()).toBe('warning') - expect(notification.getMessage()).toContain('Permission denied') - expect(notification.getMessage()).toContain('/foo') - subscription.dispose() - done() - }) + waitsFor(done => { + const subscription = atom.notifications.onDidAddNotification( + function (notification) { + expect(notification.getType()).toBe('warning') + expect(notification.getMessage()).toContain('Permission denied') + expect(notification.getMessage()).toContain('/foo') + subscription.dispose() + done() + } + ) pane.saveActiveItem() }) }) @@ -848,14 +920,16 @@ describe('Pane', () => { throw error } - waitsFor((done) => { - const subscription = atom.notifications.onDidAddNotification(function (notification) { - expect(notification.getType()).toBe('warning') - expect(notification.getMessage()).toContain('Permission denied') - expect(notification.getMessage()).toContain('/foo') - subscription.dispose() - done() - }) + waitsFor(done => { + const subscription = atom.notifications.onDidAddNotification( + function (notification) { + expect(notification.getType()).toBe('warning') + expect(notification.getMessage()).toContain('Permission denied') + expect(notification.getMessage()).toContain('/foo') + subscription.dispose() + done() + } + ) pane.saveActiveItem() }) }) @@ -866,8 +940,10 @@ describe('Pane', () => { let pane = null beforeEach(() => { - pane = new Pane(paneParams({items: [new Item('A')]})) - showSaveDialog.andCallFake((options, callback) => callback('/selected/path')) + pane = new Pane(paneParams({ items: [new Item('A')] })) + showSaveDialog.andCallFake((options, callback) => + callback('/selected/path') + ) }) describe('when the current item has a saveAs method', () => { @@ -877,10 +953,16 @@ describe('Pane', () => { pane.getActiveItem().path = __filename pane.getActiveItem().saveAs = jasmine.createSpy('saveAs') pane.saveActiveItemAs() - expect(showSaveDialog.mostRecentCall.args[0]).toEqual({defaultPath: __filename}) + expect(showSaveDialog.mostRecentCall.args[0]).toEqual({ + defaultPath: __filename + }) - await conditionPromise(() => pane.getActiveItem().saveAs.callCount === 1) - expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path') + await conditionPromise( + () => pane.getActiveItem().saveAs.callCount === 1 + ) + expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith( + '/selected/path' + ) }) }) @@ -901,14 +983,16 @@ describe('Pane', () => { return Promise.reject(error) } - waitsFor((done) => { - const subscription = atom.notifications.onDidAddNotification(function (notification) { - expect(notification.getType()).toBe('warning') - expect(notification.getMessage()).toContain('Permission denied') - expect(notification.getMessage()).toContain('/foo') - subscription.dispose() - done() - }) + waitsFor(done => { + const subscription = atom.notifications.onDidAddNotification( + function (notification) { + expect(notification.getType()).toBe('warning') + expect(notification.getMessage()).toContain('Permission denied') + expect(notification.getMessage()).toContain('/foo') + subscription.dispose() + done() + } + ) pane.saveActiveItemAs() }) }) @@ -917,7 +1001,11 @@ describe('Pane', () => { describe('::itemForURI(uri)', () => { it('returns the item for which a call to .getURI() returns the given uri', () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')]})) + const pane = new Pane( + paneParams({ + items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')] + }) + ) const [item1, item2] = pane.getItems() item1.uri = 'a' item2.uri = 'b' @@ -931,7 +1019,11 @@ describe('Pane', () => { let pane, item1, item2, item3, item4 beforeEach(() => { - pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')]})) + pane = new Pane( + paneParams({ + items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')] + }) + ) ;[item1, item2, item3, item4] = pane.getItems() }) @@ -953,8 +1045,8 @@ describe('Pane', () => { pane.moveItem(item1, 2) pane.moveItem(item2, 3) expect(events).toEqual([ - {item: item1, oldIndex: 0, newIndex: 2}, - {item: item2, oldIndex: 0, newIndex: 3} + { item: item1, oldIndex: 0, newIndex: 2 }, + { item: item2, oldIndex: 0, newIndex: 3 } ]) }) }) @@ -964,12 +1056,12 @@ describe('Pane', () => { let item1, item2, item3, item4, item5 beforeEach(() => { - container = new PaneContainer({config: atom.config, confirm}) + container = new PaneContainer({ config: atom.config, confirm }) pane1 = container.getActivePane() pane1.addItems([new Item('A'), new Item('B'), new Item('C')]) - pane2 = pane1.splitRight({items: [new Item('D'), new Item('E')]}); - [item1, item2, item3] = pane1.getItems(); - [item4, item5] = pane2.getItems() + pane2 = pane1.splitRight({ items: [new Item('D'), new Item('E')] }) + ;[item1, item2, item3] = pane1.getItems() + ;[item4, item5] = pane2.getItems() }) it('moves the item to the given pane at the given index', () => { @@ -983,7 +1075,9 @@ describe('Pane', () => { pane1.onWillRemoveItem(event => events.push(event)) pane1.moveItemToPane(item2, pane2, 1) - expect(events).toEqual([{item: item2, index: 1, moved: true, destroyed: false}]) + expect(events).toEqual([ + { item: item2, index: 1, moved: true, destroyed: false } + ]) }) it('invokes ::onDidRemoveItem() observers', () => { @@ -991,7 +1085,9 @@ describe('Pane', () => { pane1.onDidRemoveItem(event => events.push(event)) pane1.moveItemToPane(item2, pane2, 1) - expect(events).toEqual([{item: item2, index: 1, moved: true, destroyed: false}]) + expect(events).toEqual([ + { item: item2, index: 1, moved: true, destroyed: false } + ]) }) it('does not invoke ::onDidAddPaneItem observers on the container', () => { @@ -1025,7 +1121,7 @@ describe('Pane', () => { describe('when the item being moved is pending', () => { it('is made permanent in the new pane', () => { const item6 = new Item('F') - pane1.addItem(item6, {pending: true}) + pane1.addItem(item6, { pending: true }) expect(pane1.getPendingItem()).toEqual(item6) pane1.moveItemToPane(item6, pane2, 0) expect(pane2.getPendingItem()).not.toEqual(item6) @@ -1035,7 +1131,7 @@ describe('Pane', () => { describe('when the target pane has a pending item', () => { it('does not destroy the pending item', () => { const item6 = new Item('F') - pane1.addItem(item6, {pending: true}) + pane1.addItem(item6, { pending: true }) expect(pane1.getPendingItem()).toEqual(item6) pane2.moveItemToPane(item5, pane1, 0) expect(pane1.getPendingItem()).toEqual(item6) @@ -1047,7 +1143,11 @@ describe('Pane', () => { let pane1, item1, container beforeEach(() => { - container = new PaneContainer({config: atom.config, confirm, deserializerManager: atom.deserializers}) + container = new PaneContainer({ + config: atom.config, + confirm, + deserializerManager: atom.deserializers + }) pane1 = container.getActivePane() item1 = new Item('A') pane1.addItem(item1) @@ -1056,8 +1156,8 @@ describe('Pane', () => { describe('::splitLeft(params)', () => { describe('when the parent is the container root', () => { it('replaces itself with a row and inserts a new pane to the left of itself', () => { - const pane2 = pane1.splitLeft({items: [new Item('B')]}) - const pane3 = pane1.splitLeft({items: [new Item('C')]}) + const pane2 = pane1.splitLeft({ items: [new Item('B')] }) + const pane3 = pane1.splitLeft({ items: [new Item('C')] }) expect(container.root.orientation).toBe('horizontal') expect(container.root.children).toEqual([pane2, pane3, pane1]) }) @@ -1065,20 +1165,20 @@ describe('Pane', () => { describe('when `moveActiveItem: true` is passed in the params', () => { it('moves the active item', () => { - const pane2 = pane1.splitLeft({moveActiveItem: true}) + const pane2 = pane1.splitLeft({ moveActiveItem: true }) expect(pane2.getActiveItem()).toBe(item1) }) }) describe('when `copyActiveItem: true` is passed in the params', () => { it('duplicates the active item', () => { - const pane2 = pane1.splitLeft({copyActiveItem: true}) + const pane2 = pane1.splitLeft({ copyActiveItem: true }) expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem()) }) it("does nothing if the active item doesn't implement .copy()", () => { item1.copy = null - const pane2 = pane1.splitLeft({copyActiveItem: true}) + const pane2 = pane1.splitLeft({ copyActiveItem: true }) expect(pane2.getActiveItem()).toBeUndefined() }) }) @@ -1086,8 +1186,8 @@ describe('Pane', () => { describe('when the parent is a column', () => { it('replaces itself with a row and inserts a new pane to the left of itself', () => { pane1.splitDown() - const pane2 = pane1.splitLeft({items: [new Item('B')]}) - const pane3 = pane1.splitLeft({items: [new Item('C')]}) + const pane2 = pane1.splitLeft({ items: [new Item('B')] }) + const pane3 = pane1.splitLeft({ items: [new Item('C')] }) const row = container.root.children[0] expect(row.orientation).toBe('horizontal') expect(row.children).toEqual([pane2, pane3, pane1]) @@ -1098,8 +1198,8 @@ describe('Pane', () => { describe('::splitRight(params)', () => { describe('when the parent is the container root', () => { it('replaces itself with a row and inserts a new pane to the right of itself', () => { - const pane2 = pane1.splitRight({items: [new Item('B')]}) - const pane3 = pane1.splitRight({items: [new Item('C')]}) + const pane2 = pane1.splitRight({ items: [new Item('B')] }) + const pane3 = pane1.splitRight({ items: [new Item('C')] }) expect(container.root.orientation).toBe('horizontal') expect(container.root.children).toEqual([pane1, pane3, pane2]) }) @@ -1107,14 +1207,14 @@ describe('Pane', () => { describe('when `moveActiveItem: true` is passed in the params', () => { it('moves the active item', () => { - const pane2 = pane1.splitRight({moveActiveItem: true}) + const pane2 = pane1.splitRight({ moveActiveItem: true }) expect(pane2.getActiveItem()).toBe(item1) }) }) describe('when `copyActiveItem: true` is passed in the params', () => { it('duplicates the active item', () => { - const pane2 = pane1.splitRight({copyActiveItem: true}) + const pane2 = pane1.splitRight({ copyActiveItem: true }) expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem()) }) }) @@ -1122,8 +1222,8 @@ describe('Pane', () => { describe('when the parent is a column', () => { it('replaces itself with a row and inserts a new pane to the right of itself', () => { pane1.splitDown() - const pane2 = pane1.splitRight({items: [new Item('B')]}) - const pane3 = pane1.splitRight({items: [new Item('C')]}) + const pane2 = pane1.splitRight({ items: [new Item('B')] }) + const pane3 = pane1.splitRight({ items: [new Item('C')] }) const row = container.root.children[0] expect(row.orientation).toBe('horizontal') expect(row.children).toEqual([pane1, pane3, pane2]) @@ -1134,8 +1234,8 @@ describe('Pane', () => { describe('::splitUp(params)', () => { describe('when the parent is the container root', () => { it('replaces itself with a column and inserts a new pane above itself', () => { - const pane2 = pane1.splitUp({items: [new Item('B')]}) - const pane3 = pane1.splitUp({items: [new Item('C')]}) + const pane2 = pane1.splitUp({ items: [new Item('B')] }) + const pane3 = pane1.splitUp({ items: [new Item('C')] }) expect(container.root.orientation).toBe('vertical') expect(container.root.children).toEqual([pane2, pane3, pane1]) }) @@ -1143,14 +1243,14 @@ describe('Pane', () => { describe('when `moveActiveItem: true` is passed in the params', () => { it('moves the active item', () => { - const pane2 = pane1.splitUp({moveActiveItem: true}) + const pane2 = pane1.splitUp({ moveActiveItem: true }) expect(pane2.getActiveItem()).toBe(item1) }) }) describe('when `copyActiveItem: true` is passed in the params', () => { it('duplicates the active item', () => { - const pane2 = pane1.splitUp({copyActiveItem: true}) + const pane2 = pane1.splitUp({ copyActiveItem: true }) expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem()) }) }) @@ -1158,8 +1258,8 @@ describe('Pane', () => { describe('when the parent is a row', () => { it('replaces itself with a column and inserts a new pane above itself', () => { pane1.splitRight() - const pane2 = pane1.splitUp({items: [new Item('B')]}) - const pane3 = pane1.splitUp({items: [new Item('C')]}) + const pane2 = pane1.splitUp({ items: [new Item('B')] }) + const pane3 = pane1.splitUp({ items: [new Item('C')] }) const column = container.root.children[0] expect(column.orientation).toBe('vertical') expect(column.children).toEqual([pane2, pane3, pane1]) @@ -1170,8 +1270,8 @@ describe('Pane', () => { describe('::splitDown(params)', () => { describe('when the parent is the container root', () => { it('replaces itself with a column and inserts a new pane below itself', () => { - const pane2 = pane1.splitDown({items: [new Item('B')]}) - const pane3 = pane1.splitDown({items: [new Item('C')]}) + const pane2 = pane1.splitDown({ items: [new Item('B')] }) + const pane3 = pane1.splitDown({ items: [new Item('C')] }) expect(container.root.orientation).toBe('vertical') expect(container.root.children).toEqual([pane1, pane3, pane2]) }) @@ -1179,14 +1279,14 @@ describe('Pane', () => { describe('when `moveActiveItem: true` is passed in the params', () => { it('moves the active item', () => { - const pane2 = pane1.splitDown({moveActiveItem: true}) + const pane2 = pane1.splitDown({ moveActiveItem: true }) expect(pane2.getActiveItem()).toBe(item1) }) }) describe('when `copyActiveItem: true` is passed in the params', () => { it('duplicates the active item', () => { - const pane2 = pane1.splitDown({copyActiveItem: true}) + const pane2 = pane1.splitDown({ copyActiveItem: true }) expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem()) }) }) @@ -1194,8 +1294,8 @@ describe('Pane', () => { describe('when the parent is a row', () => { it('replaces itself with a column and inserts a new pane below itself', () => { pane1.splitRight() - const pane2 = pane1.splitDown({items: [new Item('B')]}) - const pane3 = pane1.splitDown({items: [new Item('C')]}) + const pane2 = pane1.splitDown({ items: [new Item('B')] }) + const pane3 = pane1.splitDown({ items: [new Item('C')] }) const column = container.root.children[0] expect(column.orientation).toBe('vertical') expect(column.children).toEqual([pane1, pane3, pane2]) @@ -1209,7 +1309,9 @@ describe('Pane', () => { pane1.destroyItem(item1) expect(pane1.getActiveItem()).toBe(undefined) - const pane2 = pane1.split('horizontal', 'before', {moveActiveItem: true}) + const pane2 = pane1.split('horizontal', 'before', { + moveActiveItem: true + }) expect(container.root.children).toEqual([pane2, pane1]) expect(pane2.getActiveItem()).toBe(undefined) @@ -1221,7 +1323,9 @@ describe('Pane', () => { pane1.destroyItem(item1) expect(pane1.getActiveItem()).toBe(undefined) - const pane2 = pane1.split('horizontal', 'before', {copyActiveItem: true}) + const pane2 = pane1.split('horizontal', 'before', { + copyActiveItem: true + }) expect(container.root.children).toEqual([pane2, pane1]) expect(pane2.getActiveItem()).toBe(undefined) @@ -1239,7 +1343,9 @@ describe('Pane', () => { describe('::close()', () => { it('prompts to save unsaved items before destroying the pane', async () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B')] }) + ) const [item1] = pane.getItems() item1.shouldPromptToSave = () => true @@ -1254,7 +1360,9 @@ describe('Pane', () => { }) it('does not destroy the pane if the user clicks cancel', async () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B')] }) + ) const [item1] = pane.getItems() item1.shouldPromptToSave = () => true @@ -1270,7 +1378,9 @@ describe('Pane', () => { }) it('does not destroy the pane if the user starts to save but then does not choose a path', async () => { - const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]})) + const pane = new Pane( + paneParams({ items: [new Item('A'), new Item('B')] }) + ) const [item1] = pane.getItems() item1.shouldPromptToSave = () => true @@ -1290,8 +1400,12 @@ describe('Pane', () => { let pane, item1 beforeEach(() => { - pane = new Pane({items: [new Item('A'), new Item('B')], applicationDelegate: atom.applicationDelegate, config: atom.config}); - [item1] = pane.getItems() + pane = new Pane({ + items: [new Item('A'), new Item('B')], + applicationDelegate: atom.applicationDelegate, + config: atom.config + }) + ;[item1] = pane.getItems() item1.shouldPromptToSave = () => true item1.getURI = () => '/test/path' @@ -1336,7 +1450,9 @@ describe('Pane', () => { await pane.close() expect(atom.applicationDelegate.confirm).toHaveBeenCalled() expect(confirmations).toBe(2) - expect(atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0]).toEqual({}) + expect( + atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0] + ).toEqual({}) expect(item1.save).toHaveBeenCalled() expect(item1.saveAs).toHaveBeenCalled() expect(pane.isDestroyed()).toBe(true) @@ -1365,7 +1481,9 @@ describe('Pane', () => { await pane.close() expect(atom.applicationDelegate.confirm).toHaveBeenCalled() expect(confirmations).toBe(3) - expect(atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0]).toEqual({}) + expect( + atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0] + ).toEqual({}) expect(item1.save).toHaveBeenCalled() expect(item1.saveAs).toHaveBeenCalled() expect(pane.isDestroyed()).toBe(true) @@ -1377,7 +1495,7 @@ describe('Pane', () => { let container, pane1, pane2 beforeEach(() => { - container = new PaneContainer({config: atom.config, confirm}) + container = new PaneContainer({ config: atom.config, confirm }) pane1 = container.root pane1.addItems([new Item('A'), new Item('B')]) pane2 = pane1.splitRight() @@ -1386,7 +1504,7 @@ describe('Pane', () => { it('invokes ::onWillDestroy observers before destroying items', () => { let itemsDestroyed = null pane1.onWillDestroy(() => { - itemsDestroyed = (pane1.getItems().map((item) => item.isDestroyed())) + itemsDestroyed = pane1.getItems().map(item => item.isDestroyed()) }) pane1.destroy() expect(itemsDestroyed).toEqual([false, false]) @@ -1435,7 +1553,7 @@ describe('Pane', () => { let editor1, pane, eventCount beforeEach(async () => { - editor1 = await atom.workspace.open('sample.txt', {pending: true}) + editor1 = await atom.workspace.open('sample.txt', { pending: true }) pane = atom.workspace.getActivePane() eventCount = 0 editor1.onDidTerminatePendingState(() => eventCount++) @@ -1458,7 +1576,7 @@ describe('Pane', () => { }) it('terminates pending state when buffer is changed', () => { - editor1.insertText('I\'ll be back!') + editor1.insertText("I'll be back!") advanceClock(editor1.getBuffer().stoppedChangingDelay) expect(pane.getPendingItem()).toBeNull() @@ -1498,10 +1616,12 @@ describe('Pane', () => { let pane = null beforeEach(() => { - pane = new Pane(paneParams({ - items: [new Item('A', 'a'), new Item('B', 'b'), new Item('C', 'c')], - flexScale: 2 - })) + pane = new Pane( + paneParams({ + items: [new Item('A', 'a'), new Item('B', 'b'), new Item('C', 'c')], + flexScale: 2 + }) + ) }) it('can serialize and deserialize the pane and all its items', () => { @@ -1524,7 +1644,7 @@ describe('Pane', () => { it("restores the correct item when it doesn't implement getURI() and some items weren't deserialized", () => { const unserializable = {} - pane.addItem(unserializable, {index: 0}) + pane.addItem(unserializable, { index: 0 }) pane.items[2].getURI = null pane.activateItemAtIndex(2) const newPane = Pane.deserialize(pane.serialize(), atom) diff --git a/spec/panel-container-element-spec.js b/spec/panel-container-element-spec.js index 5634883df..039061ae8 100644 --- a/spec/panel-container-element-spec.js +++ b/spec/panel-container-element-spec.js @@ -6,8 +6,7 @@ const PanelContainer = require('../src/panel-container') describe('PanelContainerElement', () => { let jasmineContent, element, container - class TestPanelContainerItem { - } + class TestPanelContainerItem {} class TestPanelContainerItemElement_ extends HTMLElement { createdCallback () { @@ -17,23 +16,25 @@ describe('PanelContainerElement', () => { this.model = model return this } - focus() {} + focus () {} } const TestPanelContainerItemElement = document.registerElement( 'atom-test-container-item-element', - {prototype: TestPanelContainerItemElement_.prototype} + { prototype: TestPanelContainerItemElement_.prototype } ) beforeEach(() => { jasmineContent = document.body.querySelector('#jasmine-content') - atom.views.addViewProvider( - TestPanelContainerItem, - model => new TestPanelContainerItemElement().initialize(model) + atom.views.addViewProvider(TestPanelContainerItem, model => + new TestPanelContainerItemElement().initialize(model) ) - container = new PanelContainer({viewRegistry: atom.views, location: 'left'}) + container = new PanelContainer({ + viewRegistry: atom.views, + location: 'left' + }) element = container.getElement() jasmineContent.appendChild(element) }) @@ -50,9 +51,18 @@ describe('PanelContainerElement', () => { describe('adding and removing panels', () => { it('allows panels to be inserted at any position', () => { - const panel1 = new Panel({item: new TestPanelContainerItem(), priority: 10}, atom.views) - const panel2 = new Panel({item: new TestPanelContainerItem(), priority: 5}, atom.views) - const panel3 = new Panel({item: new TestPanelContainerItem(), priority: 8}, atom.views) + const panel1 = new Panel( + { item: new TestPanelContainerItem(), priority: 10 }, + atom.views + ) + const panel2 = new Panel( + { item: new TestPanelContainerItem(), priority: 5 }, + atom.views + ) + const panel3 = new Panel( + { item: new TestPanelContainerItem(), priority: 8 }, + atom.views + ) container.addPanel(panel1) container.addPanel(panel2) @@ -67,7 +77,10 @@ describe('PanelContainerElement', () => { it('adds atom-panel elements when a new panel is added to the container; removes them when the panels are destroyed', () => { expect(element.childNodes.length).toBe(0) - const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views) + const panel1 = new Panel( + { item: new TestPanelContainerItem() }, + atom.views + ) container.addPanel(panel1) expect(element.childNodes.length).toBe(1) expect(element.childNodes[0]).toHaveClass('left') @@ -76,7 +89,10 @@ describe('PanelContainerElement', () => { expect(element.childNodes[0].tagName).toBe('ATOM-PANEL') - const panel2 = new Panel({item: new TestPanelContainerItem()}, atom.views) + const panel2 = new Panel( + { item: new TestPanelContainerItem() }, + atom.views + ) container.addPanel(panel2) expect(element.childNodes.length).toBe(2) @@ -88,12 +104,14 @@ describe('PanelContainerElement', () => { panel2.destroy() expect(element.childNodes.length).toBe(0) - }) - ) + })) describe('when the container is at the bottom location', () => { beforeEach(() => { - container = new PanelContainer({viewRegistry: atom.views, location: 'bottom'}) + container = new PanelContainer({ + viewRegistry: atom.views, + location: 'bottom' + }) element = container.getElement() jasmineContent.appendChild(element) }) @@ -101,7 +119,10 @@ describe('PanelContainerElement', () => { it('adds atom-panel elements when a new panel is added to the container; removes them when the panels are destroyed', () => { expect(element.childNodes.length).toBe(0) - const panel1 = new Panel({item: new TestPanelContainerItem(), className: 'one'}, atom.views) + const panel1 = new Panel( + { item: new TestPanelContainerItem(), className: 'one' }, + atom.views + ) container.addPanel(panel1) expect(element.childNodes.length).toBe(1) expect(element.childNodes[0]).toHaveClass('bottom') @@ -110,7 +131,10 @@ describe('PanelContainerElement', () => { expect(element.childNodes[0].tagName).toBe('ATOM-PANEL') expect(panel1.getElement()).toHaveClass('one') - const panel2 = new Panel({item: new TestPanelContainerItem(), className: 'two'}, atom.views) + const panel2 = new Panel( + { item: new TestPanelContainerItem(), className: 'two' }, + atom.views + ) container.addPanel(panel2) expect(element.childNodes.length).toBe(2) expect(panel2.getElement()).toHaveClass('two') @@ -126,18 +150,27 @@ describe('PanelContainerElement', () => { describe('when the container is modal', () => { beforeEach(() => { - container = new PanelContainer({viewRegistry: atom.views, location: 'modal'}) + container = new PanelContainer({ + viewRegistry: atom.views, + location: 'modal' + }) element = container.getElement() jasmineContent.appendChild(element) }) it('allows only one panel to be visible at a time', () => { - const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views) + const panel1 = new Panel( + { item: new TestPanelContainerItem() }, + atom.views + ) container.addPanel(panel1) expect(panel1.getElement().style.display).not.toBe('none') - const panel2 = new Panel({item: new TestPanelContainerItem()}, atom.views) + const panel2 = new Panel( + { item: new TestPanelContainerItem() }, + atom.views + ) container.addPanel(panel2) expect(panel1.getElement().style.display).toBe('none') @@ -150,7 +183,10 @@ describe('PanelContainerElement', () => { }) it("adds the 'modal' class to panels", () => { - const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views) + const panel1 = new Panel( + { item: new TestPanelContainerItem() }, + atom.views + ) container.addPanel(panel1) expect(panel1.getElement()).toHaveClass('modal') @@ -161,8 +197,8 @@ describe('PanelContainerElement', () => { expect(panel1.getElement()).toHaveClass('from-top') }) - describe("autoFocus", () => { - function createPanel() { + describe('autoFocus', () => { + function createPanel () { const panel = new Panel( { item: new TestPanelContainerItem(), @@ -176,7 +212,7 @@ describe('PanelContainerElement', () => { return panel } - it("focuses the first tabbable item if available", () => { + it('focuses the first tabbable item if available', () => { const panel = createPanel() const panelEl = panel.getElement() const inputEl = document.createElement('input') @@ -188,7 +224,7 @@ describe('PanelContainerElement', () => { expect(document.activeElement).toBe(inputEl) }) - it("focuses the entire panel item when no tabbable item is available and the panel is focusable", () => { + it('focuses the entire panel item when no tabbable item is available and the panel is focusable', () => { const panel = createPanel() const panelEl = panel.getElement() @@ -197,7 +233,7 @@ describe('PanelContainerElement', () => { expect(panelEl.focus).toHaveBeenCalled() }) - it("returns focus to the original activeElement", () => { + it('returns focus to the original activeElement', () => { const panel = createPanel() const previousActiveElement = document.activeElement const panelEl = panel.getElement() diff --git a/spec/panel-container-spec.js b/spec/panel-container-spec.js index f5537f0b8..35286cd36 100644 --- a/spec/panel-container-spec.js +++ b/spec/panel-container-spec.js @@ -6,11 +6,10 @@ const PanelContainer = require('../src/panel-container') describe('PanelContainer', () => { let container - class TestPanelItem { - } + class TestPanelItem {} beforeEach(() => { - container = new PanelContainer({viewRegistry: atom.views}) + container = new PanelContainer({ viewRegistry: atom.views }) }) describe('::addPanel(panel)', () => { @@ -18,13 +17,13 @@ describe('PanelContainer', () => { const addPanelSpy = jasmine.createSpy() container.onDidAddPanel(addPanelSpy) - const panel1 = new Panel({item: new TestPanelItem()}, atom.views) + const panel1 = new Panel({ item: new TestPanelItem() }, atom.views) container.addPanel(panel1) - expect(addPanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel: panel1, index: 0 }) - const panel2 = new Panel({item: new TestPanelItem()}, atom.views) + const panel2 = new Panel({ item: new TestPanelItem() }, atom.views) container.addPanel(panel2) - expect(addPanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel: panel2, index: 1 }) }) }) @@ -33,18 +32,18 @@ describe('PanelContainer', () => { const removePanelSpy = jasmine.createSpy() container.onDidRemovePanel(removePanelSpy) - const panel1 = new Panel({item: new TestPanelItem()}, atom.views) + const panel1 = new Panel({ item: new TestPanelItem() }, atom.views) container.addPanel(panel1) - const panel2 = new Panel({item: new TestPanelItem()}, atom.views) + const panel2 = new Panel({ item: new TestPanelItem() }, atom.views) container.addPanel(panel2) expect(removePanelSpy).not.toHaveBeenCalled() panel2.destroy() - expect(removePanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1}) + expect(removePanelSpy).toHaveBeenCalledWith({ panel: panel2, index: 1 }) panel1.destroy() - expect(removePanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0}) + expect(removePanelSpy).toHaveBeenCalledWith({ panel: panel1, index: 0 }) }) }) @@ -52,12 +51,16 @@ describe('PanelContainer', () => { it('destroys the container and all of its panels', () => { const destroyedPanels = [] - const panel1 = new Panel({item: new TestPanelItem()}, atom.views) - panel1.onDidDestroy(() => { destroyedPanels.push(panel1) }) + const panel1 = new Panel({ item: new TestPanelItem() }, atom.views) + panel1.onDidDestroy(() => { + destroyedPanels.push(panel1) + }) container.addPanel(panel1) - const panel2 = new Panel({item: new TestPanelItem()}, atom.views) - panel2.onDidDestroy(() => { destroyedPanels.push(panel2) }) + const panel2 = new Panel({ item: new TestPanelItem() }, atom.views) + panel2.onDidDestroy(() => { + destroyedPanels.push(panel2) + }) container.addPanel(panel2) container.destroy() @@ -72,8 +75,8 @@ describe('PanelContainer', () => { let initialPanel beforeEach(() => { // 'left' logic is the same as 'top' - container = new PanelContainer({location: 'left'}) - initialPanel = new Panel({item: new TestPanelItem()}, atom.views) + container = new PanelContainer({ location: 'left' }) + initialPanel = new Panel({ item: new TestPanelItem() }, atom.views) container.addPanel(initialPanel) }) @@ -81,10 +84,13 @@ describe('PanelContainer', () => { it('is inserted at the beginning of the list', () => { const addPanelSpy = jasmine.createSpy() container.onDidAddPanel(addPanelSpy) - const panel = new Panel({item: new TestPanelItem(), priority: 0}, atom.views) + const panel = new Panel( + { item: new TestPanelItem(), priority: 0 }, + atom.views + ) container.addPanel(panel) - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) expect(container.getPanels()[0]).toBe(panel) }) }) @@ -92,14 +98,20 @@ describe('PanelContainer', () => { describe('when a panel with priority between two other panels is added', () => { it('is inserted at the between the two panels', () => { const addPanelSpy = jasmine.createSpy() - let panel = new Panel({item: new TestPanelItem(), priority: 1000}, atom.views) + let panel = new Panel( + { item: new TestPanelItem(), priority: 1000 }, + atom.views + ) container.addPanel(panel) container.onDidAddPanel(addPanelSpy) - panel = new Panel({item: new TestPanelItem(), priority: 101}, atom.views) + panel = new Panel( + { item: new TestPanelItem(), priority: 101 }, + atom.views + ) container.addPanel(panel) - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 1}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 1 }) expect(container.getPanels()[1]).toBe(panel) }) }) @@ -109,8 +121,8 @@ describe('PanelContainer', () => { let initialPanel beforeEach(() => { // 'bottom' logic is the same as 'right' - container = new PanelContainer({location: 'right'}) - initialPanel = new Panel({item: new TestPanelItem()}, atom.views) + container = new PanelContainer({ location: 'right' }) + initialPanel = new Panel({ item: new TestPanelItem() }, atom.views) container.addPanel(initialPanel) }) @@ -118,10 +130,13 @@ describe('PanelContainer', () => { it('is inserted at the beginning of the list', () => { const addPanelSpy = jasmine.createSpy() container.onDidAddPanel(addPanelSpy) - const panel = new Panel({item: new TestPanelItem(), priority: 1000}, atom.views) + const panel = new Panel( + { item: new TestPanelItem(), priority: 1000 }, + atom.views + ) container.addPanel(panel) - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) expect(container.getPanels()[0]).toBe(panel) }) }) @@ -130,10 +145,13 @@ describe('PanelContainer', () => { it('is inserted at the end of the list', () => { const addPanelSpy = jasmine.createSpy() container.onDidAddPanel(addPanelSpy) - const panel = new Panel({item: new TestPanelItem(), priority: 0}, atom.views) + const panel = new Panel( + { item: new TestPanelItem(), priority: 0 }, + atom.views + ) container.addPanel(panel) - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 1}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 1 }) expect(container.getPanels()[1]).toBe(panel) }) }) diff --git a/spec/panel-spec.js b/spec/panel-spec.js index 8df51a2fb..5165e550c 100644 --- a/spec/panel-spec.js +++ b/spec/panel-spec.js @@ -12,8 +12,8 @@ describe('Panel', () => { } } - it('adds the item\'s element as a child of the panel', () => { - const panel = new Panel({item: new TestPanelItem()}, atom.views) + it("adds the item's element as a child of the panel", () => { + const panel = new Panel({ item: new TestPanelItem() }, atom.views) const element = panel.getElement() expect(element.tagName.toLowerCase()).toBe('atom-panel') expect(element.firstChild).toBe(panel.getItem().getElement()) @@ -21,7 +21,7 @@ describe('Panel', () => { describe('destroying the panel', () => { it('removes the element when the panel is destroyed', () => { - const panel = new Panel({item: new TestPanelItem()}, atom.views) + const panel = new Panel({ item: new TestPanelItem() }, atom.views) const element = panel.getElement() const jasmineContent = document.getElementById('jasmine-content') jasmineContent.appendChild(element) @@ -33,7 +33,7 @@ describe('Panel', () => { it('does not try to remove the element twice', () => { const item = new TestPanelItem() - const panel = new Panel({item}, atom.views) + const panel = new Panel({ item }, atom.views) const element = panel.getElement() const jasmineContent = document.getElementById('jasmine-content') jasmineContent.appendChild(element) @@ -52,7 +52,7 @@ describe('Panel', () => { describe('changing panel visibility', () => { it('notifies observers added with onDidChangeVisible', () => { - const panel = new Panel({item: new TestPanelItem()}, atom.views) + const panel = new Panel({ item: new TestPanelItem() }, atom.views) const spy = jasmine.createSpy() panel.onDidChangeVisible(spy) @@ -72,13 +72,16 @@ describe('Panel', () => { }) it('initially renders panel created with visible: false', () => { - const panel = new Panel({visible: false, item: new TestPanelItem()}, atom.views) + const panel = new Panel( + { visible: false, item: new TestPanelItem() }, + atom.views + ) const element = panel.getElement() expect(element.style.display).toBe('none') }) it('hides and shows the panel element when Panel::hide() and Panel::show() are called', () => { - const panel = new Panel({item: new TestPanelItem()}, atom.views) + const panel = new Panel({ item: new TestPanelItem() }, atom.views) const element = panel.getElement() expect(element.style.display).not.toBe('none') @@ -92,7 +95,10 @@ describe('Panel', () => { describe('when a class name is specified', () => { it('initially renders panel created with visible: false', () => { - const panel = new Panel({className: 'some classes', item: new TestPanelItem()}, atom.views) + const panel = new Panel( + { className: 'some classes', item: new TestPanelItem() }, + atom.views + ) const element = panel.getElement() expect(element).toHaveClass('some') @@ -102,7 +108,7 @@ describe('Panel', () => { describe('creating an atom-panel via markup', () => { it('does not throw an error', () => { - const element = document.createElement('atom-panel') + document.createElement('atom-panel') }) }) }) diff --git a/spec/path-watcher-spec.js b/spec/path-watcher-spec.js index ae5cd7d75..ec1993f96 100644 --- a/spec/path-watcher-spec.js +++ b/spec/path-watcher-spec.js @@ -1,17 +1,21 @@ /** @babel */ -import {it, beforeEach, afterEach, promisifySome} from './async-spec-helpers' -import tempCb from 'temp' -import fsCb from 'fs-plus' +import temp from 'temp' +import fs from 'fs-plus' import path from 'path' +import { promisify } from 'util' -import {CompositeDisposable} from 'event-kit' -import {watchPath, stopAllWatchers} from '../src/path-watcher' +import { CompositeDisposable } from 'event-kit' +import { watchPath, stopAllWatchers } from '../src/path-watcher' -tempCb.track() +temp.track() -const fs = promisifySome(fsCb, ['writeFile', 'mkdir', 'symlink', 'appendFile', 'realpath']) -const temp = promisifySome(tempCb, ['mkdir']) +const writeFile = promisify(fs.writeFile) +const mkdir = promisify(fs.mkdir) +const appendFile = promisify(fs.appendFile) +const realpath = promisify(fs.realpath) + +const tempMkdir = promisify(temp.mkdir) describe('watchPath', function () { let subs @@ -49,14 +53,14 @@ describe('watchPath', function () { describe('watchPath()', function () { it('resolves the returned promise when the watcher begins listening', async function () { - const rootDir = await temp.mkdir('atom-fsmanager-test-') + const rootDir = await tempMkdir('atom-fsmanager-test-') const watcher = await watchPath(rootDir, {}, () => {}) expect(watcher.constructor.name).toBe('PathWatcher') }) it('reuses an existing native watcher and resolves getStartPromise immediately if attached to a running watcher', async function () { - const rootDir = await temp.mkdir('atom-fsmanager-test-') + const rootDir = await tempMkdir('atom-fsmanager-test-') const watcher0 = await watchPath(rootDir, {}, () => {}) const watcher1 = await watchPath(rootDir, {}, () => {}) @@ -65,7 +69,7 @@ describe('watchPath', function () { }) it("reuses existing native watchers even while they're still starting", async function () { - const rootDir = await temp.mkdir('atom-fsmanager-test-') + const rootDir = await tempMkdir('atom-fsmanager-test-') const [watcher0, watcher1] = await Promise.all([ watchPath(rootDir, {}, () => {}), @@ -75,7 +79,7 @@ describe('watchPath', function () { }) it("doesn't attach new watchers to a native watcher that's stopping", async function () { - const rootDir = await temp.mkdir('atom-fsmanager-test-') + const rootDir = await tempMkdir('atom-fsmanager-test-') const watcher0 = await watchPath(rootDir, {}, () => {}) const native0 = watcher0.native @@ -87,12 +91,12 @@ describe('watchPath', function () { }) it('reuses an existing native watcher on a parent directory and filters events', async function () { - const rootDir = await temp.mkdir('atom-fsmanager-test-').then(fs.realpath) + const rootDir = await tempMkdir('atom-fsmanager-test-').then(realpath) const rootFile = path.join(rootDir, 'rootfile.txt') const subDir = path.join(rootDir, 'subdir') const subFile = path.join(subDir, 'subfile.txt') - await fs.mkdir(subDir) + await mkdir(subDir) // Keep the watchers alive with an undisposed subscription const rootWatcher = await watchPath(rootDir, {}, () => {}) @@ -105,16 +109,17 @@ describe('watchPath', function () { waitForChanges(rootWatcher, subFile), waitForChanges(childWatcher, subFile) ]) - await fs.writeFile(subFile, 'subfile\n', {encoding: 'utf8'}) + await writeFile(subFile, 'subfile\n', { encoding: 'utf8' }) await firstChanges const nextRootEvent = waitForChanges(rootWatcher, rootFile) - await fs.writeFile(rootFile, 'rootfile\n', {encoding: 'utf8'}) + await writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' }) await nextRootEvent }) it('adopts existing child watchers and filters events appropriately to them', async function () { - const parentDir = await temp.mkdir('atom-fsmanager-test-').then(fs.realpath) + const parentDir = await tempMkdir('atom-fsmanager-test-') + .then(realpath) // Create the directory tree const rootFile = path.join(parentDir, 'rootfile.txt') @@ -123,12 +128,12 @@ describe('watchPath', function () { const subDir1 = path.join(parentDir, 'subdir1') const subFile1 = path.join(subDir1, 'subfile1.txt') - await fs.mkdir(subDir0) - await fs.mkdir(subDir1) + await mkdir(subDir0) + await mkdir(subDir1) await Promise.all([ - fs.writeFile(rootFile, 'rootfile\n', {encoding: 'utf8'}), - fs.writeFile(subFile0, 'subfile 0\n', {encoding: 'utf8'}), - fs.writeFile(subFile1, 'subfile 1\n', {encoding: 'utf8'}) + writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' }), + writeFile(subFile0, 'subfile 0\n', { encoding: 'utf8' }), + writeFile(subFile1, 'subfile 1\n', { encoding: 'utf8' }) ]) // Begin the child watchers and keep them alive @@ -142,16 +147,21 @@ describe('watchPath', function () { // Create the parent watcher const parentWatcher = await watchPath(parentDir, {}, () => {}) - const parentWatcherChanges = waitForChanges(parentWatcher, rootFile, subFile0, subFile1) + const parentWatcherChanges = waitForChanges( + parentWatcher, + rootFile, + subFile0, + subFile1 + ) expect(subWatcher0.native).toBe(parentWatcher.native) expect(subWatcher1.native).toBe(parentWatcher.native) // Ensure events are filtered correctly await Promise.all([ - fs.appendFile(rootFile, 'change\n', {encoding: 'utf8'}), - fs.appendFile(subFile0, 'change\n', {encoding: 'utf8'}), - fs.appendFile(subFile1, 'change\n', {encoding: 'utf8'}) + appendFile(rootFile, 'change\n', { encoding: 'utf8' }), + appendFile(subFile0, 'change\n', { encoding: 'utf8' }), + appendFile(subFile1, 'change\n', { encoding: 'utf8' }) ]) await Promise.all([ diff --git a/spec/project-spec.js b/spec/project-spec.js index 861a0f53a..2025cae71 100644 --- a/spec/project-spec.js +++ b/spec/project-spec.js @@ -3,8 +3,8 @@ const TextBuffer = require('text-buffer') const Project = require('../src/project') const fs = require('fs-plus') const path = require('path') -const {Directory} = require('pathwatcher') -const {stopAllWatchers} = require('../src/path-watcher') +const { Directory } = require('pathwatcher') +const { stopAllWatchers } = require('../src/path-watcher') const GitRepository = require('../src/git-repository') describe('Project', () => { @@ -46,13 +46,16 @@ describe('Project', () => { let err = null waitsForPromise(() => - deserializedProject.deserialize(state, atom.deserializers) - .catch(e => { err = e }) + deserializedProject.deserialize(state, atom.deserializers).catch(e => { + err = e + }) ) runs(() => { expect(deserializedProject.getPaths()).toEqual(atom.project.getPaths()) - expect(err.missingProjectPaths).toEqual(['/directory/that/does/not/exist']) + expect(err.missingProjectPaths).toEqual([ + '/directory/that/does/not/exist' + ]) }) }) @@ -74,8 +77,9 @@ describe('Project', () => { let err = null waitsForPromise(() => - deserializedProject.deserialize(state, atom.deserializers) - .catch(e => { err = e }) + deserializedProject.deserialize(state, atom.deserializers).catch(e => { + err = e + }) ) runs(() => { @@ -98,7 +102,11 @@ describe('Project', () => { }) }) - waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))) + waitsForPromise(() => + deserializedProject.deserialize( + atom.project.serialize({ isUnloading: false }) + ) + ) runs(() => expect(deserializedProject.getBuffers().length).toBe(0)) }) @@ -116,7 +124,11 @@ describe('Project', () => { }) }) - waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))) + waitsForPromise(() => + deserializedProject.deserialize( + atom.project.serialize({ isUnloading: false }) + ) + ) runs(() => { expect(deserializedProject.getBuffers().length).toBe(1) @@ -126,7 +138,10 @@ describe('Project', () => { }) it('does not deserialize buffers when their path is now a directory', () => { - const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt') + const pathToOpen = path.join( + temp.mkdirSync('atom-spec-project'), + 'file.txt' + ) waitsForPromise(() => atom.workspace.open(pathToOpen)) @@ -141,14 +156,23 @@ describe('Project', () => { }) }) - waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))) + waitsForPromise(() => + deserializedProject.deserialize( + atom.project.serialize({ isUnloading: false }) + ) + ) runs(() => expect(deserializedProject.getBuffers().length).toBe(0)) }) it('does not deserialize buffers when their path is inaccessible', () => { - if (process.platform === 'win32') { return } // chmod not supported on win32 - const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt') + if (process.platform === 'win32') { + return + } // chmod not supported on win32 + const pathToOpen = path.join( + temp.mkdirSync('atom-spec-project'), + 'file.txt' + ) fs.writeFileSync(pathToOpen, '') waitsForPromise(() => atom.workspace.open(pathToOpen)) @@ -164,13 +188,20 @@ describe('Project', () => { }) }) - waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))) + waitsForPromise(() => + deserializedProject.deserialize( + atom.project.serialize({ isUnloading: false }) + ) + ) runs(() => expect(deserializedProject.getBuffers().length).toBe(0)) }) it('does not deserialize buffers with their path is no longer present', () => { - const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt') + const pathToOpen = path.join( + temp.mkdirSync('atom-spec-project'), + 'file.txt' + ) fs.writeFileSync(pathToOpen, '') waitsForPromise(() => atom.workspace.open(pathToOpen)) @@ -186,13 +217,20 @@ describe('Project', () => { }) }) - waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))) + waitsForPromise(() => + deserializedProject.deserialize( + atom.project.serialize({ isUnloading: false }) + ) + ) runs(() => expect(deserializedProject.getBuffers().length).toBe(0)) }) it('deserializes buffers that have never been saved before', () => { - const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt') + const pathToOpen = path.join( + temp.mkdirSync('atom-spec-project'), + 'file.txt' + ) waitsForPromise(() => atom.workspace.open(pathToOpen)) @@ -208,7 +246,11 @@ describe('Project', () => { }) }) - waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))) + waitsForPromise(() => + deserializedProject.deserialize( + atom.project.serialize({ isUnloading: false }) + ) + ) runs(() => { expect(deserializedProject.getBuffers().length).toBe(1) @@ -226,7 +268,7 @@ describe('Project', () => { runs(() => { bufferA = atom.project.getBuffers()[0] - layerA = bufferA.addMarkerLayer({persistent: true}) + layerA = bufferA.addMarkerLayer({ persistent: true }) markerA = layerA.markPosition([0, 3]) bufferA.append('!') notQuittingProject = new Project({ @@ -237,10 +279,17 @@ describe('Project', () => { }) }) - waitsForPromise(() => notQuittingProject.deserialize(atom.project.serialize({isUnloading: false}))) + waitsForPromise(() => + notQuittingProject.deserialize( + atom.project.serialize({ isUnloading: false }) + ) + ) runs(() => { - expect(notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id), x => x.getMarker(markerA.id)).toBeUndefined() + expect( + notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id), + x => x.getMarker(markerA.id) + ).toBeUndefined() expect(notQuittingProject.getBuffers()[0].undo()).toBe(false) quittingProject = new Project({ notificationManager: atom.notifications, @@ -250,10 +299,16 @@ describe('Project', () => { }) }) - waitsForPromise(() => quittingProject.deserialize(atom.project.serialize({isUnloading: true}))) + waitsForPromise(() => + quittingProject.deserialize( + atom.project.serialize({ isUnloading: true }) + ) + ) runs(() => { - expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id), x => x.getMarker(markerA.id)).not.toBeUndefined() + expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id), x => + x.getMarker(markerA.id) + ).not.toBeUndefined() expect(quittingProject.getBuffers()[0].undo()).toBe(true) }) }) @@ -266,11 +321,17 @@ describe('Project', () => { expect(atom.project.getPaths()[0]).toBeUndefined() let editor = null - waitsForPromise(() => atom.workspace.open().then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open().then(o => { + editor = o + }) + ) waitsForPromise(() => editor.saveAs(tempFile)) - runs(() => expect(atom.project.getPaths()[0]).toBe(path.dirname(tempFile))) + runs(() => + expect(atom.project.getPaths()[0]).toBe(path.dirname(tempFile)) + ) }) }) @@ -284,7 +345,7 @@ describe('Project', () => { paths: [projectPath1, projectPath2], originPath: 'originPath', config: { - 'baz': 'buzz' + baz: 'buzz' } } }) @@ -323,10 +384,12 @@ describe('Project', () => { let buffer beforeEach(() => waitsForPromise(() => - atom.project.bufferForPath(path.join(__dirname, 'fixtures', 'sample.js')).then((o) => { - buffer = o - buffer.retain() - }) + atom.project + .bufferForPath(path.join(__dirname, 'fixtures', 'sample.js')) + .then(o => { + buffer = o + buffer.retain() + }) ) ) @@ -339,10 +402,18 @@ describe('Project', () => { waitsForPromise(() => buffer.save()) runs(() => { - expect(atom.project.applicationDelegate.emitDidSavePath.calls.length).toBe(1) - expect(atom.project.applicationDelegate.emitDidSavePath).toHaveBeenCalledWith(buffer.getPath()) - expect(atom.project.applicationDelegate.emitWillSavePath.calls.length).toBe(1) - expect(atom.project.applicationDelegate.emitWillSavePath).toHaveBeenCalledWith(buffer.getPath()) + expect( + atom.project.applicationDelegate.emitDidSavePath.calls.length + ).toBe(1) + expect( + atom.project.applicationDelegate.emitDidSavePath + ).toHaveBeenCalledWith(buffer.getPath()) + expect( + atom.project.applicationDelegate.emitWillSavePath.calls.length + ).toBe(1) + expect( + atom.project.applicationDelegate.emitWillSavePath + ).toHaveBeenCalledWith(buffer.getPath()) }) }) }) @@ -350,20 +421,23 @@ describe('Project', () => { describe('when a watch error is thrown from the TextBuffer', () => { let editor = null beforeEach(() => - waitsForPromise(() => atom.workspace.open(require.resolve('./fixtures/dir/a')).then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open(require.resolve('./fixtures/dir/a')).then(o => { + editor = o + }) + ) ) it('creates a warning notification', () => { let noteSpy - atom.notifications.onDidAddNotification(noteSpy = jasmine.createSpy()) + atom.notifications.onDidAddNotification((noteSpy = jasmine.createSpy())) const error = new Error('SomeError') error.eventType = 'resurrect' editor.buffer.emitter.emit('will-throw-watch-error', { handle: jasmine.createSpy(), error - } - ) + }) expect(noteSpy).toHaveBeenCalled() @@ -371,7 +445,9 @@ describe('Project', () => { expect(notification.getType()).toBe('warning') expect(notification.getDetail()).toBe('SomeError') expect(notification.getMessage()).toContain('`resurrect`') - expect(notification.getMessage()).toContain(path.join('fixtures', 'dir', 'a')) + expect(notification.getMessage()).toContain( + path.join('fixtures', 'dir', 'a') + ) }) }) @@ -379,10 +455,18 @@ describe('Project', () => { let fakeRepositoryProvider, fakeRepository beforeEach(() => { - fakeRepository = {destroy () { return null }} + fakeRepository = { + destroy () { + return null + } + } fakeRepositoryProvider = { - repositoryForDirectory (directory) { return Promise.resolve(fakeRepository) }, - repositoryForDirectorySync (directory) { return fakeRepository } + repositoryForDirectory (directory) { + return Promise.resolve(fakeRepository) + }, + repositoryForDirectorySync (directory) { + return fakeRepository + } } }) @@ -391,7 +475,11 @@ describe('Project', () => { atom.project.setPaths([projectPath]) expect(atom.project.getRepositories()).toEqual([null]) - atom.packages.serviceHub.provide('atom.repository-provider', '0.1.0', fakeRepositoryProvider) + atom.packages.serviceHub.provide( + 'atom.repository-provider', + '0.1.0', + fakeRepositoryProvider + ) waitsFor(() => atom.project.repositoryProviders.length > 1) runs(() => atom.project.getRepositories()[0] === fakeRepository) }) @@ -401,7 +489,11 @@ describe('Project', () => { expect(repositories.length).toEqual(1) expect(repositories[0]).toBeTruthy() - atom.packages.serviceHub.provide('atom.repository-provider', '0.1.0', fakeRepositoryProvider) + atom.packages.serviceHub.provide( + 'atom.repository-provider', + '0.1.0', + fakeRepositoryProvider + ) waitsFor(() => atom.project.repositoryProviders.length > 1) runs(() => expect(atom.project.getRepositories()).toBe(repositories)) }) @@ -409,7 +501,11 @@ describe('Project', () => { it('stops using it to create repositories when the service is removed', () => { atom.project.setPaths([]) - const disposable = atom.packages.serviceHub.provide('atom.repository-provider', '0.1.0', fakeRepositoryProvider) + const disposable = atom.packages.serviceHub.provide( + 'atom.repository-provider', + '0.1.0', + fakeRepositoryProvider + ) waitsFor(() => atom.project.repositoryProviders.length > 1) runs(() => { disposable.dispose() @@ -424,15 +520,35 @@ describe('Project', () => { constructor (aPath) { this.path = aPath } - getPath () { return this.path } - getFile () { return {existsSync () { return false }} } - getSubdirectory () { return {existsSync () { return false }} } - isRoot () { return true } - existsSync () { return this.path.endsWith('does-exist') } - contains (filePath) { return filePath.startsWith(this.path) } + getPath () { + return this.path + } + getFile () { + return { + existsSync () { + return false + } + } + } + getSubdirectory () { + return { + existsSync () { + return false + } + } + } + isRoot () { + return true + } + existsSync () { + return this.path.endsWith('does-exist') + } + contains (filePath) { + return filePath.startsWith(this.path) + } onDidChangeFiles (callback) { onDidChangeFilesCallback = callback - return {dispose: () => {}} + return { dispose: () => {} } } } @@ -440,15 +556,19 @@ describe('Project', () => { let onDidChangeFilesCallback = null beforeEach(() => { - serviceDisposable = atom.packages.serviceHub.provide('atom.directory-provider', '0.1.0', { - directoryForURISync (uri) { - if (uri.startsWith('ssh://')) { - return new DummyDirectory(uri) - } else { - return null + serviceDisposable = atom.packages.serviceHub.provide( + 'atom.directory-provider', + '0.1.0', + { + directoryForURISync (uri) { + if (uri.startsWith('ssh://')) { + return new DummyDirectory(uri) + } else { + return null + } } } - }) + ) onDidChangeFilesCallback = null waitsFor(() => atom.project.directoryProviders.length > 0) @@ -467,7 +587,8 @@ describe('Project', () => { expect(directories[1] instanceof DummyDirectory).toBe(true) // It does not add new remote paths that do not exist - const nonExistentRemotePath = 'ssh://another-directory:8080/does-not-exist' + const nonExistentRemotePath = + 'ssh://another-directory:8080/does-not-exist' atom.project.addPath(nonExistentRemotePath) expect(atom.project.getDirectories().length).toBe(2) @@ -499,7 +620,7 @@ describe('Project', () => { const changeSpy = jasmine.createSpy('atom.project.onDidChangeFiles') const disposable = atom.project.onDidChangeFiles(changeSpy) - const events = [{action: 'created', path: remotePath + '/test.txt'}] + const events = [{ action: 'created', path: remotePath + '/test.txt' }] onDidChangeFilesCallback(events) expect(changeSpy).toHaveBeenCalledWith(events) @@ -520,7 +641,11 @@ describe('Project', () => { describe("when given an absolute path that isn't currently open", () => { it("returns a new edit session for the given path and emits 'buffer-created'", () => { let editor = null - waitsForPromise(() => atom.workspace.open(absolutePath).then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open(absolutePath).then(o => { + editor = o + }) + ) runs(() => { expect(editor.buffer.getPath()).toBe(absolutePath) @@ -532,7 +657,11 @@ describe('Project', () => { describe("when given a relative path that isn't currently opened", () => { it("returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", () => { let editor = null - waitsForPromise(() => atom.workspace.open(absolutePath).then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open(absolutePath).then(o => { + editor = o + }) + ) runs(() => { expect(editor.buffer.getPath()).toBe(absolutePath) @@ -545,16 +674,22 @@ describe('Project', () => { it('returns a new edit session containing currently opened buffer', () => { let editor = null - waitsForPromise(() => atom.workspace.open(absolutePath).then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open(absolutePath).then(o => { + editor = o + }) + ) runs(() => newBufferHandler.reset()) waitsForPromise(() => - atom.workspace.open(absolutePath).then(({buffer}) => expect(buffer).toBe(editor.buffer)) + atom.workspace + .open(absolutePath) + .then(({ buffer }) => expect(buffer).toBe(editor.buffer)) ) waitsForPromise(() => - atom.workspace.open('a').then(({buffer}) => { + atom.workspace.open('a').then(({ buffer }) => { expect(buffer).toBe(editor.buffer) expect(newBufferHandler).not.toHaveBeenCalled() }) @@ -565,7 +700,11 @@ describe('Project', () => { describe('when not passed a path', () => { it("returns a new edit session and emits 'buffer-created'", () => { let editor = null - waitsForPromise(() => atom.workspace.open().then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open().then(o => { + editor = o + }) + ) runs(() => { expect(editor.buffer.getPath()).toBeUndefined() @@ -580,7 +719,7 @@ describe('Project', () => { beforeEach(() => waitsForPromise(() => - atom.project.bufferForPath('a').then((o) => { + atom.project.bufferForPath('a').then(o => { buffer = o buffer.retain() }) @@ -592,11 +731,15 @@ describe('Project', () => { describe('when opening a previously opened path', () => { it('does not create a new buffer', () => { waitsForPromise(() => - atom.project.bufferForPath('a').then(anotherBuffer => expect(anotherBuffer).toBe(buffer)) + atom.project + .bufferForPath('a') + .then(anotherBuffer => expect(anotherBuffer).toBe(buffer)) ) waitsForPromise(() => - atom.project.bufferForPath('b').then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer)) + atom.project + .bufferForPath('b') + .then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer)) ) waitsForPromise(() => @@ -610,12 +753,14 @@ describe('Project', () => { }) it('retries loading the buffer if it previously failed', () => { - waitsForPromise({shouldReject: true}, () => { - spyOn(TextBuffer, 'load').andCallFake(() => Promise.reject(new Error('Could not open file'))) + waitsForPromise({ shouldReject: true }, () => { + spyOn(TextBuffer, 'load').andCallFake(() => + Promise.reject(new Error('Could not open file')) + ) return atom.project.bufferForPath('b') }) - waitsForPromise({shouldReject: false}, () => { + waitsForPromise({ shouldReject: false }, () => { TextBuffer.load.andCallThrough() return atom.project.bufferForPath('b') }) @@ -625,7 +770,9 @@ describe('Project', () => { buffer.release() waitsForPromise(() => - atom.project.bufferForPath('b').then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer)) + atom.project + .bufferForPath('b') + .then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer)) ) }) }) @@ -635,7 +782,7 @@ describe('Project', () => { it('resolves to null when the directory does not have a repository', () => { waitsForPromise(() => { const directory = new Directory('/tmp') - return atom.project.repositoryForDirectory(directory).then((result) => { + return atom.project.repositoryForDirectory(directory).then(result => { expect(result).toBeNull() expect(atom.project.repositoryProviders.length).toBeGreaterThan(0) expect(atom.project.repositoryPromisesByPath.size).toBe(0) @@ -647,7 +794,7 @@ describe('Project', () => { waitsForPromise(() => { const directory = new Directory(path.join(__dirname, '..')) const promise = atom.project.repositoryForDirectory(directory) - return promise.then((result) => { + return promise.then(result => { expect(result).toBeInstanceOf(GitRepository) const dirPath = directory.getRealPathSync() expect(result.getPath()).toBe(path.join(dirPath, '.git')) @@ -662,7 +809,11 @@ describe('Project', () => { let repository = null const directory = new Directory(path.join(__dirname, '..')) - waitsForPromise(() => atom.project.repositoryForDirectory(directory).then(repo => { repository = repo })) + waitsForPromise(() => + atom.project.repositoryForDirectory(directory).then(repo => { + repository = repo + }) + ) runs(() => { expect(repository.isDestroyed()).toBe(false) @@ -670,7 +821,11 @@ describe('Project', () => { expect(repository.isDestroyed()).toBe(true) }) - waitsForPromise(() => atom.project.repositoryForDirectory(directory).then(repo => { repository = repo })) + waitsForPromise(() => + atom.project.repositoryForDirectory(directory).then(repo => { + repository = repo + }) + ) runs(() => expect(repository.isDestroyed()).toBe(false)) }) @@ -682,7 +837,9 @@ describe('Project', () => { const filePath = require.resolve('./fixtures/dir/a') atom.project.setPaths([filePath]) expect(atom.project.getPaths()[0]).toEqual(path.dirname(filePath)) - expect(atom.project.getDirectories()[0].path).toEqual(path.dirname(filePath)) + expect(atom.project.getDirectories()[0].path).toEqual( + path.dirname(filePath) + ) }) }) @@ -692,7 +849,9 @@ describe('Project', () => { const directory2 = temp.mkdirSync('git-repo1') const directory3 = temp.mkdirSync('git-repo2') - const gitDirPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git')) + const gitDirPath = fs.absolute( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) fs.copySync(gitDirPath, path.join(directory2, '.git')) fs.copySync(gitDirPath, path.join(directory3, '.git')) @@ -701,16 +860,20 @@ describe('Project', () => { const [repo1, repo2, repo3] = atom.project.getRepositories() expect(repo1).toBeNull() expect(repo2.getShortHead()).toBe('master') - expect(repo2.getPath()).toBe(fs.realpathSync(path.join(directory2, '.git'))) + expect(repo2.getPath()).toBe( + fs.realpathSync(path.join(directory2, '.git')) + ) expect(repo3.getShortHead()).toBe('master') - expect(repo3.getPath()).toBe(fs.realpathSync(path.join(directory3, '.git'))) + expect(repo3.getPath()).toBe( + fs.realpathSync(path.join(directory3, '.git')) + ) }) it('calls callbacks registered with ::onDidChangePaths', () => { const onDidChangePathsSpy = jasmine.createSpy('onDidChangePaths spy') atom.project.onDidChangePaths(onDidChangePathsSpy) - const paths = [ temp.mkdirSync('dir1'), temp.mkdirSync('dir2') ] + const paths = [temp.mkdirSync('dir1'), temp.mkdirSync('dir2')] atom.project.setPaths(paths) expect(onDidChangePathsSpy.callCount).toBe(1) @@ -718,10 +881,15 @@ describe('Project', () => { }) it('optionally throws an error with any paths that did not exist', () => { - const paths = [temp.mkdirSync('exists0'), '/doesnt-exists/0', temp.mkdirSync('exists1'), '/doesnt-exists/1'] + const paths = [ + temp.mkdirSync('exists0'), + '/doesnt-exists/0', + temp.mkdirSync('exists1'), + '/doesnt-exists/1' + ] try { - atom.project.setPaths(paths, {mustExist: true}) + atom.project.setPaths(paths, { mustExist: true }) expect('no exception thrown').toBeUndefined() } catch (e) { expect(e.missingProjectPaths).toEqual([paths[1], paths[3]]) @@ -740,9 +908,17 @@ describe('Project', () => { }) it('normalizes the path to remove consecutive slashes, ., and .. segments', () => { - atom.project.setPaths([`${require.resolve('./fixtures/dir/a')}${path.sep}b${path.sep}${path.sep}..`]) - expect(atom.project.getPaths()[0]).toEqual(path.dirname(require.resolve('./fixtures/dir/a'))) - expect(atom.project.getDirectories()[0].path).toEqual(path.dirname(require.resolve('./fixtures/dir/a'))) + atom.project.setPaths([ + `${require.resolve('./fixtures/dir/a')}${path.sep}b${path.sep}${ + path.sep + }..` + ]) + expect(atom.project.getPaths()[0]).toEqual( + path.dirname(require.resolve('./fixtures/dir/a')) + ) + expect(atom.project.getDirectories()[0].path).toEqual( + path.dirname(require.resolve('./fixtures/dir/a')) + ) }) }) @@ -757,7 +933,10 @@ describe('Project', () => { atom.project.addPath(newPath) expect(onDidChangePathsSpy.callCount).toBe(1) - expect(onDidChangePathsSpy.mostRecentCall.args[0]).toEqual([oldPath, newPath]) + expect(onDidChangePathsSpy.mostRecentCall.args[0]).toEqual([ + oldPath, + newPath + ]) }) it("doesn't add redundant paths", () => { @@ -789,7 +968,11 @@ describe('Project', () => { }) it('optionally throws on non-existent directories', () => { - expect(() => atom.project.addPath('/this-definitely/does-not-exist', {mustExist: true})).toThrow() + expect(() => + atom.project.addPath('/this-definitely/does-not-exist', { + mustExist: true + }) + ).toThrow() }) }) @@ -821,7 +1004,9 @@ describe('Project', () => { it("doesn't destroy the repository if it is shared by another root directory", () => { atom.project.setPaths([__dirname, path.join(__dirname, '..', 'src')]) atom.project.removePath(__dirname) - expect(atom.project.getPaths()).toEqual([path.join(__dirname, '..', 'src')]) + expect(atom.project.getPaths()).toEqual([ + path.join(__dirname, '..', 'src') + ]) expect(atom.project.getRepositories()[0].isSubmodule('src')).toBe(false) }) @@ -829,10 +1014,18 @@ describe('Project', () => { atom.packages.serviceHub.provide('atom.directory-provider', '0.1.0', { directoryForURISync (uri) { return { - getPath () { return uri }, - getSubdirectory () { return {} }, - isRoot () { return true }, - existsSync () { return true }, + getPath () { + return uri + }, + getSubdirectory () { + return {} + }, + isRoot () { + return true + }, + existsSync () { + return true + }, off () {} } } @@ -854,7 +1047,7 @@ describe('Project', () => { let checkCallback = () => {} beforeEach(() => { - sub = atom.project.onDidChangeFiles((incoming) => { + sub = atom.project.onDidChangeFiles(incoming => { events.push(...incoming) checkCallback() }) @@ -862,18 +1055,24 @@ describe('Project', () => { afterEach(() => sub.dispose()) - const waitForEvents = (paths) => { - const remaining = new Set(paths.map((p) => fs.realpathSync(p))) + const waitForEvents = paths => { + const remaining = new Set(paths.map(p => fs.realpathSync(p))) return new Promise((resolve, reject) => { checkCallback = () => { - for (let event of events) { remaining.delete(event.path) } - if (remaining.size === 0) { resolve() } + for (let event of events) { + remaining.delete(event.path) + } + if (remaining.size === 0) { + resolve() + } } const expire = () => { checkCallback = () => {} console.error('Paths not seen:', remaining) - reject(new Error('Expired before all expected events were delivered.')) + reject( + new Error('Expired before all expected events were delivered.') + ) } checkCallback() @@ -904,7 +1103,9 @@ describe('Project', () => { waitsForPromise(() => waitForEvents([fileOne, fileTwo])) - runs(() => expect(events.some(event => event.path === fileThree)).toBeFalsy()) + runs(() => + expect(events.some(event => event.path === fileThree)).toBeFalsy() + ) }) }) @@ -914,7 +1115,8 @@ describe('Project', () => { const added = [] waitsForPromise(() => - atom.project.buildBuffer(require.resolve('./fixtures/dir/a')) + atom.project + .buildBuffer(require.resolve('./fixtures/dir/a')) .then(o => buffers.push(o)) ) @@ -924,7 +1126,8 @@ describe('Project', () => { }) waitsForPromise(() => - atom.project.buildBuffer(require.resolve('./fixtures/dir/b')) + atom.project + .buildBuffer(require.resolve('./fixtures/dir/b')) .then(o => buffers.push(o)) ) @@ -941,12 +1144,14 @@ describe('Project', () => { const observed = [] waitsForPromise(() => - atom.project.buildBuffer(require.resolve('./fixtures/dir/a')) + atom.project + .buildBuffer(require.resolve('./fixtures/dir/a')) .then(o => buffers.push(o)) ) waitsForPromise(() => - atom.project.buildBuffer(require.resolve('./fixtures/dir/b')) + atom.project + .buildBuffer(require.resolve('./fixtures/dir/b')) .then(o => buffers.push(o)) ) @@ -957,7 +1162,8 @@ describe('Project', () => { }) waitsForPromise(() => - atom.project.buildBuffer(require.resolve('./fixtures/dir/b')) + atom.project + .buildBuffer(require.resolve('./fixtures/dir/b')) .then(o => buffers.push(o)) ) @@ -974,22 +1180,38 @@ describe('Project', () => { const observed = [] const directory1 = temp.mkdirSync('git-repo1') - const gitDirPath1 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git')) + const gitDirPath1 = fs.absolute( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) fs.copySync(gitDirPath1, path.join(directory1, '.git')) const directory2 = temp.mkdirSync('git-repo2') - const gitDirPath2 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'repo-with-submodules', 'git.git')) + const gitDirPath2 = fs.absolute( + path.join( + __dirname, + 'fixtures', + 'git', + 'repo-with-submodules', + 'git.git' + ) + ) fs.copySync(gitDirPath2, path.join(directory2, '.git')) atom.project.setPaths([directory1]) - const disposable = atom.project.observeRepositories((repo) => observed.push(repo)) + const disposable = atom.project.observeRepositories(repo => + observed.push(repo) + ) expect(observed.length).toBe(1) - expect(observed[0].getReferenceTarget('refs/heads/master')).toBe('ef046e9eecaa5255ea5e9817132d4001724d6ae1') + expect(observed[0].getReferenceTarget('refs/heads/master')).toBe( + 'ef046e9eecaa5255ea5e9817132d4001724d6ae1' + ) atom.project.addPath(directory2) expect(observed.length).toBe(2) - expect(observed[1].getReferenceTarget('refs/heads/master')).toBe('d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5') + expect(observed[1].getReferenceTarget('refs/heads/master')).toBe( + 'd2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5' + ) disposable.dispose() }) @@ -998,25 +1220,35 @@ describe('Project', () => { describe('.onDidAddRepository()', () => { it('invokes callback when a path is added and the path is the root of a repository', () => { const observed = [] - const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo)) + const disposable = atom.project.onDidAddRepository(repo => + observed.push(repo) + ) const projectRootPath = temp.mkdirSync() - const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git')) + const fixtureRepoPath = fs.absolute( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git')) atom.project.addPath(projectRootPath) expect(observed.length).toBe(1) - expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git') + expect(observed[0].getOriginURL()).toEqual( + 'https://github.com/example-user/example-repo.git' + ) disposable.dispose() }) it('invokes callback when a path is added and the path is subdirectory of a repository', () => { const observed = [] - const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo)) + const disposable = atom.project.onDidAddRepository(repo => + observed.push(repo) + ) const projectRootPath = temp.mkdirSync() - const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git')) + const fixtureRepoPath = fs.absolute( + path.join(__dirname, 'fixtures', 'git', 'master.git') + ) fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git')) const projectSubDirPath = path.join(projectRootPath, 'sub-dir') @@ -1024,14 +1256,18 @@ describe('Project', () => { atom.project.addPath(projectSubDirPath) expect(observed.length).toBe(1) - expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git') + expect(observed[0].getOriginURL()).toEqual( + 'https://github.com/example-user/example-repo.git' + ) disposable.dispose() }) it('does not invoke callback when a path is added and the path is not part of a repository', () => { const observed = [] - const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo)) + const disposable = atom.project.onDidAddRepository(repo => + observed.push(repo) + ) atom.project.addPath(temp.mkdirSync('not-a-repository')) expect(observed.length).toBe(0) @@ -1046,11 +1282,15 @@ describe('Project', () => { let rootPath = atom.project.getPaths()[0] let childPath = path.join(rootPath, 'some', 'child', 'directory') - expect(atom.project.relativize(childPath)).toBe(path.join('some', 'child', 'directory')) + expect(atom.project.relativize(childPath)).toBe( + path.join('some', 'child', 'directory') + ) rootPath = atom.project.getPaths()[1] childPath = path.join(rootPath, 'some', 'child', 'directory') - expect(atom.project.relativize(childPath)).toBe(path.join('some', 'child', 'directory')) + expect(atom.project.relativize(childPath)).toBe( + path.join('some', 'child', 'directory') + ) }) it('returns the given path if it is not in any of the root directories', () => { @@ -1065,17 +1305,26 @@ describe('Project', () => { let rootPath = atom.project.getPaths()[0] let childPath = path.join(rootPath, 'some', 'child', 'directory') - expect(atom.project.relativizePath(childPath)).toEqual([rootPath, path.join('some', 'child', 'directory')]) + expect(atom.project.relativizePath(childPath)).toEqual([ + rootPath, + path.join('some', 'child', 'directory') + ]) rootPath = atom.project.getPaths()[1] childPath = path.join(rootPath, 'some', 'child', 'directory') - expect(atom.project.relativizePath(childPath)).toEqual([rootPath, path.join('some', 'child', 'directory')]) + expect(atom.project.relativizePath(childPath)).toEqual([ + rootPath, + path.join('some', 'child', 'directory') + ]) }) describe("when the given path isn't inside of any of the project's path", () => { it('returns null for the root path, and the given path unchanged', () => { const randomPath = path.join('some', 'random', 'path') - expect(atom.project.relativizePath(randomPath)).toEqual([null, randomPath]) + expect(atom.project.relativizePath(randomPath)).toEqual([ + null, + randomPath + ]) }) }) @@ -1090,7 +1339,10 @@ describe('Project', () => { it('uses the root folder that is closest to the given path', () => { atom.project.addPath(path.join(atom.project.getPaths()[0], 'a-dir')) - const inputPath = path.join(atom.project.getPaths()[1], 'somewhere/something.txt') + const inputPath = path.join( + atom.project.getPaths()[1], + 'somewhere/something.txt' + ) expect(atom.project.getDirectories()[0].contains(inputPath)).toBe(true) expect(atom.project.getDirectories()[1].contains(inputPath)).toBe(true) diff --git a/spec/reopen-project-menu-manager-spec.js b/spec/reopen-project-menu-manager-spec.js index b11561c31..f122eaa3b 100644 --- a/spec/reopen-project-menu-manager-spec.js +++ b/spec/reopen-project-menu-manager-spec.js @@ -1,21 +1,20 @@ /** @babel */ -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' -import {Emitter, Disposable, CompositeDisposable} from 'event-kit' +import { Disposable } from 'event-kit' const ReopenProjectMenuManager = require('../src/reopen-project-menu-manager') -numberRange = (low, high) => { +function numberRange (low, high) { const size = high - low const result = new Array(size) - for (var i = 0; i < size; i++) - result[i] = low + i + for (var i = 0; i < size; i++) result[i] = low + i return result } -describe("ReopenProjectMenuManager", () => { +describe('ReopenProjectMenuManager', () => { let menuManager, commandRegistry, config, historyManager, reopenProjects let commandDisposable, configDisposable, historyDisposable + let openFunction beforeEach(() => { menuManager = jasmine.createSpyObj('MenuManager', ['add']) @@ -28,43 +27,54 @@ describe("ReopenProjectMenuManager", () => { config = jasmine.createSpyObj('Config', ['onDidChange', 'get']) config.get.andReturn(10) configDisposable = jasmine.createSpyObj('Disposable', ['dispose']) - config.didChangeListener = { } + config.didChangeListener = {} config.onDidChange.andCallFake((key, fn) => { config.didChangeListener[key] = fn return configDisposable }) - historyManager = jasmine.createSpyObj('historyManager', ['getProjects','onDidChangeProjects']) + historyManager = jasmine.createSpyObj('historyManager', [ + 'getProjects', + 'onDidChangeProjects' + ]) historyManager.getProjects.andReturn([]) historyDisposable = jasmine.createSpyObj('Disposable', ['dispose']) - historyManager.onDidChangeProjects.andCallFake((fn) => { + historyManager.onDidChangeProjects.andCallFake(fn => { historyManager.changeProjectsListener = fn return historyDisposable }) openFunction = jasmine.createSpy() - reopenProjects = new ReopenProjectMenuManager({menu:menuManager, commands: commandRegistry, history: historyManager, config, open:openFunction}) + reopenProjects = new ReopenProjectMenuManager({ + menu: menuManager, + commands: commandRegistry, + history: historyManager, + config, + open: openFunction + }) }) - describe("constructor", () => { + describe('constructor', () => { it("registers the 'reopen-project' command function", () => { expect(commandRegistry.add).toHaveBeenCalled() const cmdCall = commandRegistry.add.calls[0] expect(cmdCall.args.length).toBe(2) expect(cmdCall.args[0]).toBe('atom-workspace') - expect(typeof cmdCall.args[1]['application:reopen-project']).toBe('function') + expect(typeof cmdCall.args[1]['application:reopen-project']).toBe( + 'function' + ) }) }) - describe("dispose", () => { - it("disposes of the history, command and config disposables", () => { + describe('dispose', () => { + it('disposes of the history, command and config disposables', () => { reopenProjects.dispose() expect(historyDisposable.dispose).toHaveBeenCalled() expect(configDisposable.dispose).toHaveBeenCalled() expect(commandDisposable.dispose).toHaveBeenCalled() }) - it("disposes of the menu disposable once used", () => { + it('disposes of the menu disposable once used', () => { const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose']) menuManager.add.andReturn(menuDisposable) reopenProjects.update() @@ -74,36 +84,45 @@ describe("ReopenProjectMenuManager", () => { }) }) - describe("the command", () => { - it("calls open with the paths of the project specified by the detail index", () => { - historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] }]) + describe('the command', () => { + it('calls open with the paths of the project specified by the detail index', () => { + historyManager.getProjects.andReturn([ + { paths: ['/a'] }, + { paths: ['/b', 'c:\\'] } + ]) reopenProjects.update() - reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project'] + const reopenProjectCommand = + commandRegistry.add.calls[0].args[1]['application:reopen-project'] reopenProjectCommand({ detail: { index: 1 } }) expect(openFunction).toHaveBeenCalled() expect(openFunction.calls[0].args[0]).toEqual(['/b', 'c:\\']) }) - it("does not call open when no command detail is supplied", () => { - reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project'] + it('does not call open when no command detail is supplied', () => { + const reopenProjectCommand = + commandRegistry.add.calls[0].args[1]['application:reopen-project'] reopenProjectCommand({}) expect(openFunction).not.toHaveBeenCalled() }) - it("does not call open when no command detail index is supplied", () => { - reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project'] + it('does not call open when no command detail index is supplied', () => { + const reopenProjectCommand = + commandRegistry.add.calls[0].args[1]['application:reopen-project'] reopenProjectCommand({ detail: { anything: 'here' } }) expect(openFunction).not.toHaveBeenCalled() }) }) - describe("update", () => { - it("adds menu items to MenuManager based on projects from HistoryManager", () => { - historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] }]) + describe('update', () => { + it('adds menu items to MenuManager based on projects from HistoryManager', () => { + historyManager.getProjects.andReturn([ + { paths: ['/a'] }, + { paths: ['/b', 'c:\\'] } + ]) reopenProjects.update() expect(historyManager.getProjects).toHaveBeenCalled() expect(menuManager.add).toHaveBeenCalled() @@ -127,7 +146,9 @@ describe("ReopenProjectMenuManager", () => { }) it("adds only the number of menu items specified in the 'core.reopenProjectMenuCount' config", () => { - historyManager.getProjects.andReturn(numberRange(1, 100).map(i => ({ paths: [ '/test/' + i ] }))) + historyManager.getProjects.andReturn( + numberRange(1, 100).map(i => ({ paths: ['/test/' + i] })) + ) reopenProjects.update() expect(menuManager.add).toHaveBeenCalled() const menu = menuManager.add.calls[0].args[0][0] @@ -137,7 +158,7 @@ describe("ReopenProjectMenuManager", () => { expect(menu.submenu[0].submenu.length).toBe(10) }) - it("disposes the previously menu built", () => { + it('disposes the previously menu built', () => { const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose']) menuManager.add.andReturn(menuDisposable) reopenProjects.update() @@ -147,10 +168,15 @@ describe("ReopenProjectMenuManager", () => { }) it("is called when the Config changes for 'core.reopenProjectMenuCount'", () => { - historyManager.getProjects.andReturn(numberRange(1, 100).map(i => ({ paths: [ '/test/' + i ] }))) + historyManager.getProjects.andReturn( + numberRange(1, 100).map(i => ({ paths: ['/test/' + i] })) + ) reopenProjects.update() config.get.andReturn(25) - config.didChangeListener['core.reopenProjectMenuCount']({oldValue:10, newValue: 25}) + config.didChangeListener['core.reopenProjectMenuCount']({ + oldValue: 10, + newValue: 25 + }) const finalArgs = menuManager.add.calls[1].args[0] const projectsMenu = finalArgs[0].submenu[0].submenu @@ -160,7 +186,10 @@ describe("ReopenProjectMenuManager", () => { it("is called when the HistoryManager's projects change", () => { reopenProjects.update() - historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] } ]) + historyManager.getProjects.andReturn([ + { paths: ['/a'] }, + { paths: ['/b', 'c:\\'] } + ]) historyManager.changeProjectsListener() expect(menuManager.add.calls.length).toBe(2) @@ -179,11 +208,11 @@ describe("ReopenProjectMenuManager", () => { }) }) - describe("updateProjects", () => { - it("creates correct menu items commands for recent projects", () => { + describe('updateProjects', () => { + it('creates correct menu items commands for recent projects', () => { const projects = [ - { paths: [ '/users/neila' ] }, - { paths: [ '/users/buzza', 'users/michaelc' ] } + { paths: ['/users/neila'] }, + { paths: ['/users/buzza', 'users/michaelc'] } ] const menu = ReopenProjectMenuManager.createProjectsMenu(projects) @@ -197,69 +226,81 @@ describe("ReopenProjectMenuManager", () => { const first = recentMenu.submenu[0] expect(first.label).toBe('/users/neila') expect(first.command).toBe('application:reopen-project') - expect(first.commandDetail).toEqual({index: 0}) + expect(first.commandDetail).toEqual({ index: 0 }) const second = recentMenu.submenu[1] expect(second.label).toBe('buzza, michaelc') expect(second.command).toBe('application:reopen-project') - expect(second.commandDetail).toEqual({index: 1}) + expect(second.commandDetail).toEqual({ index: 1 }) }) }) - describe("createLabel", () => { - it("returns the Unix path unchanged if there is only one", () => { - const label = ReopenProjectMenuManager.createLabel({ paths: ['/a/b/c/d/e/f'] }) + describe('createLabel', () => { + it('returns the Unix path unchanged if there is only one', () => { + const label = ReopenProjectMenuManager.createLabel({ + paths: ['/a/b/c/d/e/f'] + }) expect(label).toBe('/a/b/c/d/e/f') }) - it("returns the Windows path unchanged if there is only one", () => { - const label = ReopenProjectMenuManager.createLabel({ paths: ['c:\\missions\\apollo11'] }) + it('returns the Windows path unchanged if there is only one', () => { + const label = ReopenProjectMenuManager.createLabel({ + paths: ['c:\\missions\\apollo11'] + }) expect(label).toBe('c:\\missions\\apollo11') }) - it("returns the URL unchanged if there is only one", () => { - const label = ReopenProjectMenuManager.createLabel({ paths: ['https://launch.pad/apollo/11'] }) + it('returns the URL unchanged if there is only one', () => { + const label = ReopenProjectMenuManager.createLabel({ + paths: ['https://launch.pad/apollo/11'] + }) expect(label).toBe('https://launch.pad/apollo/11') }) - it("returns a comma-separated list of base names if there are multiple", () => { - const project = { paths: [ '/var/one', '/usr/bin/two', '/etc/mission/control/three' ] } + it('returns a comma-separated list of base names if there are multiple', () => { + const project = { + paths: ['/var/one', '/usr/bin/two', '/etc/mission/control/three'] + } const label = ReopenProjectMenuManager.createLabel(project) expect(label).toBe('one, two, three') }) - describe("betterBaseName", () => { - it("returns the standard base name for an absolute Unix path", () => { + describe('betterBaseName', () => { + it('returns the standard base name for an absolute Unix path', () => { const name = ReopenProjectMenuManager.betterBaseName('/one/to/three') expect(name).toBe('three') }) - it("returns the standard base name for a relative Windows path", () => { + it('returns the standard base name for a relative Windows path', () => { if (process.platform === 'win32') { const name = ReopenProjectMenuManager.betterBaseName('.\\one\\two') expect(name).toBe('two') } }) - it("returns the standard base name for an absolute Windows path", () => { + it('returns the standard base name for an absolute Windows path', () => { if (process.platform === 'win32') { - const name = ReopenProjectMenuManager.betterBaseName('c:\\missions\\apollo\\11') + const name = ReopenProjectMenuManager.betterBaseName( + 'c:\\missions\\apollo\\11' + ) expect(name).toBe('11') } }) - it("returns the drive root for a Windows drive name", () => { + it('returns the drive root for a Windows drive name', () => { const name = ReopenProjectMenuManager.betterBaseName('d:') expect(name).toBe('d:\\') }) - it("returns the drive root for a Windows drive root", () => { + it('returns the drive root for a Windows drive root', () => { const name = ReopenProjectMenuManager.betterBaseName('e:\\') expect(name).toBe('e:\\') }) - it("returns the final path for a URI", () => { - const name = ReopenProjectMenuManager.betterBaseName('https://something/else') + it('returns the final path for a URI', () => { + const name = ReopenProjectMenuManager.betterBaseName( + 'https://something/else' + ) expect(name).toBe('else') }) }) diff --git a/spec/selection-spec.js b/spec/selection-spec.js index 216c3936c..5dc6cd199 100644 --- a/spec/selection-spec.js +++ b/spec/selection-spec.js @@ -1,12 +1,11 @@ const TextEditor = require('../src/text-editor') -// Tests crash the renderer process on Electron 3.0, disabling for now. describe('Selection', () => { let buffer, editor, selection beforeEach(() => { buffer = atom.project.bufferForPathSync('sample.js') - editor = new TextEditor({buffer, tabLength: 2}) + editor = new TextEditor({ buffer, tabLength: 2 }) selection = editor.getLastSelection() }) @@ -89,23 +88,31 @@ describe('Selection', () => { describe("when the selection's range is moved", () => { it('notifies ::onDidChangeRange observers', () => { selection.setBufferRange([[2, 0], [2, 10]]) - const changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler') + const changeScreenRangeHandler = jasmine.createSpy( + 'changeScreenRangeHandler' + ) selection.onDidChangeRange(changeScreenRangeHandler) buffer.insert([2, 5], 'abc') expect(changeScreenRangeHandler).toHaveBeenCalled() - expect(changeScreenRangeHandler.mostRecentCall.args[0]).not.toBeUndefined() - }); - }); + expect( + changeScreenRangeHandler.mostRecentCall.args[0] + ).not.toBeUndefined() + }) + }) describe("when only the selection's tail is moved (regression)", () => { it('notifies ::onDidChangeRange observers', () => { - selection.setBufferRange([[2, 0], [2, 10]], {reversed: true}) - const changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler') + selection.setBufferRange([[2, 0], [2, 10]], { reversed: true }) + const changeScreenRangeHandler = jasmine.createSpy( + 'changeScreenRangeHandler' + ) selection.onDidChangeRange(changeScreenRangeHandler) buffer.insert([2, 5], 'abc') expect(changeScreenRangeHandler).toHaveBeenCalled() - expect(changeScreenRangeHandler.mostRecentCall.args[0]).not.toBeUndefined() + expect( + changeScreenRangeHandler.mostRecentCall.args[0] + ).not.toBeUndefined() }) }) @@ -121,7 +128,7 @@ describe('Selection', () => { describe('.insertText(text, options)', () => { it('allows pasting white space only lines when autoIndent is enabled', () => { selection.setBufferRange([[0, 0], [0, 0]]) - selection.insertText(' \n \n\n', {autoIndent: true}) + selection.insertText(' \n \n\n', { autoIndent: true }) expect(buffer.lineForRow(0)).toBe(' ') expect(buffer.lineForRow(1)).toBe(' ') expect(buffer.lineForRow(2)).toBe('') @@ -129,19 +136,22 @@ describe('Selection', () => { it('auto-indents if only a newline is inserted', () => { selection.setBufferRange([[2, 0], [3, 0]]) - selection.insertText('\n', {autoIndent: true}) + selection.insertText('\n', { autoIndent: true }) expect(buffer.lineForRow(2)).toBe(' ') }) it('auto-indents if only a carriage return + newline is inserted', () => { selection.setBufferRange([[2, 0], [3, 0]]) - selection.insertText('\r\n', {autoIndent: true}) + selection.insertText('\r\n', { autoIndent: true }) expect(buffer.lineForRow(2)).toBe(' ') }) it('does not adjust the indent of trailing lines if preserveTrailingLineIndentation is true', () => { selection.setBufferRange([[5, 0], [5, 0]]) - selection.insertText(' foo\n bar\n', {preserveTrailingLineIndentation: true, indentBasis: 1}) + selection.insertText(' foo\n bar\n', { + preserveTrailingLineIndentation: true, + indentBasis: 1 + }) expect(buffer.lineForRow(6)).toBe(' bar') }) }) @@ -153,7 +163,9 @@ describe('Selection', () => { expect(selection.getScreenRange()).toEqual([[0, 4], [0, 4]]) expect(selection.getBufferRange()).toEqual([[1, 6], [1, 6]]) - expect(editor.lineTextForScreenRow(0)).toBe(`var${editor.displayLayer.foldCharacter}sort = function(items) {`) + expect(editor.lineTextForScreenRow(0)).toBe( + `var${editor.displayLayer.foldCharacter}sort = function(items) {` + ) expect(editor.isFoldedAtBufferRow(0)).toBe(true) }) @@ -163,7 +175,9 @@ describe('Selection', () => { expect(selection.getScreenRange()).toEqual([[0, 3], [0, 3]]) expect(selection.getBufferRange()).toEqual([[0, 3], [0, 3]]) - expect(editor.lineTextForScreenRow(0)).toBe('var quicksort = function () {') + expect(editor.lineTextForScreenRow(0)).toBe( + 'var quicksort = function () {' + ) expect(editor.isFoldedAtBufferRow(0)).toBe(false) }) }) @@ -262,11 +276,11 @@ describe('Selection', () => { { name: 'indentSelectedRows', op: opts => selection.indentSelectedRows(opts) - }, + } ] describe('without bypassReadOnly', () => { - for (const {name, op} of modifications) { + for (const { name, op } of modifications) { it(`throws an error on ${name}`, () => { expect(op).toThrow() }) @@ -274,9 +288,9 @@ describe('Selection', () => { }) describe('with bypassReadOnly', () => { - for (const {name, op} of modifications) { + for (const { name, op } of modifications) { it(`permits ${name}`, () => { - op({bypassReadOnly: true}) + op({ bypassReadOnly: true }) }) } }) diff --git a/spec/state-store-spec.js b/spec/state-store-spec.js index 0b62066c8..7b206cdb2 100644 --- a/spec/state-store-spec.js +++ b/spec/state-store-spec.js @@ -1,68 +1,68 @@ /** @babel */ -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' const StateStore = require('../src/state-store.js') -describe("StateStore", () => { +describe('StateStore', () => { let databaseName = `test-database-${Date.now()}` let version = 1 - it("can save, load, and delete states", () => { + it('can save, load, and delete states', () => { const store = new StateStore(databaseName, version) - return store.save('key', {foo:'bar'}) + return store + .save('key', { foo: 'bar' }) .then(() => store.load('key')) - .then((state) => { - expect(state).toEqual({foo:'bar'}) + .then(state => { + expect(state).toEqual({ foo: 'bar' }) }) .then(() => store.delete('key')) .then(() => store.load('key')) - .then((value) => { + .then(value => { expect(value).toBeNull() }) .then(() => store.count()) - .then((count) => { + .then(count => { expect(count).toBe(0) }) }) - it("resolves with null when a non-existent key is loaded", () => { + it('resolves with null when a non-existent key is loaded', () => { const store = new StateStore(databaseName, version) - return store.load('no-such-key').then((value) => { + return store.load('no-such-key').then(value => { expect(value).toBeNull() }) }) - it("can clear the state object store", () => { + it('can clear the state object store', () => { const store = new StateStore(databaseName, version) - return store.save('key', {foo:'bar'}) + return store + .save('key', { foo: 'bar' }) .then(() => store.count()) - .then((count) => - expect(count).toBe(1) - ) + .then(count => expect(count).toBe(1)) .then(() => store.clear()) .then(() => store.count()) - .then((count) => { + .then(count => { expect(count).toBe(0) }) }) - describe("when there is an error reading from the database", () => { - it("rejects the promise returned by load", () => { + describe('when there is an error reading from the database', () => { + it('rejects the promise returned by load', () => { const store = new StateStore(databaseName, version) - const fakeErrorEvent = {target: {errorCode: "Something bad happened"}} + const fakeErrorEvent = { target: { errorCode: 'Something bad happened' } } - spyOn(IDBObjectStore.prototype, 'get').andCallFake((key) => { + spyOn(IDBObjectStore.prototype, 'get').andCallFake(key => { let request = {} process.nextTick(() => request.onerror(fakeErrorEvent)) return request }) - return store.load('nonexistentKey') + return store + .load('nonexistentKey') .then(() => { - throw new Error("Promise should have been rejected") + throw new Error('Promise should have been rejected') }) - .catch((event) => { + .catch(event => { expect(event).toBe(fakeErrorEvent) }) }) diff --git a/spec/style-manager-spec.js b/spec/style-manager-spec.js index fba73c808..95ec0dacf 100644 --- a/spec/style-manager-spec.js +++ b/spec/style-manager-spec.js @@ -5,13 +5,21 @@ describe('StyleManager', () => { let [styleManager, addEvents, removeEvents, updateEvents] = [] beforeEach(() => { - styleManager = new StyleManager({configDirPath: temp.mkdirSync('atom-config')}) + styleManager = new StyleManager({ + configDirPath: temp.mkdirSync('atom-config') + }) addEvents = [] removeEvents = [] updateEvents = [] - styleManager.onDidAddStyleElement((event) => { addEvents.push(event) }) - styleManager.onDidRemoveStyleElement((event) => { removeEvents.push(event) }) - styleManager.onDidUpdateStyleElement((event) => { updateEvents.push(event) }) + styleManager.onDidAddStyleElement(event => { + addEvents.push(event) + }) + styleManager.onDidRemoveStyleElement(event => { + removeEvents.push(event) + }) + styleManager.onDidUpdateStyleElement(event => { + updateEvents.push(event) + }) }) afterEach(() => { @@ -39,7 +47,9 @@ describe('StyleManager', () => { describe('atom-text-editor shadow DOM selectors upgrades', () => { beforeEach(() => { // attach styles element to the DOM to parse CSS rules - styleManager.onDidAddStyleElement((styleElement) => { jasmine.attachToDOM(styleElement) }) + styleManager.onDidAddStyleElement(styleElement => { + jasmine.attachToDOM(styleElement) + }) }) it('removes the ::shadow pseudo-element from atom-text-editor selectors', () => { @@ -47,27 +57,40 @@ describe('StyleManager', () => { atom-text-editor::shadow .class-1, atom-text-editor::shadow .class-2 { color: red } atom-text-editor::shadow > .class-3 { color: yellow } atom-text-editor .class-4 { color: blue } + another-element::shadow .class-5 { color: white } atom-text-editor[data-grammar*=\"js\"]::shadow .class-6 { color: green; } atom-text-editor[mini].is-focused::shadow .class-7 { color: green; } `) - expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([ + expect( + Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map( + r => r.selectorText + ) + ).toEqual([ 'atom-text-editor.editor .class-1, atom-text-editor.editor .class-2', 'atom-text-editor.editor > .class-3', 'atom-text-editor .class-4', - 'atom-text-editor[data-grammar*=\"js\"].editor .class-6', + 'another-element::shadow .class-5', + 'atom-text-editor[data-grammar*="js"].editor .class-6', 'atom-text-editor[mini].is-focused.editor .class-7' ]) }) describe('when a selector targets the atom-text-editor shadow DOM', () => { it('prepends "--syntax" to class selectors matching a grammar scope name and not already starting with "syntax--"', () => { - styleManager.addStyleSheet(` + styleManager.addStyleSheet( + ` .class-1 { color: red } .source > .js, .source.coffee { color: green } .syntax--source { color: gray } #id-1 { color: blue } - `, {context: 'atom-text-editor'}) - expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([ + `, + { context: 'atom-text-editor' } + ) + expect( + Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map( + r => r.selectorText + ) + ).toEqual([ '.class-1', '.syntax--source > .syntax--js, .syntax--source.syntax--coffee', '.syntax--source', @@ -80,7 +103,11 @@ describe('StyleManager', () => { atom-text-editor[mini].is-focused::shadow .source > .js { color: gray } atom-text-editor .source > .js { color: red } `) - expect(Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map((r) => r.selectorText)).toEqual([ + expect( + Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map( + r => r.selectorText + ) + ).toEqual([ '.source > .js, .source.coffee', 'atom-text-editor.editor .syntax--source > .syntax--js', 'atom-text-editor[mini].is-focused.editor .syntax--source > .syntax--js', @@ -90,28 +117,50 @@ describe('StyleManager', () => { }) it('replaces ":host" with "atom-text-editor" only when the context of a style sheet is "atom-text-editor"', () => { - styleManager.addStyleSheet(':host .class-1, :host .class-2 { color: red; }') + styleManager.addStyleSheet( + ':host .class-1, :host .class-2 { color: red; }' + ) + expect( + Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map( + r => r.selectorText + ) + ).toEqual([':host .class-1, :host .class-2']) + styleManager.addStyleSheet( + ':host .class-1, :host .class-2 { color: red; }', + { context: 'atom-text-editor' } + ) + expect( + Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map( + r => r.selectorText + ) + ).toEqual(['atom-text-editor .class-1, atom-text-editor .class-2']) + }) + + it('does not transform CSS rules with invalid syntax', () => { + styleManager.addStyleSheet("atom-text-editor::shadow .class-1 { font-family: inval'id }") expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([ - ':host .class-1, :host .class-2' - ]) - styleManager.addStyleSheet(':host .class-1, :host .class-2 { color: red; }', {context: 'atom-text-editor'}) - expect(Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map((r) => r.selectorText)).toEqual([ - 'atom-text-editor .class-1, atom-text-editor .class-2' + 'atom-text-editor::shadow .class-1' ]) }) it('does not throw exceptions on rules with no selectors', () => { - styleManager.addStyleSheet('@media screen {font-size: 10px}', {context: 'atom-text-editor'}) + styleManager.addStyleSheet('@media screen {font-size: 10px}', { + context: 'atom-text-editor' + }) }) }) describe('when a sourcePath parameter is specified', () => { it('ensures a maximum of one style element for the given source path, updating a previous if it exists', () => { - const disposable1 = styleManager.addStyleSheet('a {color: red}', {sourcePath: '/foo/bar'}) + styleManager.addStyleSheet('a {color: red}', { + sourcePath: '/foo/bar' + }) expect(addEvents.length).toBe(1) expect(addEvents[0].getAttribute('source-path')).toBe('/foo/bar') - const disposable2 = styleManager.addStyleSheet('a {color: blue}', {sourcePath: '/foo/bar'}) + const disposable2 = styleManager.addStyleSheet('a {color: blue}', { + sourcePath: '/foo/bar' + }) expect(addEvents.length).toBe(1) expect(updateEvents.length).toBe(1) expect(updateEvents[0].getAttribute('source-path')).toBe('/foo/bar') @@ -119,7 +168,9 @@ describe('StyleManager', () => { disposable2.dispose() addEvents = [] - styleManager.addStyleSheet('a {color: yellow}', {sourcePath: '/foo/bar'}) + styleManager.addStyleSheet('a {color: yellow}', { + sourcePath: '/foo/bar' + }) expect(addEvents.length).toBe(1) expect(addEvents[0].getAttribute('source-path')).toBe('/foo/bar') expect(addEvents[0].textContent).toBe('a {color: yellow}') @@ -128,11 +179,13 @@ describe('StyleManager', () => { describe('when a priority parameter is specified', () => { it('inserts the style sheet based on the priority', () => { - styleManager.addStyleSheet('a {color: red}', {priority: 1}) - styleManager.addStyleSheet('a {color: blue}', {priority: 0}) - styleManager.addStyleSheet('a {color: green}', {priority: 2}) - styleManager.addStyleSheet('a {color: yellow}', {priority: 1}) - expect(styleManager.getStyleElements().map((elt) => elt.textContent)).toEqual([ + styleManager.addStyleSheet('a {color: red}', { priority: 1 }) + styleManager.addStyleSheet('a {color: blue}', { priority: 0 }) + styleManager.addStyleSheet('a {color: green}', { priority: 2 }) + styleManager.addStyleSheet('a {color: yellow}', { priority: 1 }) + expect( + styleManager.getStyleElements().map(elt => elt.textContent) + ).toEqual([ 'a {color: blue}', 'a {color: red}', 'a {color: yellow}', diff --git a/spec/syntax-scope-map-spec.js b/spec/syntax-scope-map-spec.js index 15b44095c..ec52c24b6 100644 --- a/spec/syntax-scope-map-spec.js +++ b/spec/syntax-scope-map-spec.js @@ -5,7 +5,7 @@ describe('SyntaxScopeMap', () => { const map = new SyntaxScopeMap({ 'a > b > c': 'x', 'b > c': 'y', - 'c': 'z' + c: 'z' }) expect(map.get(['a', 'b', 'c'], [0, 0, 0])).toBe('x') @@ -20,7 +20,7 @@ describe('SyntaxScopeMap', () => { const map = new SyntaxScopeMap({ 'a > b': 'w', 'a > b:nth-child(1)': 'x', - 'b': 'y', + b: 'y', 'b:nth-child(2)': 'z' }) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index dbad4a3e7..5529f8730 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -1,19 +1,22 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise, timeoutPromise} = require('./async-spec-helpers') +const { conditionPromise } = require('./async-spec-helpers') const Random = require('../script/node_modules/random-seed') -const {getRandomBufferRange, buildRandomLines} = require('./helpers/random') +const { getRandomBufferRange, buildRandomLines } = require('./helpers/random') const TextEditorComponent = require('../src/text-editor-component') const TextEditorElement = require('../src/text-editor-element') const TextEditor = require('../src/text-editor') const TextBuffer = require('text-buffer') -const {Point} = TextBuffer +const { Point } = TextBuffer const fs = require('fs') const path = require('path') const Grim = require('grim') const electron = require('electron') const clipboard = electron.clipboard -const SAMPLE_TEXT = fs.readFileSync(path.join(__dirname, 'fixtures', 'sample.js'), 'utf8') +const SAMPLE_TEXT = fs.readFileSync( + path.join(__dirname, 'fixtures', 'sample.js'), + 'utf8' +) document.registerElement('text-editor-component-test-element', { prototype: Object.create(HTMLElement.prototype, { @@ -34,11 +37,16 @@ describe('TextEditorComponent', () => { // Force scrollbars to be visible regardless of local system configuration const scrollbarStyle = document.createElement('style') - scrollbarStyle.textContent = 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }' + scrollbarStyle.textContent = + 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }' jasmine.attachToDOM(scrollbarStyle) if (verticalScrollbarWidth == null) { - const {component, element} = buildComponent({text: 'abcdefgh\n'.repeat(10), width: 30, height: 30}) + const { component, element } = buildComponent({ + text: 'abcdefgh\n'.repeat(10), + width: 30, + height: 30 + }) verticalScrollbarWidth = getVerticalScrollbarWidth(component) horizontalScrollbarHeight = getHorizontalScrollbarHeight(component) element.remove() @@ -54,7 +62,10 @@ describe('TextEditorComponent', () => { describe('rendering', () => { it('renders lines and line numbers for the visible region', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 3, + autoHeight: false + }) expect(queryOnScreenLineNumberElements(element).length).toBe(13) expect(queryOnScreenLineElements(element).length).toBe(13) @@ -69,13 +80,19 @@ describe('TextEditorComponent', () => { // After scrolling down beyond > 3 rows, the order of line numbers and lines // in the DOM is a bit weird because the first tile is recycled to the bottom // when it is scrolled out of view - expect(queryOnScreenLineNumberElements(element).map(element => element.textContent.trim())).toEqual([ - '10', '11', '12', '4', '5', '6', '7', '8', '9' - ]) - expect(queryOnScreenLineElements(element).map(element => element.dataset.screenRow)).toEqual([ - '9', '10', '11', '3', '4', '5', '6', '7', '8' - ]) - expect(queryOnScreenLineElements(element).map(element => element.textContent)).toEqual([ + expect( + queryOnScreenLineNumberElements(element).map(element => + element.textContent.trim() + ) + ).toEqual(['10', '11', '12', '4', '5', '6', '7', '8', '9']) + expect( + queryOnScreenLineElements(element).map( + element => element.dataset.screenRow + ) + ).toEqual(['9', '10', '11', '3', '4', '5', '6', '7', '8']) + expect( + queryOnScreenLineElements(element).map(element => element.textContent) + ).toEqual([ editor.lineTextForScreenRow(9), ' ', // this line is blank in the model, but we render a space to prevent the line from collapsing vertically editor.lineTextForScreenRow(11), @@ -88,13 +105,19 @@ describe('TextEditorComponent', () => { ]) await setScrollTop(component, 2.5 * component.getLineHeight()) - expect(queryOnScreenLineNumberElements(element).map(element => element.textContent.trim())).toEqual([ - '1', '2', '3', '4', '5', '6', '7', '8', '9' - ]) - expect(queryOnScreenLineElements(element).map(element => element.dataset.screenRow)).toEqual([ - '0', '1', '2', '3', '4', '5', '6', '7', '8' - ]) - expect(queryOnScreenLineElements(element).map(element => element.textContent)).toEqual([ + expect( + queryOnScreenLineNumberElements(element).map(element => + element.textContent.trim() + ) + ).toEqual(['1', '2', '3', '4', '5', '6', '7', '8', '9']) + expect( + queryOnScreenLineElements(element).map( + element => element.dataset.screenRow + ) + ).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']) + expect( + queryOnScreenLineElements(element).map(element => element.textContent) + ).toEqual([ editor.lineTextForScreenRow(0), editor.lineTextForScreenRow(1), editor.lineTextForScreenRow(2), @@ -108,22 +131,30 @@ describe('TextEditorComponent', () => { }) it('bases the width of the lines div on the width of the longest initially-visible screen line', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, height: 20, width: 100}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 2, + height: 20, + width: 100 + }) { expect(editor.getApproximateLongestScreenRow()).toBe(3) const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(3, Infinity)).left + - component.getBaseCharacterWidth() + component.getBaseCharacterWidth() + ) + expect(element.querySelector('.lines').style.width).toBe( + expectedWidth + 'px' ) - expect(element.querySelector('.lines').style.width).toBe(expectedWidth + 'px') } { // Get the next update promise synchronously here to ensure we don't // miss the update while polling the condition. const nextUpdatePromise = component.getNextUpdatePromise() - await conditionPromise(() => editor.getApproximateLongestScreenRow() === 6) + await conditionPromise( + () => editor.getApproximateLongestScreenRow() === 6 + ) await nextUpdatePromise // Capture the width of the lines before requesting the width of @@ -131,11 +162,12 @@ describe('TextEditorComponent', () => { const actualWidth = element.querySelector('.lines').style.width const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(6, Infinity)).left + - component.getBaseCharacterWidth() + component.getBaseCharacterWidth() ) expect(actualWidth).toBe(expectedWidth + 'px') } + // eslint-disable-next-line no-lone-blocks { // Make sure we do not throw an error if a synchronous update is // triggered before measuring the longest line from a @@ -154,7 +186,10 @@ describe('TextEditorComponent', () => { }) it('re-renders lines when their height changes', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) + const { component, element } = buildComponent({ + rowsPerTile: 3, + autoHeight: false + }) element.style.height = 4 * component.measurements.lineHeight + 'px' await component.getNextUpdatePromise() expect(queryOnScreenLineNumberElements(element).length).toBe(9) @@ -192,84 +227,118 @@ describe('TextEditorComponent', () => { }) it('makes the content at least as tall as the scroll container client height', async () => { - const {component, element, editor} = buildComponent({text: 'a'.repeat(100), width: 50, height: 100}) - expect(component.refs.content.offsetHeight).toBe(100 - getHorizontalScrollbarHeight(component)) + const { component, editor } = buildComponent({ + text: 'a'.repeat(100), + width: 50, + height: 100 + }) + expect(component.refs.content.offsetHeight).toBe( + 100 - getHorizontalScrollbarHeight(component) + ) editor.setText('a\n'.repeat(30)) await component.getNextUpdatePromise() expect(component.refs.content.offsetHeight).toBeGreaterThan(100) - expect(component.refs.content.offsetHeight).toBe(component.getContentHeight()) + expect(component.refs.content.offsetHeight).toBe( + component.getContentHeight() + ) }) it('honors the scrollPastEnd option by adding empty space equivalent to the clientHeight to the end of the content area', async () => { - const {component, element, editor} = buildComponent({autoHeight: false, autoWidth: false}) - const {scrollContainer} = component.refs + const { component, editor } = buildComponent({ + autoHeight: false, + autoWidth: false + }) - await editor.update({scrollPastEnd: true}) + await editor.update({ scrollPastEnd: true }) await setEditorHeightInLines(component, 6) // scroll to end await setScrollTop(component, Infinity) - expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() - 3) + expect(component.getFirstVisibleRow()).toBe( + editor.getScreenLineCount() - 3 + ) - editor.update({scrollPastEnd: false}) + editor.update({ scrollPastEnd: false }) await component.getNextUpdatePromise() // wait for scrollable content resize - expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() - 6) + expect(component.getFirstVisibleRow()).toBe( + editor.getScreenLineCount() - 6 + ) // Always allows at least 3 lines worth of overscroll if the editor is short await setEditorHeightInLines(component, 2) - await editor.update({scrollPastEnd: true}) + await editor.update({ scrollPastEnd: true }) await setScrollTop(component, Infinity) - expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() + 1) + expect(component.getFirstVisibleRow()).toBe( + editor.getScreenLineCount() + 1 + ) }) it('does not fire onDidChangeScrollTop listeners when assigning the same maximal value and the content height has fractional pixels (regression)', async () => { - const {component, element, editor} = buildComponent({autoHeight: false, autoWidth: false}) + const { component, element, editor } = buildComponent({ + autoHeight: false, + autoWidth: false + }) await setEditorHeightInLines(component, 3) // Force a fractional content height with a block decoration - const item = document.createElement("div") + const item = document.createElement('div') item.style.height = '10.6px' - editor.decorateMarker(editor.markBufferPosition([0, 0]), {type: "block", item}) + editor.decorateMarker(editor.markBufferPosition([0, 0]), { + type: 'block', + item + }) await component.getNextUpdatePromise() component.setScrollTop(Infinity) - element.onDidChangeScrollTop((newScrollTop) => { + element.onDidChangeScrollTop(newScrollTop => { throw new Error('Scroll top should not have changed') }) component.setScrollTop(component.getScrollTop()) }) it('gives the line number tiles an explicit width and height so their layout can be strictly contained', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3}) + const { component, editor } = buildComponent({ rowsPerTile: 3 }) - const lineNumberGutterElement = component.refs.gutterContainer.refs.lineNumberGutter.element - expect(lineNumberGutterElement.offsetHeight).toBe(component.getScrollHeight()) + const lineNumberGutterElement = + component.refs.gutterContainer.refs.lineNumberGutter.element + expect(lineNumberGutterElement.offsetHeight).toBe( + component.getScrollHeight() + ) for (const child of lineNumberGutterElement.children) { expect(child.offsetWidth).toBe(lineNumberGutterElement.offsetWidth) if (!child.classList.contains('line-number')) { for (const lineNumberElement of child.children) { - expect(lineNumberElement.offsetWidth).toBe(lineNumberGutterElement.offsetWidth) + expect(lineNumberElement.offsetWidth).toBe( + lineNumberGutterElement.offsetWidth + ) } } } editor.setText('x\n'.repeat(99)) await component.getNextUpdatePromise() - expect(lineNumberGutterElement.offsetHeight).toBe(component.getScrollHeight()) + expect(lineNumberGutterElement.offsetHeight).toBe( + component.getScrollHeight() + ) for (const child of lineNumberGutterElement.children) { expect(child.offsetWidth).toBe(lineNumberGutterElement.offsetWidth) if (!child.classList.contains('line-number')) { for (const lineNumberElement of child.children) { - expect(lineNumberElement.offsetWidth).toBe(lineNumberGutterElement.offsetWidth) + expect(lineNumberElement.offsetWidth).toBe( + lineNumberGutterElement.offsetWidth + ) } } } }) it('keeps the number of tiles stable when the visible line count changes during vertical scrolling', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) + const { component } = buildComponent({ + rowsPerTile: 3, + autoHeight: false + }) await setEditorHeightInLines(component, 5.5) expect(component.refs.lineTiles.children.length).toBe(3 + 2) // account for cursors and highlights containers @@ -281,7 +350,10 @@ describe('TextEditorComponent', () => { }) it('recycles tiles on resize', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, autoHeight: false}) + const { component } = buildComponent({ + rowsPerTile: 2, + autoHeight: false + }) await setEditorHeightInLines(component, 7) await setScrollTop(component, 3.5 * component.getLineHeight()) const lineNode = lineNodeForScreenRow(component, 7) @@ -289,42 +361,68 @@ describe('TextEditorComponent', () => { expect(lineNodeForScreenRow(component, 7)).toBe(lineNode) }) - it('updates lines numbers when a row\'s foldability changes (regression)', async () => { - const {component, element, editor} = buildComponent({text: 'abc\n'}) + it("updates lines numbers when a row's foldability changes (regression)", async () => { + const { component, editor } = buildComponent({ text: 'abc\n' }) editor.setCursorBufferPosition([1, 0]) await component.getNextUpdatePromise() - expect(lineNumberNodeForScreenRow(component, 0).querySelector('.foldable')).toBeNull() + expect( + lineNumberNodeForScreenRow(component, 0).querySelector('.foldable') + ).toBeNull() editor.insertText(' def') await component.getNextUpdatePromise() - expect(lineNumberNodeForScreenRow(component, 0).querySelector('.foldable')).toBeDefined() + expect( + lineNumberNodeForScreenRow(component, 0).querySelector('.foldable') + ).toBeDefined() editor.undo() await component.getNextUpdatePromise() - expect(lineNumberNodeForScreenRow(component, 0).querySelector('.foldable')).toBeNull() + expect( + lineNumberNodeForScreenRow(component, 0).querySelector('.foldable') + ).toBeNull() }) it('shows the foldable icon on the last screen row of a buffer row that can be folded', async () => { - const {component, element, editor} = buildComponent({text: 'abc\n de\nfghijklm\n no', softWrapped: true}) + const { component } = buildComponent({ + text: 'abc\n de\nfghijklm\n no', + softWrapped: true + }) await setEditorWidthInCharacters(component, 5) - expect(lineNumberNodeForScreenRow(component, 0).classList.contains('foldable')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('foldable')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 2).classList.contains('foldable')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 3).classList.contains('foldable')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 4).classList.contains('foldable')).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 0).classList.contains('foldable') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('foldable') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 2).classList.contains('foldable') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 3).classList.contains('foldable') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 4).classList.contains('foldable') + ).toBe(false) }) it('renders dummy vertical and horizontal scrollbars when content overflows', async () => { - const {component, element, editor} = buildComponent({height: 100, width: 100}) + const { component, editor } = buildComponent({ + height: 100, + width: 100 + }) const verticalScrollbar = component.refs.verticalScrollbar.element const horizontalScrollbar = component.refs.horizontalScrollbar.element expect(verticalScrollbar.scrollHeight).toBe(component.getContentHeight()) expect(horizontalScrollbar.scrollWidth).toBe(component.getContentWidth()) expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(0) expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(0) - expect(verticalScrollbar.style.bottom).toBe(getVerticalScrollbarWidth(component) + 'px') + expect(verticalScrollbar.style.bottom).toBe( + getVerticalScrollbarWidth(component) + 'px' + ) expect(verticalScrollbar.style.visibility).toBe('') - expect(horizontalScrollbar.style.right).toBe(getHorizontalScrollbarHeight(component) + 'px') + expect(horizontalScrollbar.style.right).toBe( + getHorizontalScrollbarHeight(component) + 'px' + ) expect(horizontalScrollbar.style.visibility).toBe('') expect(component.refs.scrollbarCorner).toBeDefined() @@ -363,7 +461,10 @@ describe('TextEditorComponent', () => { describe('when scrollbar styles change or the editor element is detached and then reattached', () => { it('updates the bottom/right of dummy scrollbars and client height/width measurements', async () => { - const {component, element, editor} = buildComponent({height: 100, width: 100}) + const { component, element, editor } = buildComponent({ + height: 100, + width: 100 + }) expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(10) expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(10) setScrollTop(component, 20) @@ -379,12 +480,18 @@ describe('TextEditorComponent', () => { expect(getHorizontalScrollbarHeight(component)).toBe(10) expect(getVerticalScrollbarWidth(component)).toBe(10) - expect(component.refs.horizontalScrollbar.element.style.right).toBe('10px') - expect(component.refs.verticalScrollbar.element.style.bottom).toBe('10px') + expect(component.refs.horizontalScrollbar.element.style.right).toBe( + '10px' + ) + expect(component.refs.verticalScrollbar.element.style.bottom).toBe( + '10px' + ) expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(10) expect(component.refs.verticalScrollbar.element.scrollTop).toBe(20) expect(component.getScrollContainerClientHeight()).toBe(100 - 10) - expect(component.getScrollContainerClientWidth()).toBe(100 - component.getGutterContainerWidth() - 10) + expect(component.getScrollContainerClientWidth()).toBe( + 100 - component.getGutterContainerWidth() - 10 + ) // Detaching and re-attaching the editor element. element.remove() @@ -392,15 +499,21 @@ describe('TextEditorComponent', () => { expect(getHorizontalScrollbarHeight(component)).toBe(10) expect(getVerticalScrollbarWidth(component)).toBe(10) - expect(component.refs.horizontalScrollbar.element.style.right).toBe('10px') - expect(component.refs.verticalScrollbar.element.style.bottom).toBe('10px') + expect(component.refs.horizontalScrollbar.element.style.right).toBe( + '10px' + ) + expect(component.refs.verticalScrollbar.element.style.bottom).toBe( + '10px' + ) expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(10) expect(component.refs.verticalScrollbar.element.scrollTop).toBe(20) expect(component.getScrollContainerClientHeight()).toBe(100 - 10) - expect(component.getScrollContainerClientWidth()).toBe(100 - component.getGutterContainerWidth() - 10) + expect(component.getScrollContainerClientWidth()).toBe( + 100 - component.getGutterContainerWidth() - 10 + ) // Ensure we don't throw an error trying to remeasure non-existent scrollbars for mini editors. - await editor.update({mini: true}) + await editor.update({ mini: true }) TextEditor.didUpdateScrollbarStyles() component.scheduleUpdate() await component.getNextUpdatePromise() @@ -408,19 +521,22 @@ describe('TextEditorComponent', () => { }) it('renders cursors within the visible row range', async () => { - const {component, element, editor} = buildComponent({height: 40, rowsPerTile: 2}) + const { component, element, editor } = buildComponent({ + height: 40, + rowsPerTile: 2 + }) await setScrollTop(component, 100) expect(component.getRenderedStartRow()).toBe(4) expect(component.getRenderedEndRow()).toBe(10) - editor.setCursorScreenPosition([0, 0], {autoscroll: false}) // out of view - editor.addCursorAtScreenPosition([2, 2], {autoscroll: false}) // out of view - editor.addCursorAtScreenPosition([4, 0], {autoscroll: false}) // line start - editor.addCursorAtScreenPosition([4, 4], {autoscroll: false}) // at token boundary - editor.addCursorAtScreenPosition([4, 6], {autoscroll: false}) // within token - editor.addCursorAtScreenPosition([5, Infinity], {autoscroll: false}) // line end - editor.addCursorAtScreenPosition([10, 2], {autoscroll: false}) // out of view + editor.setCursorScreenPosition([0, 0], { autoscroll: false }) // out of view + editor.addCursorAtScreenPosition([2, 2], { autoscroll: false }) // out of view + editor.addCursorAtScreenPosition([4, 0], { autoscroll: false }) // line start + editor.addCursorAtScreenPosition([4, 4], { autoscroll: false }) // at token boundary + editor.addCursorAtScreenPosition([4, 6], { autoscroll: false }) // within token + editor.addCursorAtScreenPosition([5, Infinity], { autoscroll: false }) // line end + editor.addCursorAtScreenPosition([10, 2], { autoscroll: false }) // out of view await component.getNextUpdatePromise() let cursorNodes = Array.from(element.querySelectorAll('.cursor')) @@ -430,31 +546,28 @@ describe('TextEditorComponent', () => { verifyCursorPosition(component, cursorNodes[2], 4, 6) verifyCursorPosition(component, cursorNodes[3], 5, 30) - editor.setCursorScreenPosition([8, 11], {autoscroll: false}) + editor.setCursorScreenPosition([8, 11], { autoscroll: false }) await component.getNextUpdatePromise() cursorNodes = Array.from(element.querySelectorAll('.cursor')) expect(cursorNodes.length).toBe(1) verifyCursorPosition(component, cursorNodes[0], 8, 11) - editor.setCursorScreenPosition([0, 0], {autoscroll: false}) + editor.setCursorScreenPosition([0, 0], { autoscroll: false }) await component.getNextUpdatePromise() cursorNodes = Array.from(element.querySelectorAll('.cursor')) expect(cursorNodes.length).toBe(0) - editor.setSelectedScreenRange([[8, 0], [12, 0]], {autoscroll: false}) + editor.setSelectedScreenRange([[8, 0], [12, 0]], { autoscroll: false }) await component.getNextUpdatePromise() cursorNodes = Array.from(element.querySelectorAll('.cursor')) expect(cursorNodes.length).toBe(0) }) it('hides cursors with non-empty selections when showCursorOnSelection is false', async () => { - const {component, element, editor} = buildComponent() - editor.setSelectedScreenRanges([ - [[0, 0], [0, 3]], - [[1, 0], [1, 0]] - ]) + const { component, element, editor } = buildComponent() + editor.setSelectedScreenRanges([[[0, 0], [0, 3]], [[1, 0], [1, 0]]]) await component.getNextUpdatePromise() { const cursorNodes = Array.from(element.querySelectorAll('.cursor')) @@ -463,7 +576,7 @@ describe('TextEditorComponent', () => { verifyCursorPosition(component, cursorNodes[1], 1, 0) } - editor.update({showCursorOnSelection: false}) + editor.update({ showCursorOnSelection: false }) await component.getNextUpdatePromise() { const cursorNodes = Array.from(element.querySelectorAll('.cursor')) @@ -471,10 +584,7 @@ describe('TextEditorComponent', () => { verifyCursorPosition(component, cursorNodes[0], 1, 0) } - editor.setSelectedScreenRanges([ - [[0, 0], [0, 3]], - [[1, 0], [1, 4]] - ]) + editor.setSelectedScreenRanges([[[0, 0], [0, 3]], [[1, 0], [1, 4]]]) await component.getNextUpdatePromise() { const cursorNodes = Array.from(element.querySelectorAll('.cursor')) @@ -484,7 +594,7 @@ describe('TextEditorComponent', () => { it('blinks cursors when the editor is focused and the cursors are not moving', async () => { assertDocumentFocused() - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() component.props.cursorBlinkPeriod = 40 component.props.cursorBlinkResumeDelay = 40 editor.addCursorAtScreenPosition([1, 0]) @@ -493,14 +603,20 @@ describe('TextEditorComponent', () => { await component.getNextUpdatePromise() const [cursor1, cursor2] = element.querySelectorAll('.cursor') - await conditionPromise(() => - getComputedStyle(cursor1).opacity === '1' && getComputedStyle(cursor2).opacity === '1' + await conditionPromise( + () => + getComputedStyle(cursor1).opacity === '1' && + getComputedStyle(cursor2).opacity === '1' ) - await conditionPromise(() => - getComputedStyle(cursor1).opacity === '0' && getComputedStyle(cursor2).opacity === '0' + await conditionPromise( + () => + getComputedStyle(cursor1).opacity === '0' && + getComputedStyle(cursor2).opacity === '0' ) - await conditionPromise(() => - getComputedStyle(cursor1).opacity === '1' && getComputedStyle(cursor2).opacity === '1' + await conditionPromise( + () => + getComputedStyle(cursor1).opacity === '1' && + getComputedStyle(cursor2).opacity === '1' ) editor.moveRight() @@ -511,13 +627,15 @@ describe('TextEditorComponent', () => { }) it('gives cursors at the end of lines the width of an "x" character', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() editor.setText('abcde') await setEditorWidthInCharacters(component, 5.5) editor.setCursorScreenPosition([0, Infinity]) await component.getNextUpdatePromise() - expect(element.querySelector('.cursor').offsetWidth).toBe(Math.round(component.getBaseCharacterWidth())) + expect(element.querySelector('.cursor').offsetWidth).toBe( + Math.round(component.getBaseCharacterWidth()) + ) // Clip cursor width when soft-wrap is on and the cursor is at the end of // the line. This prevents the parent tile from disabling sub-pixel @@ -525,11 +643,13 @@ describe('TextEditorComponent', () => { // container doesn't solve this issue so we're adding this workaround instead. editor.setSoftWrapped(true) await component.getNextUpdatePromise() - expect(element.querySelector('.cursor').offsetWidth).toBeLessThan(Math.round(component.getBaseCharacterWidth())) + expect(element.querySelector('.cursor').offsetWidth).toBeLessThan( + Math.round(component.getBaseCharacterWidth()) + ) }) it('positions and sizes cursors correctly when they are located next to a fold marker', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() editor.foldBufferRange([[0, 3], [0, 6]]) editor.setCursorScreenPosition([0, 3]) @@ -542,7 +662,9 @@ describe('TextEditorComponent', () => { }) it('positions cursors and placeholder text correctly when the lines container has a margin and/or is padded', async () => { - const {component, element, editor} = buildComponent({placeholderText: 'testing'}) + const { component, element, editor } = buildComponent({ + placeholderText: 'testing' + }) component.refs.lineTiles.style.marginLeft = '10px' TextEditor.didUpdateStyles() @@ -569,14 +691,20 @@ describe('TextEditorComponent', () => { editor.setText('') await component.getNextUpdatePromise() - const placeholderTextLeft = element.querySelector('.placeholder-text').getBoundingClientRect().left + const placeholderTextLeft = element + .querySelector('.placeholder-text') + .getBoundingClientRect().left const linesLeft = component.refs.lineTiles.getBoundingClientRect().left expect(placeholderTextLeft).toBe(linesLeft) }) it('places the hidden input element at the location of the last cursor if it is visible', async () => { - const {component, element, editor} = buildComponent({height: 60, width: 120, rowsPerTile: 2}) - const {hiddenInput} = component.refs.cursorsAndInput.refs + const { component, editor } = buildComponent({ + height: 60, + width: 120, + rowsPerTile: 2 + }) + const { hiddenInput } = component.refs.cursorsAndInput.refs setScrollTop(component, 100) await setScrollLeft(component, 40) @@ -591,21 +719,25 @@ describe('TextEditorComponent', () => { // Otherwise it is positioned at the last cursor position editor.addCursorAtScreenPosition([7, 4]) await component.getNextUpdatePromise() - expect(hiddenInput.getBoundingClientRect().top).toBe(clientTopForLine(component, 7)) - expect(Math.round(hiddenInput.getBoundingClientRect().left)).toBe(clientLeftForCharacter(component, 7, 4)) + expect(hiddenInput.getBoundingClientRect().top).toBe( + clientTopForLine(component, 7) + ) + expect(Math.round(hiddenInput.getBoundingClientRect().left)).toBe( + clientLeftForCharacter(component, 7, 4) + ) }) it('soft wraps lines based on the content width when soft wrap is enabled', async () => { let baseCharacterWidth, gutterContainerWidth { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() baseCharacterWidth = component.getBaseCharacterWidth() gutterContainerWidth = component.getGutterContainerWidth() editor.destroy() } - const {component, element, editor} = buildComponent({ - width: gutterContainerWidth + (baseCharacterWidth * 55), + const { component, element, editor } = buildComponent({ + width: gutterContainerWidth + baseCharacterWidth * 55, attach: false }) editor.setSoftWrapped(true) @@ -627,13 +759,18 @@ describe('TextEditorComponent', () => { ' = [], right = [];' ) - const {scrollContainer} = component.refs + const { scrollContainer } = component.refs expect(scrollContainer.clientWidth).toBe(scrollContainer.scrollWidth) }) it('correctly forces the display layer to index visible rows when resizing (regression)', async () => { const text = 'a'.repeat(30) + '\n' + 'b'.repeat(1000) - const {component, element, editor} = buildComponent({height: 300, width: 800, attach: false, text}) + const { component, element, editor } = buildComponent({ + height: 300, + width: 800, + attach: false, + text + }) editor.setSoftWrapped(true) jasmine.attachToDOM(element) @@ -643,38 +780,44 @@ describe('TextEditorComponent', () => { }) it('decorates the line numbers of folded lines', async () => { - const {component, element, editor} = buildComponent() + const { component, editor } = buildComponent() editor.foldBufferRow(1) await component.getNextUpdatePromise() - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('folded')).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('folded') + ).toBe(true) }) it('makes lines at least as wide as the scrollContainer', async () => { - const {component, element, editor} = buildComponent() - const {scrollContainer, gutterContainer} = component.refs + const { component, element, editor } = buildComponent() + const { scrollContainer } = component.refs editor.setText('a') await component.getNextUpdatePromise() - expect(element.querySelector('.line').offsetWidth).toBe(scrollContainer.offsetWidth - verticalScrollbarWidth) + expect(element.querySelector('.line').offsetWidth).toBe( + scrollContainer.offsetWidth - verticalScrollbarWidth + ) }) it('resizes based on the content when the autoHeight and/or autoWidth options are true', async () => { - const {component, element, editor} = buildComponent({autoHeight: true, autoWidth: true}) + const { component, element, editor } = buildComponent({ + autoHeight: true, + autoWidth: true + }) const editorPadding = 3 element.style.padding = editorPadding + 'px' - const {gutterContainer, scrollContainer} = component.refs const initialWidth = element.offsetWidth const initialHeight = element.offsetHeight expect(initialWidth).toBe( component.getGutterContainerWidth() + - component.getContentWidth() + - verticalScrollbarWidth + - 2 * editorPadding + component.getContentWidth() + + verticalScrollbarWidth + + 2 * editorPadding ) expect(initialHeight).toBe( component.getContentHeight() + - horizontalScrollbarHeight + - 2 * editorPadding + horizontalScrollbarHeight + + 2 * editorPadding ) // When autoWidth is enabled, width adjusts to content @@ -683,9 +826,9 @@ describe('TextEditorComponent', () => { await component.getNextUpdatePromise() expect(element.offsetWidth).toBe( component.getGutterContainerWidth() + - component.getContentWidth() + - verticalScrollbarWidth + - 2 * editorPadding + component.getContentWidth() + + verticalScrollbarWidth + + 2 * editorPadding ) expect(element.offsetWidth).toBeGreaterThan(initialWidth) @@ -694,48 +837,79 @@ describe('TextEditorComponent', () => { await component.getNextUpdatePromise() expect(element.offsetHeight).toBe( component.getContentHeight() + - horizontalScrollbarHeight + - 2 * editorPadding + horizontalScrollbarHeight + + 2 * editorPadding ) expect(element.offsetHeight).toBeGreaterThan(initialHeight) }) it('does not render the line number gutter at all if the isLineNumberGutterVisible parameter is false', () => { - const {component, element, editor} = buildComponent({lineNumberGutterVisible: false}) + const { element } = buildComponent({ + lineNumberGutterVisible: false + }) expect(element.querySelector('.line-number')).toBe(null) }) it('does not render the line numbers but still renders the line number gutter if showLineNumbers is false', async () => { function checkScrollContainerLeft (component) { - const {scrollContainer, gutterContainer} = component.refs - expect(scrollContainer.getBoundingClientRect().left).toBe(Math.round(gutterContainer.element.getBoundingClientRect().right)) + const { scrollContainer, gutterContainer } = component.refs + expect(scrollContainer.getBoundingClientRect().left).toBe( + Math.round(gutterContainer.element.getBoundingClientRect().right) + ) } - const {component, element, editor} = buildComponent({showLineNumbers: false}) - expect(Array.from(element.querySelectorAll('.line-number')).every((e) => e.textContent === '')).toBe(true) + const { component, element, editor } = buildComponent({ + showLineNumbers: false + }) + expect( + Array.from(element.querySelectorAll('.line-number')).every( + e => e.textContent === '' + ) + ).toBe(true) checkScrollContainerLeft(component) - await editor.update({showLineNumbers: true}) - expect(Array.from(element.querySelectorAll('.line-number')).map((e) => e.textContent)).toEqual([ - '00', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13' + await editor.update({ showLineNumbers: true }) + expect( + Array.from(element.querySelectorAll('.line-number')).map( + e => e.textContent + ) + ).toEqual([ + '00', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13' ]) checkScrollContainerLeft(component) - await editor.update({showLineNumbers: false}) - expect(Array.from(element.querySelectorAll('.line-number')).every((e) => e.textContent === '')).toBe(true) + await editor.update({ showLineNumbers: false }) + expect( + Array.from(element.querySelectorAll('.line-number')).every( + e => e.textContent === '' + ) + ).toBe(true) checkScrollContainerLeft(component) }) it('supports the placeholderText parameter', () => { const placeholderText = 'Placeholder Test' - const {element} = buildComponent({placeholderText, text: ''}) + const { element } = buildComponent({ placeholderText, text: '' }) expect(element.textContent).toContain(placeholderText) }) it('adds the data-grammar attribute and updates it when the grammar changes', async () => { await atom.packages.activatePackage('language-javascript') - const {editor, element, component} = buildComponent() + const { editor, element, component } = buildComponent() expect(element.dataset.grammar).toBe('text plain null-grammar') atom.grammars.assignLanguageMode(editor.getBuffer(), 'source.js') @@ -744,7 +918,7 @@ describe('TextEditorComponent', () => { }) it('adds the data-encoding attribute and updates it when the encoding changes', async () => { - const {editor, element, component} = buildComponent() + const { editor, element, component } = buildComponent() expect(element.dataset.encoding).toBe('utf8') editor.setEncoding('ascii') @@ -753,61 +927,133 @@ describe('TextEditorComponent', () => { }) it('adds the has-selection class when the editor has a non-empty selection', async () => { - const {editor, element, component} = buildComponent() + const { editor, element, component } = buildComponent() expect(element.classList.contains('has-selection')).toBe(false) - editor.setSelectedBufferRanges([ - [[0, 0], [0, 0]], - [[1, 0], [1, 10]] - ]) + editor.setSelectedBufferRanges([[[0, 0], [0, 0]], [[1, 0], [1, 10]]]) await component.getNextUpdatePromise() expect(element.classList.contains('has-selection')).toBe(true) - editor.setSelectedBufferRanges([ - [[0, 0], [0, 0]], - [[1, 0], [1, 0]] - ]) + editor.setSelectedBufferRanges([[[0, 0], [0, 0]], [[1, 0], [1, 0]]]) await component.getNextUpdatePromise() expect(element.classList.contains('has-selection')).toBe(false) }) it('assigns buffer-row and screen-row to each line number as data fields', async () => { - const {editor, element, component} = buildComponent() + const { editor, element, component } = buildComponent() editor.setSoftWrapped(true) await component.getNextUpdatePromise() await setEditorWidthInCharacters(component, 40) { - const bufferRows = queryOnScreenLineNumberElements(element).map((e) => e.dataset.bufferRow) - const screenRows = queryOnScreenLineNumberElements(element).map((e) => e.dataset.screenRow) + const bufferRows = queryOnScreenLineNumberElements(element).map( + e => e.dataset.bufferRow + ) + const screenRows = queryOnScreenLineNumberElements(element).map( + e => e.dataset.screenRow + ) expect(bufferRows).toEqual([ - '0', '1', '2', '3', '3', '4', '5', '6', '6', '6', - '7', '8', '8', '8', '9', '10', '11', '11', '12' + '0', + '1', + '2', + '3', + '3', + '4', + '5', + '6', + '6', + '6', + '7', + '8', + '8', + '8', + '9', + '10', + '11', + '11', + '12' ]) expect(screenRows).toEqual([ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '10', '11', '12', '13', '14', '15', '16', '17', '18' + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18' ]) } editor.getBuffer().insert([2, 0], '\n') await component.getNextUpdatePromise() { - const bufferRows = queryOnScreenLineNumberElements(element).map((e) => e.dataset.bufferRow) - const screenRows = queryOnScreenLineNumberElements(element).map((e) => e.dataset.screenRow) + const bufferRows = queryOnScreenLineNumberElements(element).map( + e => e.dataset.bufferRow + ) + const screenRows = queryOnScreenLineNumberElements(element).map( + e => e.dataset.screenRow + ) expect(bufferRows).toEqual([ - '0', '1', '2', '3', '4', '4', '5', '6', '7', '7', - '7', '8', '9', '9', '9', '10', '11', '12', '12', '13' + '0', + '1', + '2', + '3', + '4', + '4', + '5', + '6', + '7', + '7', + '7', + '8', + '9', + '9', + '9', + '10', + '11', + '12', + '12', + '13' ]) expect(screenRows).toEqual([ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '10', '11', '12', '13', '14', '15', '16', '17', '18', '19' + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19' ]) } }) it('does not blow away class names added to the element by packages when changing the class name', async () => { assertDocumentFocused() - const {component, element, editor} = buildComponent() + const { component, element } = buildComponent() element.classList.add('a', 'b') expect(element.className).toBe('editor a b') element.focus() @@ -820,18 +1066,20 @@ describe('TextEditorComponent', () => { it('does not blow away class names managed by the component when packages change the element class name', async () => { assertDocumentFocused() - const {component, element, editor} = buildComponent({mini: true}) + const { component, element } = buildComponent({ mini: true }) element.classList.add('a', 'b') element.focus() await component.getNextUpdatePromise() expect(element.className).toBe('editor mini a b is-focused') - element.className = 'a c d'; + element.className = 'a c d' await component.getNextUpdatePromise() expect(element.className).toBe('a c d editor is-focused mini') }) it('ignores resize events when the editor is hidden', async () => { - const {component, element, editor} = buildComponent({autoHeight: false}) + const { component, element } = buildComponent({ + autoHeight: false + }) element.style.height = 5 * component.getLineHeight() + 'px' await component.getNextUpdatePromise() const originalClientContainerHeight = component.getClientContainerHeight() @@ -848,16 +1096,28 @@ describe('TextEditorComponent', () => { expect(component.visible).toBe(true) component.didResize() component.didResizeGutterContainer() - expect(component.getClientContainerHeight()).toBe(originalClientContainerHeight) - expect(component.getGutterContainerWidth()).toBe(originalGutterContainerWidth) - expect(component.getLineNumberGutterWidth()).toBe(originalLineNumberGutterWidth) + expect(component.getClientContainerHeight()).toBe( + originalClientContainerHeight + ) + expect(component.getGutterContainerWidth()).toBe( + originalGutterContainerWidth + ) + expect(component.getLineNumberGutterWidth()).toBe( + originalLineNumberGutterWidth + ) // Ensure measurements stay the same after receiving the intersection // observer events. await conditionPromise(() => !component.visible) - expect(component.getClientContainerHeight()).toBe(originalClientContainerHeight) - expect(component.getGutterContainerWidth()).toBe(originalGutterContainerWidth) - expect(component.getLineNumberGutterWidth()).toBe(originalLineNumberGutterWidth) + expect(component.getClientContainerHeight()).toBe( + originalClientContainerHeight + ) + expect(component.getGutterContainerWidth()).toBe( + originalGutterContainerWidth + ) + expect(component.getLineNumberGutterWidth()).toBe( + originalLineNumberGutterWidth + ) }) describe('randomized tests', () => { @@ -881,7 +1141,10 @@ describe('TextEditorComponent', () => { const random = Random(seed) const rowsPerTile = random.intBetween(1, 6) - const {component, element, editor} = buildComponent({rowsPerTile, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile, + autoHeight: false + }) editor.setSoftWrapped(Boolean(random(2))) await setEditorWidthInCharacters(component, random(20)) await setEditorHeightInLines(component, random(10)) @@ -918,20 +1181,37 @@ describe('TextEditorComponent', () => { } else if (k < 95) { editor.setSelectedBufferRange(range) } else { - if (random(2)) component.setScrollTop(random(component.getScrollHeight())) - if (random(2)) component.setScrollLeft(random(component.getScrollWidth())) + if (random(2)) { + component.setScrollTop(random(component.getScrollHeight())) + } + if (random(2)) { + component.setScrollLeft(random(component.getScrollWidth())) + } } component.scheduleUpdate() await component.getNextUpdatePromise() - const renderedLines = queryOnScreenLineElements(element).sort((a, b) => a.dataset.screenRow - b.dataset.screenRow) - const renderedLineNumbers = queryOnScreenLineNumberElements(element).sort((a, b) => a.dataset.screenRow - b.dataset.screenRow) + const renderedLines = queryOnScreenLineElements(element).sort( + (a, b) => a.dataset.screenRow - b.dataset.screenRow + ) + const renderedLineNumbers = queryOnScreenLineNumberElements( + element + ).sort((a, b) => a.dataset.screenRow - b.dataset.screenRow) const renderedStartRow = component.getRenderedStartRow() - const expectedLines = editor.displayLayer.getScreenLines(renderedStartRow, component.getRenderedEndRow()) + const expectedLines = editor.displayLayer.getScreenLines( + renderedStartRow, + component.getRenderedEndRow() + ) - expect(renderedLines.length).toBe(expectedLines.length, failureMessage) - expect(renderedLineNumbers.length).toBe(expectedLines.length, failureMessage) + expect(renderedLines.length).toBe( + expectedLines.length, + failureMessage + ) + expect(renderedLineNumbers.length).toBe( + expectedLines.length, + failureMessage + ) for (let k = 0; k < renderedLines.length; k++) { const expectedLine = expectedLines[k] const expectedText = expectedLine.lineText || ' ' @@ -942,12 +1222,21 @@ describe('TextEditorComponent', () => { // We append zero width NBSPs after folds at the end of the // line in order to support measurement. if (expectedText.endsWith(editor.displayLayer.foldCharacter)) { - renderedText = renderedText.substring(0, renderedText.length - 1) + renderedText = renderedText.substring( + 0, + renderedText.length - 1 + ) } expect(renderedText).toBe(expectedText, failureMessage) - expect(parseInt(renderedLine.dataset.screenRow)).toBe(renderedStartRow + k, failureMessage) - expect(parseInt(renderedLineNumber.dataset.screenRow)).toBe(renderedStartRow + k, failureMessage) + expect(parseInt(renderedLine.dataset.screenRow)).toBe( + renderedStartRow + k, + failureMessage + ) + expect(parseInt(renderedLineNumber.dataset.screenRow)).toBe( + renderedStartRow + k, + failureMessage + ) } } @@ -961,39 +1250,51 @@ describe('TextEditorComponent', () => { describe('mini editors', () => { it('adds the mini attribute and class even when the element is not attached', () => { { - const {element, editor} = buildComponent({mini: true}) + const { element } = buildComponent({ mini: true }) expect(element.hasAttribute('mini')).toBe(true) expect(element.classList.contains('mini')).toBe(true) } { - const {element, editor} = buildComponent({mini: true, attach: false}) + const { element } = buildComponent({ + mini: true, + attach: false + }) expect(element.hasAttribute('mini')).toBe(true) expect(element.classList.contains('mini')).toBe(true) } }) it('does not render the gutter container', () => { - const {component, element, editor} = buildComponent({mini: true}) + const { component, element } = buildComponent({ mini: true }) expect(component.refs.gutterContainer).toBeUndefined() expect(element.querySelector('gutter-container')).toBeNull() }) it('does not render line decorations for the cursor line', async () => { - const {component, element, editor} = buildComponent({mini: true}) - expect(element.querySelector('.line').classList.contains('cursor-line')).toBe(false) + const { component, element, editor } = buildComponent({ mini: true }) + expect( + element.querySelector('.line').classList.contains('cursor-line') + ).toBe(false) - editor.update({mini: false}) + editor.update({ mini: false }) await component.getNextUpdatePromise() - expect(element.querySelector('.line').classList.contains('cursor-line')).toBe(true) + expect( + element.querySelector('.line').classList.contains('cursor-line') + ).toBe(true) - editor.update({mini: true}) + editor.update({ mini: true }) await component.getNextUpdatePromise() - expect(element.querySelector('.line').classList.contains('cursor-line')).toBe(false) + expect( + element.querySelector('.line').classList.contains('cursor-line') + ).toBe(false) }) it('does not render scrollbars', async () => { - const {component, element, editor} = buildComponent({mini: true, autoHeight: false}) + const { component, editor } = buildComponent({ + mini: true, + autoHeight: false + }) await setEditorWidthInCharacters(component, 10) editor.setText('x'.repeat(20) + 'y'.repeat(20)) @@ -1012,8 +1313,8 @@ describe('TextEditorComponent', () => { }) it('focuses the hidden input element and adds the is-focused class when focused', async () => { - const {component, element, editor} = buildComponent() - const {hiddenInput} = component.refs.cursorsAndInput.refs + const { component, element } = buildComponent() + const { hiddenInput } = component.refs.cursorsAndInput.refs expect(document.activeElement).not.toBe(hiddenInput) element.focus() @@ -1032,8 +1333,8 @@ describe('TextEditorComponent', () => { }) it('updates the component when the hidden input is focused directly', async () => { - const {component, element, editor} = buildComponent() - const {hiddenInput} = component.refs.cursorsAndInput.refs + const { component, element } = buildComponent() + const { hiddenInput } = component.refs.cursorsAndInput.refs expect(element.classList.contains('is-focused')).toBe(false) expect(document.activeElement).not.toBe(hiddenInput) @@ -1043,27 +1344,33 @@ describe('TextEditorComponent', () => { }) it('gracefully handles a focus event that occurs prior to the attachedCallback of the element', () => { - const {component, element, editor} = buildComponent({attach: false}) - const parent = document.createElement('text-editor-component-test-element') + const { component, element } = buildComponent({ attach: false }) + const parent = document.createElement( + 'text-editor-component-test-element' + ) parent.appendChild(element) parent.didAttach = () => element.focus() jasmine.attachToDOM(parent) - expect(document.activeElement).toBe(component.refs.cursorsAndInput.refs.hiddenInput) + expect(document.activeElement).toBe( + component.refs.cursorsAndInput.refs.hiddenInput + ) }) it('gracefully handles a focus event that occurs prior to detecting the element has become visible', async () => { - const {component, element, editor} = buildComponent({attach: false}) + const { component, element } = buildComponent({ attach: false }) element.style.display = 'none' jasmine.attachToDOM(element) element.style.display = 'block' element.focus() await component.getNextUpdatePromise() - expect(document.activeElement).toBe(component.refs.cursorsAndInput.refs.hiddenInput) + expect(document.activeElement).toBe( + component.refs.cursorsAndInput.refs.hiddenInput + ) }) it('emits blur events only when focus shifts to something other than the editor itself or its hidden input', () => { - const {element} = buildComponent() + const { element } = buildComponent() let blurEventCount = 0 element.addEventListener('blur', () => blurEventCount++) @@ -1079,20 +1386,29 @@ describe('TextEditorComponent', () => { describe('autoscroll', () => { it('automatically scrolls vertically when the requested range is within the vertical scroll margin of the top or bottom', async () => { - const {component, editor} = buildComponent({height: 120 + horizontalScrollbarHeight}) + const { component, editor } = buildComponent({ + height: 120 + horizontalScrollbarHeight + }) expect(component.getLastVisibleRow()).toBe(7) editor.scrollToScreenRange([[4, 0], [6, 0]]) await component.getNextUpdatePromise() - expect(component.getScrollBottom()).toBe((6 + 1 + editor.verticalScrollMargin) * component.getLineHeight()) + expect(component.getScrollBottom()).toBe( + (6 + 1 + editor.verticalScrollMargin) * component.getLineHeight() + ) editor.scrollToScreenPosition([8, 0]) await component.getNextUpdatePromise() - expect(component.getScrollBottom()).toBe((8 + 1 + editor.verticalScrollMargin) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe( + (8 + 1 + editor.verticalScrollMargin) * + component.measurements.lineHeight + ) editor.scrollToScreenPosition([3, 0]) await component.getNextUpdatePromise() - expect(component.getScrollTop()).toBe((3 - editor.verticalScrollMargin) * component.measurements.lineHeight) + expect(component.getScrollTop()).toBe( + (3 - editor.verticalScrollMargin) * component.measurements.lineHeight + ) editor.scrollToScreenPosition([2, 0]) await component.getNextUpdatePromise() @@ -1100,47 +1416,63 @@ describe('TextEditorComponent', () => { }) it('does not vertically autoscroll by more than half of the visible lines if the editor is shorter than twice the scroll margin', async () => { - const {component, element, editor} = buildComponent({autoHeight: false}) - element.style.height = 5.5 * component.measurements.lineHeight + horizontalScrollbarHeight + 'px' + const { component, element, editor } = buildComponent({ + autoHeight: false + }) + element.style.height = + 5.5 * component.measurements.lineHeight + + horizontalScrollbarHeight + + 'px' await component.getNextUpdatePromise() expect(component.getLastVisibleRow()).toBe(5) const scrollMarginInLines = 2 editor.scrollToScreenPosition([6, 0]) await component.getNextUpdatePromise() - expect(component.getScrollBottom()).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe( + (6 + 1 + scrollMarginInLines) * component.measurements.lineHeight + ) editor.scrollToScreenPosition([6, 4]) await component.getNextUpdatePromise() - expect(component.getScrollBottom()).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe( + (6 + 1 + scrollMarginInLines) * component.measurements.lineHeight + ) editor.scrollToScreenRange([[4, 4], [6, 4]]) await component.getNextUpdatePromise() - expect(component.getScrollTop()).toBe((4 - scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollTop()).toBe( + (4 - scrollMarginInLines) * component.measurements.lineHeight + ) - editor.scrollToScreenRange([[4, 4], [6, 4]], {reversed: false}) + editor.scrollToScreenRange([[4, 4], [6, 4]], { reversed: false }) await component.getNextUpdatePromise() - expect(component.getScrollBottom()).toBe((6 + 1 + scrollMarginInLines) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe( + (6 + 1 + scrollMarginInLines) * component.measurements.lineHeight + ) }) it('autoscrolls the given range to the center of the screen if the `center` option is true', async () => { - const {component, editor} = buildComponent({height: 50}) + const { component, editor } = buildComponent({ height: 50 }) expect(component.getLastVisibleRow()).toBe(2) - editor.scrollToScreenRange([[4, 0], [6, 0]], {center: true}) + editor.scrollToScreenRange([[4, 0], [6, 0]], { center: true }) await component.getNextUpdatePromise() - const actualScrollCenter = (component.getScrollTop() + component.getScrollBottom()) / 2 - const expectedScrollCenter = (4 + 7) / 2 * component.getLineHeight() + const actualScrollCenter = + (component.getScrollTop() + component.getScrollBottom()) / 2 + const expectedScrollCenter = ((4 + 7) / 2) * component.getLineHeight() expect(actualScrollCenter).toBeCloseTo(expectedScrollCenter, 0) }) it('automatically scrolls horizontally when the requested range is within the horizontal scroll margin of the right edge of the gutter or right edge of the scroll container', async () => { - const {component, element, editor} = buildComponent() - const {scrollContainer} = component.refs + const { component, element, editor } = buildComponent() element.style.width = component.getGutterContainerWidth() + - 3 * editor.horizontalScrollMargin * component.measurements.baseCharacterWidth + 'px' + 3 * + editor.horizontalScrollMargin * + component.measurements.baseCharacterWidth + + 'px' await component.getNextUpdatePromise() editor.scrollToScreenRange([[1, 12], [2, 28]]) @@ -1148,59 +1480,77 @@ describe('TextEditorComponent', () => { let expectedScrollLeft = clientLeftForCharacter(component, 1, 12) - lineNodeForScreenRow(component, 1).getBoundingClientRect().left - - (editor.horizontalScrollMargin * component.measurements.baseCharacterWidth) + editor.horizontalScrollMargin * + component.measurements.baseCharacterWidth expect(component.getScrollLeft()).toBeCloseTo(expectedScrollLeft, 0) - editor.scrollToScreenRange([[1, 12], [2, 28]], {reversed: false}) + editor.scrollToScreenRange([[1, 12], [2, 28]], { reversed: false }) await component.getNextUpdatePromise() expectedScrollLeft = component.getGutterContainerWidth() + clientLeftForCharacter(component, 2, 28) - lineNodeForScreenRow(component, 2).getBoundingClientRect().left + - (editor.horizontalScrollMargin * component.measurements.baseCharacterWidth) - + editor.horizontalScrollMargin * + component.measurements.baseCharacterWidth - component.getScrollContainerClientWidth() expect(component.getScrollLeft()).toBeCloseTo(expectedScrollLeft, 0) }) it('does not horizontally autoscroll by more than half of the visible "base-width" characters if the editor is narrower than twice the scroll margin', async () => { - const {component, editor} = buildComponent({autoHeight: false}) - await setEditorWidthInCharacters(component, 1.5 * editor.horizontalScrollMargin) - const editorWidthInChars = component.getScrollContainerClientWidth() / component.getBaseCharacterWidth() + const { component, editor } = buildComponent({ autoHeight: false }) + await setEditorWidthInCharacters( + component, + 1.5 * editor.horizontalScrollMargin + ) + const editorWidthInChars = + component.getScrollContainerClientWidth() / + component.getBaseCharacterWidth() expect(Math.round(editorWidthInChars)).toBe(9) editor.scrollToScreenRange([[6, 10], [6, 15]]) await component.getNextUpdatePromise() let expectedScrollLeft = Math.floor( clientLeftForCharacter(component, 6, 10) - - lineNodeForScreenRow(component, 1).getBoundingClientRect().left - - Math.floor((editorWidthInChars - 1) / 2) * component.getBaseCharacterWidth() + lineNodeForScreenRow(component, 1).getBoundingClientRect().left - + Math.floor((editorWidthInChars - 1) / 2) * + component.getBaseCharacterWidth() ) expect(component.getScrollLeft()).toBe(expectedScrollLeft) }) it('correctly autoscrolls after inserting a line that exceeds the current content width', async () => { - const {component, element, editor} = buildComponent() - element.style.width = component.getGutterContainerWidth() + component.getContentWidth() + 'px' + const { component, element, editor } = buildComponent() + element.style.width = + component.getGutterContainerWidth() + component.getContentWidth() + 'px' await component.getNextUpdatePromise() editor.setCursorScreenPosition([0, Infinity]) editor.insertText('x'.repeat(100)) await component.getNextUpdatePromise() - expect(component.getScrollLeft()).toBe(component.getScrollWidth() - component.getScrollContainerClientWidth()) + expect(component.getScrollLeft()).toBe( + component.getScrollWidth() - component.getScrollContainerClientWidth() + ) }) it('does not try to measure lines that do not exist when the animation frame is delivered', async () => { - const {component, editor} = buildComponent({autoHeight: false, height: 30, rowsPerTile: 2}) + const { component, editor } = buildComponent({ + autoHeight: false, + height: 30, + rowsPerTile: 2 + }) editor.scrollToBufferPosition([11, 5]) editor.getBuffer().deleteRows(11, 12) await component.getNextUpdatePromise() - expect(component.getScrollBottom()).toBe((10 + 1) * component.measurements.lineHeight) + expect(component.getScrollBottom()).toBe( + (10 + 1) * component.measurements.lineHeight + ) }) it('accounts for the presence of horizontal scrollbars that appear during the same frame as the autoscroll', async () => { - const {component, element, editor} = buildComponent({autoHeight: false}) - const {scrollContainer} = component.refs + const { component, element, editor } = buildComponent({ + autoHeight: false + }) element.style.height = component.getContentHeight() / 2 + 'px' element.style.width = component.getScrollWidth() + 'px' await component.getNextUpdatePromise() @@ -1209,8 +1559,12 @@ describe('TextEditorComponent', () => { editor.insertText('\n\n' + 'x'.repeat(100)) await component.getNextUpdatePromise() - expect(component.getScrollTop()).toBe(component.getScrollHeight() - component.getScrollContainerClientHeight()) - expect(component.getScrollLeft()).toBe(component.getScrollWidth() - component.getScrollContainerClientWidth()) + expect(component.getScrollTop()).toBe( + component.getScrollHeight() - component.getScrollContainerClientHeight() + ) + expect(component.getScrollLeft()).toBe( + component.getScrollWidth() - component.getScrollContainerClientWidth() + ) // Scrolling to the top should not throw an error. This failed // previously due to horizontalPositionsToMeasure not being empty after @@ -1223,7 +1577,10 @@ describe('TextEditorComponent', () => { describe('logical scroll positions', () => { it('allows the scrollTop to be changed and queried in terms of rows via setScrollTopRow and getScrollTopRow', () => { - const {component, element, editor} = buildComponent({attach: false, height: 80}) + const { component, element } = buildComponent({ + attach: false, + height: 80 + }) // Caches the scrollTopRow if we don't have measurements component.setScrollTopRow(6) @@ -1234,30 +1591,43 @@ describe('TextEditorComponent', () => { const expectedScrollTop = Math.round(6 * component.getLineHeight()) expect(component.getScrollTopRow()).toBe(6) expect(component.getScrollTop()).toBe(expectedScrollTop) - expect(component.refs.content.style.transform).toBe(`translate(0px, -${expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(0px, -${expectedScrollTop}px)` + ) // Allows the scrollTopRow to be updated while attached component.setScrollTopRow(4) expect(component.getScrollTopRow()).toBe(4) - expect(component.getScrollTop()).toBe(Math.round(4 * component.getLineHeight())) + expect(component.getScrollTop()).toBe( + Math.round(4 * component.getLineHeight()) + ) // Preserves the scrollTopRow when detached element.remove() expect(component.getScrollTopRow()).toBe(4) - expect(component.getScrollTop()).toBe(Math.round(4 * component.getLineHeight())) + expect(component.getScrollTop()).toBe( + Math.round(4 * component.getLineHeight()) + ) component.setScrollTopRow(6) expect(component.getScrollTopRow()).toBe(6) - expect(component.getScrollTop()).toBe(Math.round(6 * component.getLineHeight())) + expect(component.getScrollTop()).toBe( + Math.round(6 * component.getLineHeight()) + ) jasmine.attachToDOM(element) element.style.height = '60px' expect(component.getScrollTopRow()).toBe(6) - expect(component.getScrollTop()).toBe(Math.round(6 * component.getLineHeight())) + expect(component.getScrollTop()).toBe( + Math.round(6 * component.getLineHeight()) + ) }) it('allows the scrollLeft to be changed and queried in terms of base character columns via setScrollLeftColumn and getScrollLeftColumn', () => { - const {component, element} = buildComponent({attach: false, width: 80}) + const { component, element } = buildComponent({ + attach: false, + width: 80 + }) // Caches the scrollTopRow if we don't have measurements component.setScrollLeftColumn(2) @@ -1265,143 +1635,218 @@ describe('TextEditorComponent', () => { // Assigns the scrollTop based on the logical position when attached jasmine.attachToDOM(element) - expect(component.getScrollLeft()).toBeCloseTo(2 * component.getBaseCharacterWidth(), 0) + expect(component.getScrollLeft()).toBeCloseTo( + 2 * component.getBaseCharacterWidth(), + 0 + ) // Allows the scrollTopRow to be updated while attached component.setScrollLeftColumn(4) - expect(component.getScrollLeft()).toBeCloseTo(4 * component.getBaseCharacterWidth(), 0) + expect(component.getScrollLeft()).toBeCloseTo( + 4 * component.getBaseCharacterWidth(), + 0 + ) // Preserves the scrollTopRow when detached element.remove() - expect(component.getScrollLeft()).toBeCloseTo(4 * component.getBaseCharacterWidth(), 0) + expect(component.getScrollLeft()).toBeCloseTo( + 4 * component.getBaseCharacterWidth(), + 0 + ) component.setScrollLeftColumn(6) - expect(component.getScrollLeft()).toBeCloseTo(6 * component.getBaseCharacterWidth(), 0) + expect(component.getScrollLeft()).toBeCloseTo( + 6 * component.getBaseCharacterWidth(), + 0 + ) jasmine.attachToDOM(element) element.style.width = '60px' - expect(component.getScrollLeft()).toBeCloseTo(6 * component.getBaseCharacterWidth(), 0) + expect(component.getScrollLeft()).toBeCloseTo( + 6 * component.getBaseCharacterWidth(), + 0 + ) }) }) describe('scrolling via the mouse wheel', () => { it('scrolls vertically or horizontally depending on whether deltaX or deltaY is larger', () => { const scrollSensitivity = 30 - const {component, editor} = buildComponent({height: 50, width: 50, scrollSensitivity}) + const { component } = buildComponent({ + height: 50, + width: 50, + scrollSensitivity + }) { const expectedScrollTop = 20 * (scrollSensitivity / 100) const expectedScrollLeft = component.getScrollLeft() - component.didMouseWheel({wheelDeltaX: -5, wheelDeltaY: -20}) + component.didMouseWheel({ wheelDeltaX: -5, wheelDeltaY: -20 }) expect(component.getScrollTop()).toBe(expectedScrollTop) expect(component.getScrollLeft()).toBe(expectedScrollLeft) - expect(component.refs.content.style.transform).toBe(`translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)` + ) } { - const expectedScrollTop = component.getScrollTop() - (10 * (scrollSensitivity / 100)) + const expectedScrollTop = + component.getScrollTop() - 10 * (scrollSensitivity / 100) const expectedScrollLeft = component.getScrollLeft() - component.didMouseWheel({wheelDeltaX: -5, wheelDeltaY: 10}) + component.didMouseWheel({ wheelDeltaX: -5, wheelDeltaY: 10 }) expect(component.getScrollTop()).toBe(expectedScrollTop) expect(component.getScrollLeft()).toBe(expectedScrollLeft) - expect(component.refs.content.style.transform).toBe(`translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)` + ) } { const expectedScrollTop = component.getScrollTop() const expectedScrollLeft = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: -20, wheelDeltaY: 10}) + component.didMouseWheel({ wheelDeltaX: -20, wheelDeltaY: 10 }) expect(component.getScrollTop()).toBe(expectedScrollTop) expect(component.getScrollLeft()).toBe(expectedScrollLeft) - expect(component.refs.content.style.transform).toBe(`translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)` + ) } { const expectedScrollTop = component.getScrollTop() - const expectedScrollLeft = component.getScrollLeft() - (10 * (scrollSensitivity / 100)) - component.didMouseWheel({wheelDeltaX: 10, wheelDeltaY: -8}) + const expectedScrollLeft = + component.getScrollLeft() - 10 * (scrollSensitivity / 100) + component.didMouseWheel({ wheelDeltaX: 10, wheelDeltaY: -8 }) expect(component.getScrollTop()).toBe(expectedScrollTop) expect(component.getScrollLeft()).toBe(expectedScrollLeft) - expect(component.refs.content.style.transform).toBe(`translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(${-expectedScrollLeft}px, ${-expectedScrollTop}px)` + ) } }) it('inverts deltaX and deltaY when holding shift on Windows and Linux', async () => { const scrollSensitivity = 50 - const {component, editor} = buildComponent({height: 50, width: 50, scrollSensitivity}) + const { component } = buildComponent({ + height: 50, + width: 50, + scrollSensitivity + }) component.props.platform = 'linux' { const expectedScrollTop = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: -20}) + component.didMouseWheel({ wheelDeltaX: 0, wheelDeltaY: -20 }) expect(component.getScrollTop()).toBe(expectedScrollTop) - expect(component.refs.content.style.transform).toBe(`translate(0px, -${expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(0px, -${expectedScrollTop}px)` + ) await setScrollTop(component, 0) } { const expectedScrollLeft = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: -20, shiftKey: true}) + component.didMouseWheel({ + wheelDeltaX: 0, + wheelDeltaY: -20, + shiftKey: true + }) expect(component.getScrollLeft()).toBe(expectedScrollLeft) - expect(component.refs.content.style.transform).toBe(`translate(-${expectedScrollLeft}px, 0px)`) + expect(component.refs.content.style.transform).toBe( + `translate(-${expectedScrollLeft}px, 0px)` + ) await setScrollLeft(component, 0) } { const expectedScrollTop = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: -20, wheelDeltaY: 0, shiftKey: true}) + component.didMouseWheel({ + wheelDeltaX: -20, + wheelDeltaY: 0, + shiftKey: true + }) expect(component.getScrollTop()).toBe(expectedScrollTop) - expect(component.refs.content.style.transform).toBe(`translate(0px, -${expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(0px, -${expectedScrollTop}px)` + ) await setScrollTop(component, 0) } component.props.platform = 'win32' { const expectedScrollTop = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: -20}) + component.didMouseWheel({ wheelDeltaX: 0, wheelDeltaY: -20 }) expect(component.getScrollTop()).toBe(expectedScrollTop) - expect(component.refs.content.style.transform).toBe(`translate(0px, -${expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(0px, -${expectedScrollTop}px)` + ) await setScrollTop(component, 0) } { const expectedScrollLeft = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: -20, shiftKey: true}) + component.didMouseWheel({ + wheelDeltaX: 0, + wheelDeltaY: -20, + shiftKey: true + }) expect(component.getScrollLeft()).toBe(expectedScrollLeft) - expect(component.refs.content.style.transform).toBe(`translate(-${expectedScrollLeft}px, 0px)`) + expect(component.refs.content.style.transform).toBe( + `translate(-${expectedScrollLeft}px, 0px)` + ) await setScrollLeft(component, 0) } { const expectedScrollTop = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: -20, wheelDeltaY: 0, shiftKey: true}) + component.didMouseWheel({ + wheelDeltaX: -20, + wheelDeltaY: 0, + shiftKey: true + }) expect(component.getScrollTop()).toBe(expectedScrollTop) - expect(component.refs.content.style.transform).toBe(`translate(0px, -${expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(0px, -${expectedScrollTop}px)` + ) await setScrollTop(component, 0) } component.props.platform = 'darwin' { const expectedScrollTop = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: -20}) + component.didMouseWheel({ wheelDeltaX: 0, wheelDeltaY: -20 }) expect(component.getScrollTop()).toBe(expectedScrollTop) - expect(component.refs.content.style.transform).toBe(`translate(0px, -${expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(0px, -${expectedScrollTop}px)` + ) await setScrollTop(component, 0) } { const expectedScrollTop = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: -20, shiftKey: true}) + component.didMouseWheel({ + wheelDeltaX: 0, + wheelDeltaY: -20, + shiftKey: true + }) expect(component.getScrollTop()).toBe(expectedScrollTop) - expect(component.refs.content.style.transform).toBe(`translate(0px, -${expectedScrollTop}px)`) + expect(component.refs.content.style.transform).toBe( + `translate(0px, -${expectedScrollTop}px)` + ) await setScrollTop(component, 0) } { const expectedScrollLeft = 20 * (scrollSensitivity / 100) - component.didMouseWheel({wheelDeltaX: -20, wheelDeltaY: 0, shiftKey: true}) + component.didMouseWheel({ + wheelDeltaX: -20, + wheelDeltaY: 0, + shiftKey: true + }) expect(component.getScrollLeft()).toBe(expectedScrollLeft) - expect(component.refs.content.style.transform).toBe(`translate(-${expectedScrollLeft}px, 0px)`) + expect(component.refs.content.style.transform).toBe( + `translate(-${expectedScrollLeft}px, 0px)` + ) await setScrollLeft(component, 0) } }) @@ -1409,12 +1854,17 @@ describe('TextEditorComponent', () => { describe('scrolling via the API', () => { it('ignores scroll requests to NaN, null or undefined positions', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, autoHeight: false}) + const { component } = buildComponent({ + rowsPerTile: 2, + autoHeight: false + }) await setEditorHeightInLines(component, 3) await setEditorWidthInCharacters(component, 10) const initialScrollTop = Math.round(2 * component.getLineHeight()) - const initialScrollLeft = Math.round(5 * component.getBaseCharacterWidth()) + const initialScrollLeft = Math.round( + 5 * component.getBaseCharacterWidth() + ) setScrollTop(component, initialScrollTop) setScrollLeft(component, initialScrollLeft) await component.getNextUpdatePromise() @@ -1441,7 +1891,9 @@ describe('TextEditorComponent', () => { describe('line and line number decorations', () => { it('adds decoration classes on screen lines spanned by decorated markers', async () => { - const {component, element, editor} = buildComponent({softWrapped: true}) + const { component, editor } = buildComponent({ + softWrapped: true + }) await setEditorWidthInCharacters(component, 55) expect(lineNodeForScreenRow(component, 3).textContent).toBe( ' var pivot = items.shift(), current, left = [], ' @@ -1452,144 +1904,314 @@ describe('TextEditorComponent', () => { const marker1 = editor.markScreenRange([[1, 10], [3, 10]]) const layer = editor.addMarkerLayer() - const marker2 = layer.markScreenPosition([5, 0]) - const marker3 = layer.markScreenPosition([8, 0]) + layer.markScreenPosition([5, 0]) + layer.markScreenPosition([8, 0]) const marker4 = layer.markScreenPosition([10, 0]) - const markerDecoration = editor.decorateMarker(marker1, {type: ['line', 'line-number'], class: 'a'}) - const layerDecoration = editor.decorateMarkerLayer(layer, {type: ['line', 'line-number'], class: 'b'}) - layerDecoration.setPropertiesForMarker(marker4, {type: 'line', class: 'c'}) + editor.decorateMarker(marker1, { + type: ['line', 'line-number'], + class: 'a' + }) + const layerDecoration = editor.decorateMarkerLayer(layer, { + type: ['line', 'line-number'], + class: 'b' + }) + layerDecoration.setPropertiesForMarker(marker4, { + type: 'line', + class: 'c' + }) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 4).classList.contains('a')).toBe(false) - expect(lineNodeForScreenRow(component, 5).classList.contains('b')).toBe(true) - expect(lineNodeForScreenRow(component, 8).classList.contains('b')).toBe(true) - expect(lineNodeForScreenRow(component, 10).classList.contains('b')).toBe(false) - expect(lineNodeForScreenRow(component, 10).classList.contains('c')).toBe(true) + expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 4).classList.contains('a')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 5).classList.contains('b')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 8).classList.contains('b')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 10).classList.contains('b')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 10).classList.contains('c')).toBe( + true + ) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 2).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 3).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 4).classList.contains('a')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 5).classList.contains('b')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 8).classList.contains('b')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 10).classList.contains('b')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 10).classList.contains('c')).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 2).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 3).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 4).classList.contains('a') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 5).classList.contains('b') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 8).classList.contains('b') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 10).classList.contains('b') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 10).classList.contains('c') + ).toBe(false) marker1.setScreenRange([[5, 0], [8, 0]]) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(false) - expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe(false) - expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe(false) - expect(lineNodeForScreenRow(component, 4).classList.contains('a')).toBe(false) - expect(lineNodeForScreenRow(component, 5).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 5).classList.contains('b')).toBe(true) - expect(lineNodeForScreenRow(component, 6).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 7).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 8).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 8).classList.contains('b')).toBe(true) + expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 4).classList.contains('a')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 5).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 5).classList.contains('b')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 6).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 7).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 8).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 8).classList.contains('b')).toBe( + true + ) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('a')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 2).classList.contains('a')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 3).classList.contains('a')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 4).classList.contains('a')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 5).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 5).classList.contains('b')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 6).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 7).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 8).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 8).classList.contains('b')).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('a') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 2).classList.contains('a') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 3).classList.contains('a') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 4).classList.contains('a') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 5).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 5).classList.contains('b') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 6).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 7).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 8).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 8).classList.contains('b') + ).toBe(true) }) it('honors the onlyEmpty and onlyNonEmpty decoration options', async () => { - const {component, element, editor} = buildComponent() + const { component, editor } = buildComponent() const marker = editor.markScreenPosition([1, 0]) - editor.decorateMarker(marker, {type: ['line', 'line-number'], class: 'a', onlyEmpty: true}) - editor.decorateMarker(marker, {type: ['line', 'line-number'], class: 'b', onlyNonEmpty: true}) - editor.decorateMarker(marker, {type: ['line', 'line-number'], class: 'c'}) + editor.decorateMarker(marker, { + type: ['line', 'line-number'], + class: 'a', + onlyEmpty: true + }) + editor.decorateMarker(marker, { + type: ['line', 'line-number'], + class: 'b', + onlyNonEmpty: true + }) + editor.decorateMarker(marker, { + type: ['line', 'line-number'], + class: 'c' + }) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 1).classList.contains('b')).toBe(false) - expect(lineNodeForScreenRow(component, 1).classList.contains('c')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('b')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('c')).toBe(true) + expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 1).classList.contains('b')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 1).classList.contains('c')).toBe( + true + ) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('a') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('b') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('c') + ).toBe(true) marker.setScreenRange([[1, 0], [2, 4]]) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(false) - expect(lineNodeForScreenRow(component, 1).classList.contains('b')).toBe(true) - expect(lineNodeForScreenRow(component, 1).classList.contains('c')).toBe(true) - expect(lineNodeForScreenRow(component, 2).classList.contains('b')).toBe(true) - expect(lineNodeForScreenRow(component, 2).classList.contains('c')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('a')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('b')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('c')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 2).classList.contains('b')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 2).classList.contains('c')).toBe(true) + expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 1).classList.contains('b')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 1).classList.contains('c')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 2).classList.contains('b')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 2).classList.contains('c')).toBe( + true + ) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('a') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('b') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('c') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 2).classList.contains('b') + ).toBe(true) + expect( + lineNumberNodeForScreenRow(component, 2).classList.contains('c') + ).toBe(true) }) it('honors the onlyHead option', async () => { - const {component, element, editor} = buildComponent() + const { component, editor } = buildComponent() const marker = editor.markScreenRange([[1, 4], [3, 4]]) - editor.decorateMarker(marker, {type: ['line', 'line-number'], class: 'a', onlyHead: true}) + editor.decorateMarker(marker, { + type: ['line', 'line-number'], + class: 'a', + onlyHead: true + }) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(false) - expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe(true) - expect(lineNumberNodeForScreenRow(component, 1).classList.contains('a')).toBe(false) - expect(lineNumberNodeForScreenRow(component, 3).classList.contains('a')).toBe(true) + expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe( + false + ) + expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe( + true + ) + expect( + lineNumberNodeForScreenRow(component, 1).classList.contains('a') + ).toBe(false) + expect( + lineNumberNodeForScreenRow(component, 3).classList.contains('a') + ).toBe(true) }) it('only decorates the last row of non-empty ranges that end at column 0 if omitEmptyLastRow is false', async () => { - const {component, element, editor} = buildComponent() + const { component, editor } = buildComponent() const marker = editor.markScreenRange([[1, 0], [3, 0]]) - editor.decorateMarker(marker, {type: ['line', 'line-number'], class: 'a'}) - editor.decorateMarker(marker, {type: ['line', 'line-number'], class: 'b', omitEmptyLastRow: false}) + editor.decorateMarker(marker, { + type: ['line', 'line-number'], + class: 'a' + }) + editor.decorateMarker(marker, { + type: ['line', 'line-number'], + class: 'b', + omitEmptyLastRow: false + }) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe(true) - expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe(false) + expect(lineNodeForScreenRow(component, 1).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 3).classList.contains('a')).toBe( + false + ) - expect(lineNodeForScreenRow(component, 1).classList.contains('b')).toBe(true) - expect(lineNodeForScreenRow(component, 2).classList.contains('b')).toBe(true) - expect(lineNodeForScreenRow(component, 3).classList.contains('b')).toBe(true) + expect(lineNodeForScreenRow(component, 1).classList.contains('b')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 2).classList.contains('b')).toBe( + true + ) + expect(lineNodeForScreenRow(component, 3).classList.contains('b')).toBe( + true + ) }) it('does not decorate invalidated markers', async () => { - const {component, element, editor} = buildComponent() - const marker = editor.markScreenRange([[1, 0], [3, 0]], {invalidate: 'touch'}) - editor.decorateMarker(marker, {type: ['line', 'line-number'], class: 'a'}) + const { component, editor } = buildComponent() + const marker = editor.markScreenRange([[1, 0], [3, 0]], { + invalidate: 'touch' + }) + editor.decorateMarker(marker, { + type: ['line', 'line-number'], + class: 'a' + }) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe(true) + expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe( + true + ) editor.getBuffer().insert([2, 0], 'x') expect(marker.isValid()).toBe(false) await component.getNextUpdatePromise() - expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe(false) + expect(lineNodeForScreenRow(component, 2).classList.contains('a')).toBe( + false + ) }) }) describe('highlight decorations', () => { it('renders single-line highlights', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() const marker = editor.markScreenRange([[1, 2], [1, 10]]) - editor.decorateMarker(marker, {type: 'highlight', class: 'a'}) + editor.decorateMarker(marker, { type: 'highlight', class: 'a' }) await component.getNextUpdatePromise() { const regions = element.querySelectorAll('.highlight.a .region.a') expect(regions.length).toBe(1) const regionRect = regions[0].getBoundingClientRect() - expect(regionRect.top).toBe(lineNodeForScreenRow(component, 1).getBoundingClientRect().top) - expect(Math.round(regionRect.left)).toBe(clientLeftForCharacter(component, 1, 2)) - expect(Math.round(regionRect.right)).toBe(clientLeftForCharacter(component, 1, 10)) + expect(regionRect.top).toBe( + lineNodeForScreenRow(component, 1).getBoundingClientRect().top + ) + expect(Math.round(regionRect.left)).toBe( + clientLeftForCharacter(component, 1, 2) + ) + expect(Math.round(regionRect.right)).toBe( + clientLeftForCharacter(component, 1, 10) + ) } marker.setScreenRange([[1, 4], [1, 8]]) @@ -1599,17 +2221,25 @@ describe('TextEditorComponent', () => { const regions = element.querySelectorAll('.highlight.a .region.a') expect(regions.length).toBe(1) const regionRect = regions[0].getBoundingClientRect() - expect(regionRect.top).toBe(lineNodeForScreenRow(component, 1).getBoundingClientRect().top) - expect(regionRect.bottom).toBe(lineNodeForScreenRow(component, 1).getBoundingClientRect().bottom) - expect(Math.round(regionRect.left)).toBe(clientLeftForCharacter(component, 1, 4)) - expect(Math.round(regionRect.right)).toBe(clientLeftForCharacter(component, 1, 8)) + expect(regionRect.top).toBe( + lineNodeForScreenRow(component, 1).getBoundingClientRect().top + ) + expect(regionRect.bottom).toBe( + lineNodeForScreenRow(component, 1).getBoundingClientRect().bottom + ) + expect(Math.round(regionRect.left)).toBe( + clientLeftForCharacter(component, 1, 4) + ) + expect(Math.round(regionRect.right)).toBe( + clientLeftForCharacter(component, 1, 8) + ) } }) it('renders multi-line highlights', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3}) + const { component, element, editor } = buildComponent({ rowsPerTile: 3 }) const marker = editor.markScreenRange([[2, 4], [3, 4]]) - editor.decorateMarker(marker, {type: 'highlight', class: 'a'}) + editor.decorateMarker(marker, { type: 'highlight', class: 'a' }) await component.getNextUpdatePromise() @@ -1619,16 +2249,32 @@ describe('TextEditorComponent', () => { const regions = element.querySelectorAll('.highlight.a .region.a') expect(regions.length).toBe(2) const region0Rect = regions[0].getBoundingClientRect() - expect(region0Rect.top).toBe(lineNodeForScreenRow(component, 2).getBoundingClientRect().top) - expect(region0Rect.bottom).toBe(lineNodeForScreenRow(component, 2).getBoundingClientRect().bottom) - expect(Math.round(region0Rect.left)).toBe(clientLeftForCharacter(component, 2, 4)) - expect(Math.round(region0Rect.right)).toBe(component.refs.content.getBoundingClientRect().right) + expect(region0Rect.top).toBe( + lineNodeForScreenRow(component, 2).getBoundingClientRect().top + ) + expect(region0Rect.bottom).toBe( + lineNodeForScreenRow(component, 2).getBoundingClientRect().bottom + ) + expect(Math.round(region0Rect.left)).toBe( + clientLeftForCharacter(component, 2, 4) + ) + expect(Math.round(region0Rect.right)).toBe( + component.refs.content.getBoundingClientRect().right + ) const region1Rect = regions[1].getBoundingClientRect() - expect(region1Rect.top).toBe(lineNodeForScreenRow(component, 3).getBoundingClientRect().top) - expect(region1Rect.bottom).toBe(lineNodeForScreenRow(component, 3).getBoundingClientRect().bottom) - expect(Math.round(region1Rect.left)).toBe(clientLeftForCharacter(component, 3, 0)) - expect(Math.round(region1Rect.right)).toBe(clientLeftForCharacter(component, 3, 4)) + expect(region1Rect.top).toBe( + lineNodeForScreenRow(component, 3).getBoundingClientRect().top + ) + expect(region1Rect.bottom).toBe( + lineNodeForScreenRow(component, 3).getBoundingClientRect().bottom + ) + expect(Math.round(region1Rect.left)).toBe( + clientLeftForCharacter(component, 3, 0) + ) + expect(Math.round(region1Rect.right)).toBe( + clientLeftForCharacter(component, 3, 4) + ) } marker.setScreenRange([[2, 4], [5, 4]]) @@ -1641,29 +2287,59 @@ describe('TextEditorComponent', () => { expect(regions.length).toBe(3) const region0Rect = regions[0].getBoundingClientRect() - expect(region0Rect.top).toBe(lineNodeForScreenRow(component, 2).getBoundingClientRect().top) - expect(region0Rect.bottom).toBe(lineNodeForScreenRow(component, 2).getBoundingClientRect().bottom) - expect(Math.round(region0Rect.left)).toBe(clientLeftForCharacter(component, 2, 4)) - expect(Math.round(region0Rect.right)).toBe(component.refs.content.getBoundingClientRect().right) + expect(region0Rect.top).toBe( + lineNodeForScreenRow(component, 2).getBoundingClientRect().top + ) + expect(region0Rect.bottom).toBe( + lineNodeForScreenRow(component, 2).getBoundingClientRect().bottom + ) + expect(Math.round(region0Rect.left)).toBe( + clientLeftForCharacter(component, 2, 4) + ) + expect(Math.round(region0Rect.right)).toBe( + component.refs.content.getBoundingClientRect().right + ) const region1Rect = regions[1].getBoundingClientRect() - expect(region1Rect.top).toBe(lineNodeForScreenRow(component, 3).getBoundingClientRect().top) - expect(region1Rect.bottom).toBe(lineNodeForScreenRow(component, 5).getBoundingClientRect().top) - expect(Math.round(region1Rect.left)).toBe(component.refs.content.getBoundingClientRect().left) - expect(Math.round(region1Rect.right)).toBe(component.refs.content.getBoundingClientRect().right) + expect(region1Rect.top).toBe( + lineNodeForScreenRow(component, 3).getBoundingClientRect().top + ) + expect(region1Rect.bottom).toBe( + lineNodeForScreenRow(component, 5).getBoundingClientRect().top + ) + expect(Math.round(region1Rect.left)).toBe( + component.refs.content.getBoundingClientRect().left + ) + expect(Math.round(region1Rect.right)).toBe( + component.refs.content.getBoundingClientRect().right + ) const region2Rect = regions[2].getBoundingClientRect() - expect(region2Rect.top).toBe(lineNodeForScreenRow(component, 5).getBoundingClientRect().top) - expect(region2Rect.bottom).toBe(lineNodeForScreenRow(component, 6).getBoundingClientRect().top) - expect(Math.round(region2Rect.left)).toBe(component.refs.content.getBoundingClientRect().left) - expect(Math.round(region2Rect.right)).toBe(clientLeftForCharacter(component, 5, 4)) + expect(region2Rect.top).toBe( + lineNodeForScreenRow(component, 5).getBoundingClientRect().top + ) + expect(region2Rect.bottom).toBe( + lineNodeForScreenRow(component, 6).getBoundingClientRect().top + ) + expect(Math.round(region2Rect.left)).toBe( + component.refs.content.getBoundingClientRect().left + ) + expect(Math.round(region2Rect.right)).toBe( + clientLeftForCharacter(component, 5, 4) + ) } }) it('can flash highlight decorations', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, height: 200}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 3, + height: 200 + }) const marker = editor.markScreenRange([[2, 4], [3, 4]]) - const decoration = editor.decorateMarker(marker, {type: 'highlight', class: 'a'}) + const decoration = editor.decorateMarker(marker, { + type: 'highlight', + class: 'a' + }) decoration.flash('b', 10) // Flash on initial appearance of highlight @@ -1695,9 +2371,15 @@ describe('TextEditorComponent', () => { }) it("flashing a highlight decoration doesn't unflash other highlight decorations", async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, height: 200}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 3, + height: 200 + }) const marker = editor.markScreenRange([[2, 4], [3, 4]]) - const decoration = editor.decorateMarker(marker, {type: 'highlight', class: 'a'}) + const decoration = editor.decorateMarker(marker, { + type: 'highlight', + class: 'a' + }) // Flash one class decoration.flash('c', 1000) @@ -1714,34 +2396,46 @@ describe('TextEditorComponent', () => { }) it('supports layer decorations', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 12}) + const { component, element, editor } = buildComponent({ rowsPerTile: 12 }) const markerLayer = editor.addMarkerLayer() const marker1 = markerLayer.markScreenRange([[2, 4], [3, 4]]) const marker2 = markerLayer.markScreenRange([[5, 6], [7, 8]]) - const decoration = editor.decorateMarkerLayer(markerLayer, {type: 'highlight', class: 'a'}) + const decoration = editor.decorateMarkerLayer(markerLayer, { + type: 'highlight', + class: 'a' + }) await component.getNextUpdatePromise() const highlights = element.querySelectorAll('.highlight') expect(highlights[0].classList.contains('a')).toBe(true) expect(highlights[1].classList.contains('a')).toBe(true) - decoration.setPropertiesForMarker(marker1, {type: 'highlight', class: 'b'}) + decoration.setPropertiesForMarker(marker1, { + type: 'highlight', + class: 'b' + }) await component.getNextUpdatePromise() expect(highlights[0].classList.contains('b')).toBe(true) expect(highlights[1].classList.contains('a')).toBe(true) decoration.setPropertiesForMarker(marker1, null) - decoration.setPropertiesForMarker(marker2, {type: 'highlight', class: 'c'}) + decoration.setPropertiesForMarker(marker2, { + type: 'highlight', + class: 'c' + }) await component.getNextUpdatePromise() expect(highlights[0].classList.contains('a')).toBe(true) expect(highlights[1].classList.contains('c')).toBe(true) }) it('clears highlights when recycling a tile that previously contained highlights and now does not', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 2, + autoHeight: false + }) await setEditorHeightInLines(component, 2) const marker = editor.markScreenRange([[1, 2], [1, 10]]) - editor.decorateMarker(marker, {type: 'highlight', class: 'a'}) + editor.decorateMarker(marker, { type: 'highlight', class: 'a' }) await component.getNextUpdatePromise() expect(element.querySelectorAll('.highlight.a').length).toBe(1) @@ -1751,43 +2445,56 @@ describe('TextEditorComponent', () => { }) it('does not move existing highlights when adding or removing other highlight decorations (regression)', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() const marker1 = editor.markScreenRange([[1, 6], [1, 10]]) - editor.decorateMarker(marker1, {type: 'highlight', class: 'a'}) + editor.decorateMarker(marker1, { type: 'highlight', class: 'a' }) await component.getNextUpdatePromise() const marker1Region = element.querySelector('.highlight.a') - expect(Array.from(marker1Region.parentElement.children).indexOf(marker1Region)).toBe(0) + expect( + Array.from(marker1Region.parentElement.children).indexOf(marker1Region) + ).toBe(0) const marker2 = editor.markScreenRange([[1, 2], [1, 4]]) - editor.decorateMarker(marker2, {type: 'highlight', class: 'b'}) + editor.decorateMarker(marker2, { type: 'highlight', class: 'b' }) await component.getNextUpdatePromise() const marker2Region = element.querySelector('.highlight.b') - expect(Array.from(marker1Region.parentElement.children).indexOf(marker1Region)).toBe(0) - expect(Array.from(marker2Region.parentElement.children).indexOf(marker2Region)).toBe(1) + expect( + Array.from(marker1Region.parentElement.children).indexOf(marker1Region) + ).toBe(0) + expect( + Array.from(marker2Region.parentElement.children).indexOf(marker2Region) + ).toBe(1) marker2.destroy() await component.getNextUpdatePromise() - expect(Array.from(marker1Region.parentElement.children).indexOf(marker1Region)).toBe(0) + expect( + Array.from(marker1Region.parentElement.children).indexOf(marker1Region) + ).toBe(0) }) it('correctly positions highlights that end on rows preceding or following block decorations', async () => { - const {editor, element, component} = buildComponent() + const { editor, element, component } = buildComponent() const item1 = document.createElement('div') item1.style.height = '30px' item1.style.backgroundColor = 'blue' editor.decorateMarker(editor.markBufferPosition([4, 0]), { - type: 'block', position: 'after', item: item1 + type: 'block', + position: 'after', + item: item1 }) const item2 = document.createElement('div') item2.style.height = '30px' item2.style.backgroundColor = 'yellow' editor.decorateMarker(editor.markBufferPosition([4, 0]), { - type: 'block', position: 'before', item: item2 + type: 'block', + position: 'before', + item: item2 }) editor.decorateMarker(editor.markBufferRange([[3, 0], [4, Infinity]]), { - type: 'highlight', class: 'highlight' + type: 'highlight', + class: 'highlight' }) await component.getNextUpdatePromise() @@ -1806,13 +2513,21 @@ describe('TextEditorComponent', () => { fakeWindow.style.backgroundColor = 'blue' fakeWindow.appendChild(component.element) jasmine.attachToDOM(fakeWindow) - spyOn(component, 'getWindowInnerWidth').andCallFake(() => fakeWindow.getBoundingClientRect().width) - spyOn(component, 'getWindowInnerHeight').andCallFake(() => fakeWindow.getBoundingClientRect().height) + spyOn(component, 'getWindowInnerWidth').andCallFake( + () => fakeWindow.getBoundingClientRect().width + ) + spyOn(component, 'getWindowInnerHeight').andCallFake( + () => fakeWindow.getBoundingClientRect().height + ) return fakeWindow } it('renders overlay elements at the specified screen position unless it would overflow the window', async () => { - const {component, element, editor} = buildComponent({width: 200, height: 100, attach: false}) + const { component, editor } = buildComponent({ + width: 200, + height: 100, + attach: false + }) const fakeWindow = attachFakeWindow(component) await setScrollTop(component, 50) @@ -1826,62 +2541,94 @@ describe('TextEditorComponent', () => { overlayElement.style.margin = '3px' overlayElement.style.backgroundColor = 'red' - const decoration = editor.decorateMarker(marker, {type: 'overlay', item: overlayElement, class: 'a'}) + const decoration = editor.decorateMarker(marker, { + type: 'overlay', + item: overlayElement, + class: 'a' + }) await component.getNextUpdatePromise() const overlayComponent = component.overlayComponents.values().next().value const overlayWrapper = overlayElement.parentElement expect(overlayWrapper.classList.contains('a')).toBe(true) - expect(overlayWrapper.getBoundingClientRect().top).toBe(clientTopForLine(component, 5)) - expect(overlayWrapper.getBoundingClientRect().left).toBe(clientLeftForCharacter(component, 4, 25)) + expect(overlayWrapper.getBoundingClientRect().top).toBe( + clientTopForLine(component, 5) + ) + expect(overlayWrapper.getBoundingClientRect().left).toBe( + clientLeftForCharacter(component, 4, 25) + ) // Updates the horizontal position on scroll await setScrollLeft(component, 150) - expect(overlayWrapper.getBoundingClientRect().left).toBe(clientLeftForCharacter(component, 4, 25)) + expect(overlayWrapper.getBoundingClientRect().left).toBe( + clientLeftForCharacter(component, 4, 25) + ) // Shifts the overlay horizontally to ensure the overlay element does not // overflow the window await setScrollLeft(component, 30) - expect(overlayElement.getBoundingClientRect().right).toBe(fakeWindow.getBoundingClientRect().right) + expect(overlayElement.getBoundingClientRect().right).toBe( + fakeWindow.getBoundingClientRect().right + ) await setScrollLeft(component, 280) - expect(overlayElement.getBoundingClientRect().left).toBe(fakeWindow.getBoundingClientRect().left) + expect(overlayElement.getBoundingClientRect().left).toBe( + fakeWindow.getBoundingClientRect().left + ) // Updates the vertical position on scroll await setScrollTop(component, 60) - expect(overlayWrapper.getBoundingClientRect().top).toBe(clientTopForLine(component, 5)) + expect(overlayWrapper.getBoundingClientRect().top).toBe( + clientTopForLine(component, 5) + ) // Flips the overlay vertically to ensure the overlay element does not // overflow the bottom of the window setScrollLeft(component, 100) await setScrollTop(component, 0) - expect(overlayWrapper.getBoundingClientRect().bottom).toBe(clientTopForLine(component, 4)) + expect(overlayWrapper.getBoundingClientRect().bottom).toBe( + clientTopForLine(component, 4) + ) // Flips the overlay vertically on overlay resize if necessary await setScrollTop(component, 20) - expect(overlayWrapper.getBoundingClientRect().top).toBe(clientTopForLine(component, 5)) + expect(overlayWrapper.getBoundingClientRect().top).toBe( + clientTopForLine(component, 5) + ) overlayElement.style.height = 60 + 'px' await overlayComponent.getNextUpdatePromise() - expect(overlayWrapper.getBoundingClientRect().bottom).toBe(clientTopForLine(component, 4)) + expect(overlayWrapper.getBoundingClientRect().bottom).toBe( + clientTopForLine(component, 4) + ) // Does not flip the overlay vertically if it would overflow the top of the window overlayElement.style.height = 80 + 'px' await overlayComponent.getNextUpdatePromise() - expect(overlayWrapper.getBoundingClientRect().top).toBe(clientTopForLine(component, 5)) + expect(overlayWrapper.getBoundingClientRect().top).toBe( + clientTopForLine(component, 5) + ) // Can update overlay wrapper class - decoration.setProperties({type: 'overlay', item: overlayElement, class: 'b'}) + decoration.setProperties({ + type: 'overlay', + item: overlayElement, + class: 'b' + }) await component.getNextUpdatePromise() expect(overlayWrapper.classList.contains('a')).toBe(false) expect(overlayWrapper.classList.contains('b')).toBe(true) - decoration.setProperties({type: 'overlay', item: overlayElement}) + decoration.setProperties({ type: 'overlay', item: overlayElement }) await component.getNextUpdatePromise() expect(overlayWrapper.classList.contains('b')).toBe(false) }) it('does not attempt to avoid overflowing the window if `avoidOverflow` is false on the decoration', async () => { - const {component, element, editor} = buildComponent({width: 200, height: 100, attach: false}) + const { component, editor } = buildComponent({ + width: 200, + height: 100, + attach: false + }) const fakeWindow = attachFakeWindow(component) const overlayElement = document.createElement('div') overlayElement.style.width = '50px' @@ -1889,46 +2636,58 @@ describe('TextEditorComponent', () => { overlayElement.style.margin = '3px' overlayElement.style.backgroundColor = 'red' const marker = editor.markScreenPosition([4, 25]) - const decoration = editor.decorateMarker(marker, {type: 'overlay', item: overlayElement, avoidOverflow: false}) + editor.decorateMarker(marker, { + type: 'overlay', + item: overlayElement, + avoidOverflow: false + }) await component.getNextUpdatePromise() await setScrollLeft(component, 30) - expect(overlayElement.getBoundingClientRect().right).toBeGreaterThan(fakeWindow.getBoundingClientRect().right) + expect(overlayElement.getBoundingClientRect().right).toBeGreaterThan( + fakeWindow.getBoundingClientRect().right + ) await setScrollLeft(component, 280) - expect(overlayElement.getBoundingClientRect().left).toBeLessThan(fakeWindow.getBoundingClientRect().left) + expect(overlayElement.getBoundingClientRect().left).toBeLessThan( + fakeWindow.getBoundingClientRect().left + ) }) }) describe('custom gutter decorations', () => { it('arranges custom gutters based on their priority', async () => { - const {component, element, editor} = buildComponent() - editor.addGutter({name: 'e', priority: 2}) - editor.addGutter({name: 'a', priority: -2}) - editor.addGutter({name: 'd', priority: 1}) - editor.addGutter({name: 'b', priority: -1}) - editor.addGutter({name: 'c', priority: 0}) + const { component, editor } = buildComponent() + editor.addGutter({ name: 'e', priority: 2 }) + editor.addGutter({ name: 'a', priority: -2 }) + editor.addGutter({ name: 'd', priority: 1 }) + editor.addGutter({ name: 'b', priority: -1 }) + editor.addGutter({ name: 'c', priority: 0 }) await component.getNextUpdatePromise() - const gutters = component.refs.gutterContainer.element.querySelectorAll('.gutter') - expect(Array.from(gutters).map((g) => g.getAttribute('gutter-name'))).toEqual([ - 'a', 'b', 'c', 'line-number', 'd', 'e' - ]) + const gutters = component.refs.gutterContainer.element.querySelectorAll( + '.gutter' + ) + expect( + Array.from(gutters).map(g => g.getAttribute('gutter-name')) + ).toEqual(['a', 'b', 'c', 'line-number', 'd', 'e']) }) it('adjusts the left edge of the scroll container based on changes to the gutter container width', async () => { - const {component, element, editor} = buildComponent() - const {scrollContainer, gutterContainer} = component.refs + const { component, editor } = buildComponent() + const { scrollContainer, gutterContainer } = component.refs function checkScrollContainerLeft () { - expect(scrollContainer.getBoundingClientRect().left).toBe(Math.round(gutterContainer.element.getBoundingClientRect().right)) + expect(scrollContainer.getBoundingClientRect().left).toBe( + Math.round(gutterContainer.element.getBoundingClientRect().right) + ) } checkScrollContainerLeft() - const gutterA = editor.addGutter({name: 'a'}) + const gutterA = editor.addGutter({ name: 'a' }) await component.getNextUpdatePromise() checkScrollContainerLeft() - const gutterB = editor.addGutter({name: 'b'}) + const gutterB = editor.addGutter({ name: 'b' }) await component.getNextUpdatePromise() checkScrollContainerLeft() @@ -1954,10 +2713,10 @@ describe('TextEditorComponent', () => { }) it('allows the element of custom gutters to be retrieved before being rendered in the editor component', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() const [lineNumberGutter] = editor.getGutters() - const gutterA = editor.addGutter({name: 'a', priority: -1}) - const gutterB = editor.addGutter({name: 'b', priority: 1}) + const gutterA = editor.addGutter({ name: 'a', priority: -1 }) + const gutterB = editor.addGutter({ name: 'b', priority: 1 }) const lineNumberGutterElement = lineNumberGutter.getElement() const gutterAElement = gutterA.getElement() @@ -1971,9 +2730,9 @@ describe('TextEditorComponent', () => { }) it('can show and hide custom gutters', async () => { - const {component, element, editor} = buildComponent() - const gutterA = editor.addGutter({name: 'a', priority: -1}) - const gutterB = editor.addGutter({name: 'b', priority: 1}) + const { component, editor } = buildComponent() + const gutterA = editor.addGutter({ name: 'a', priority: -1 }) + const gutterB = editor.addGutter({ name: 'b', priority: 1 }) const gutterAElement = gutterA.getElement() const gutterBElement = gutterB.getElement() @@ -1998,9 +2757,9 @@ describe('TextEditorComponent', () => { }) it('renders decorations in custom gutters', async () => { - const {component, element, editor} = buildComponent() - const gutterA = editor.addGutter({name: 'a', priority: -1}) - const gutterB = editor.addGutter({name: 'b', priority: 1}) + const { component, element, editor } = buildComponent() + const gutterA = editor.addGutter({ name: 'a', priority: -1 }) + const gutterB = editor.addGutter({ name: 'b', priority: 1 }) const marker1 = editor.markScreenRange([[2, 0], [4, 0]]) const marker2 = editor.markScreenRange([[6, 0], [7, 0]]) const marker3 = editor.markScreenRange([[9, 0], [12, 0]]) @@ -2009,42 +2768,68 @@ describe('TextEditorComponent', () => { // Packages may adopt this class name for decorations to be styled the same as line numbers decorationElement2.className = 'line-number' - const decoration1 = gutterA.decorateMarker(marker1, {class: 'a'}) - const decoration2 = gutterA.decorateMarker(marker2, {class: 'b', item: decorationElement1}) - const decoration3 = gutterB.decorateMarker(marker3, {item: decorationElement2}) + const decoration1 = gutterA.decorateMarker(marker1, { class: 'a' }) + const decoration2 = gutterA.decorateMarker(marker2, { + class: 'b', + item: decorationElement1 + }) + const decoration3 = gutterB.decorateMarker(marker3, { + item: decorationElement2 + }) await component.getNextUpdatePromise() - let [decorationNode1, decorationNode2] = gutterA.getElement().firstChild.children + let [ + decorationNode1, + decorationNode2 + ] = gutterA.getElement().firstChild.children const [decorationNode3] = gutterB.getElement().firstChild.children expect(decorationNode1.className).toBe('decoration a') - expect(decorationNode1.getBoundingClientRect().top).toBe(clientTopForLine(component, 2)) - expect(decorationNode1.getBoundingClientRect().bottom).toBe(clientTopForLine(component, 5)) + expect(decorationNode1.getBoundingClientRect().top).toBe( + clientTopForLine(component, 2) + ) + expect(decorationNode1.getBoundingClientRect().bottom).toBe( + clientTopForLine(component, 5) + ) expect(decorationNode1.firstChild).toBeNull() expect(decorationNode2.className).toBe('decoration b') - expect(decorationNode2.getBoundingClientRect().top).toBe(clientTopForLine(component, 6)) - expect(decorationNode2.getBoundingClientRect().bottom).toBe(clientTopForLine(component, 8)) + expect(decorationNode2.getBoundingClientRect().top).toBe( + clientTopForLine(component, 6) + ) + expect(decorationNode2.getBoundingClientRect().bottom).toBe( + clientTopForLine(component, 8) + ) expect(decorationNode2.firstChild).toBe(decorationElement1) expect(decorationElement1.offsetHeight).toBe(decorationNode2.offsetHeight) expect(decorationElement1.offsetWidth).toBe(decorationNode2.offsetWidth) expect(decorationNode3.className).toBe('decoration') - expect(decorationNode3.getBoundingClientRect().top).toBe(clientTopForLine(component, 9)) - expect(decorationNode3.getBoundingClientRect().bottom).toBe(clientTopForLine(component, 12) + component.getLineHeight()) + expect(decorationNode3.getBoundingClientRect().top).toBe( + clientTopForLine(component, 9) + ) + expect(decorationNode3.getBoundingClientRect().bottom).toBe( + clientTopForLine(component, 12) + component.getLineHeight() + ) expect(decorationNode3.firstChild).toBe(decorationElement2) expect(decorationElement2.offsetHeight).toBe(decorationNode3.offsetHeight) expect(decorationElement2.offsetWidth).toBe(decorationNode3.offsetWidth) // Inline styled height is updated when line height changes - element.style.fontSize = parseInt(getComputedStyle(element).fontSize) + 10 + 'px' + element.style.fontSize = + parseInt(getComputedStyle(element).fontSize) + 10 + 'px' TextEditor.didUpdateStyles() await component.getNextUpdatePromise() expect(decorationElement1.offsetHeight).toBe(decorationNode2.offsetHeight) expect(decorationElement2.offsetHeight).toBe(decorationNode3.offsetHeight) - decoration1.setProperties({type: 'gutter', gutterName: 'a', class: 'c', item: decorationElement1}) - decoration2.setProperties({type: 'gutter', gutterName: 'a'}) + decoration1.setProperties({ + type: 'gutter', + gutterName: 'a', + class: 'c', + item: decorationElement1 + }) + decoration2.setProperties({ type: 'gutter', gutterName: 'a' }) decoration3.destroy() await component.getNextUpdatePromise() expect(decorationNode1.className).toBe('decoration c') @@ -2056,44 +2841,62 @@ describe('TextEditorComponent', () => { }) it('renders custom line number gutters', async () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() const gutterA = editor.addGutter({ name: 'a', priority: 1, type: 'line-number', class: 'a-number', - labelFn: ({bufferRow}) => `a - ${bufferRow}` + labelFn: ({ bufferRow }) => `a - ${bufferRow}` }) const gutterB = editor.addGutter({ name: 'b', priority: 1, type: 'line-number', class: 'b-number', - labelFn: ({bufferRow}) => `b - ${bufferRow}` + labelFn: ({ bufferRow }) => `b - ${bufferRow}` }) editor.setText('0000\n0001\n0002\n0003\n0004\n') await component.getNextUpdatePromise() const gutterAElement = gutterA.getElement() - const aNumbers = gutterAElement.querySelectorAll('div.line-number[data-buffer-row]') + const aNumbers = gutterAElement.querySelectorAll( + 'div.line-number[data-buffer-row]' + ) const aLabels = Array.from(aNumbers, e => e.textContent) - expect(aLabels).toEqual(['a - 0', 'a - 1', 'a - 2', 'a - 3', 'a - 4', 'a - 5']) + expect(aLabels).toEqual([ + 'a - 0', + 'a - 1', + 'a - 2', + 'a - 3', + 'a - 4', + 'a - 5' + ]) const gutterBElement = gutterB.getElement() - const bNumbers = gutterBElement.querySelectorAll('div.line-number[data-buffer-row]') + const bNumbers = gutterBElement.querySelectorAll( + 'div.line-number[data-buffer-row]' + ) const bLabels = Array.from(bNumbers, e => e.textContent) - expect(bLabels).toEqual(['b - 0', 'b - 1', 'b - 2', 'b - 3', 'b - 4', 'b - 5']) + expect(bLabels).toEqual([ + 'b - 0', + 'b - 1', + 'b - 2', + 'b - 3', + 'b - 4', + 'b - 5' + ]) }) it("updates the editor's soft wrap width when a custom gutter's measurement is available", () => { - const {component, element, editor} = buildComponent({ + const { component, element, editor } = buildComponent({ lineNumberGutterVisible: false, width: 400, softWrapped: true, - attach: false, + attach: false }) - const gutter = editor.addGutter({name: 'a', priority: 10}) + const gutter = editor.addGutter({ name: 'a', priority: 10 }) gutter.getElement().style.width = '100px' jasmine.attachToDOM(element) @@ -2102,30 +2905,53 @@ describe('TextEditorComponent', () => { // Component client width - gutter container width - vertical scrollbar width const softWrapColumn = Math.floor( - (400 - 100 - component.getVerticalScrollbarWidth()) / component.getBaseCharacterWidth()) + (400 - 100 - component.getVerticalScrollbarWidth()) / + component.getBaseCharacterWidth() + ) expect(editor.getSoftWrapColumn()).toBe(softWrapColumn) }) }) describe('block decorations', () => { it('renders visible block decorations between the appropriate lines, refreshing and measuring them as needed', async () => { - const editor = buildEditor({autoHeight: false}) - const {item: item1, decoration: decoration1} = createBlockDecorationAtScreenRow(editor, 0, {height: 11, position: 'before'}) - const {item: item2, decoration: decoration2} = createBlockDecorationAtScreenRow(editor, 2, {height: 22, margin: 10, position: 'before'}) + const editor = buildEditor({ autoHeight: false }) + const { + item: item1, + decoration: decoration1 + } = createBlockDecorationAtScreenRow(editor, 0, { + height: 11, + position: 'before' + }) + const { + item: item2, + decoration: decoration2 + } = createBlockDecorationAtScreenRow(editor, 2, { + height: 22, + margin: 10, + position: 'before' + }) // render an editor that already contains some block decorations - const {component, element} = buildComponent({editor, rowsPerTile: 3}) - element.style.height = 4 * component.getLineHeight() + horizontalScrollbarHeight + 'px' + const { component, element } = buildComponent({ editor, rowsPerTile: 3 }) + element.style.height = + 4 * component.getLineHeight() + horizontalScrollbarHeight + 'px' await component.getNextUpdatePromise() expect(component.getRenderedStartRow()).toBe(0) expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item1) + getElementHeight(item2) + getElementHeight(item1) + + getElementHeight(item2) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item1) + getElementHeight(item2)}, - {tileStartRow: 3, height: 3 * component.getLineHeight()} + { + tileStartRow: 0, + height: + 3 * component.getLineHeight() + + getElementHeight(item1) + + getElementHeight(item2) + }, + { tileStartRow: 3, height: 3 * component.getLineHeight() } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2135,21 +2961,51 @@ describe('TextEditorComponent', () => { expect(item2.nextSibling).toBe(lineNodeForScreenRow(component, 2)) // add block decorations - const {item: item3, decoration: decoration3} = createBlockDecorationAtScreenRow(editor, 4, {height: 33, position: 'before'}) - const {item: item4, decoration: decoration4} = createBlockDecorationAtScreenRow(editor, 7, {height: 44, position: 'before'}) - const {item: item5, decoration: decoration5} = createBlockDecorationAtScreenRow(editor, 7, {height: 50, marginBottom: 5, position: 'after'}) - const {item: item6, decoration: decoration6} = createBlockDecorationAtScreenRow(editor, 12, {height: 60, marginTop: 6, position: 'after'}) + const { + item: item3, + decoration: decoration3 + } = createBlockDecorationAtScreenRow(editor, 4, { + height: 33, + position: 'before' + }) + const { item: item4 } = createBlockDecorationAtScreenRow(editor, 7, { + height: 44, + position: 'before' + }) + const { item: item5 } = createBlockDecorationAtScreenRow(editor, 7, { + height: 50, + marginBottom: 5, + position: 'after' + }) + const { item: item6 } = createBlockDecorationAtScreenRow(editor, 12, { + height: 60, + marginTop: 6, + position: 'after' + }) await component.getNextUpdatePromise() expect(component.getRenderedStartRow()).toBe(0) expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item1) + getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item1) + + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item1) + getElementHeight(item2)}, - {tileStartRow: 3, height: 3 * component.getLineHeight() + getElementHeight(item3)} + { + tileStartRow: 0, + height: + 3 * component.getLineHeight() + + getElementHeight(item1) + + getElementHeight(item2) + }, + { + tileStartRow: 3, + height: 3 * component.getLineHeight() + getElementHeight(item3) + } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2170,12 +3026,21 @@ describe('TextEditorComponent', () => { expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2)}, - {tileStartRow: 3, height: 3 * component.getLineHeight() + getElementHeight(item3)} + { + tileStartRow: 0, + height: 3 * component.getLineHeight() + getElementHeight(item2) + }, + { + tileStartRow: 3, + height: 3 * component.getLineHeight() + getElementHeight(item3) + } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2196,12 +3061,21 @@ describe('TextEditorComponent', () => { expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2) + getElementHeight(item3)}, - {tileStartRow: 3, height: 3 * component.getLineHeight()} + { + tileStartRow: 0, + height: + 3 * component.getLineHeight() + + getElementHeight(item2) + + getElementHeight(item3) + }, + { tileStartRow: 3, height: 3 * component.getLineHeight() } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2221,12 +3095,21 @@ describe('TextEditorComponent', () => { expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item3)}, - {tileStartRow: 3, height: 3 * component.getLineHeight() + getElementHeight(item2)} + { + tileStartRow: 0, + height: 3 * component.getLineHeight() + getElementHeight(item3) + }, + { + tileStartRow: 3, + height: 3 * component.getLineHeight() + getElementHeight(item2) + } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2240,17 +3123,26 @@ describe('TextEditorComponent', () => { expect(element.contains(item6)).toBe(false) // scroll past the first tile - await setScrollTop(component, 3 * component.getLineHeight() + getElementHeight(item3)) + await setScrollTop( + component, + 3 * component.getLineHeight() + getElementHeight(item3) + ) expect(component.getRenderedStartRow()).toBe(3) expect(component.getRenderedEndRow()).toBe(12) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 3, height: 3 * component.getLineHeight() + getElementHeight(item2)}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { + tileStartRow: 3, + height: 3 * component.getLineHeight() + getElementHeight(item2) + }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2270,12 +3162,21 @@ describe('TextEditorComponent', () => { expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2) + getElementHeight(item3)}, - {tileStartRow: 3, height: 3 * component.getLineHeight()} + { + tileStartRow: 0, + height: + 3 * component.getLineHeight() + + getElementHeight(item2) + + getElementHeight(item3) + }, + { tileStartRow: 3, height: 3 * component.getLineHeight() } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2300,12 +3201,21 @@ describe('TextEditorComponent', () => { expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2) + getElementHeight(item3)}, - {tileStartRow: 3, height: 3 * component.getLineHeight()} + { + tileStartRow: 0, + height: + 3 * component.getLineHeight() + + getElementHeight(item2) + + getElementHeight(item3) + }, + { tileStartRow: 3, height: 3 * component.getLineHeight() } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2323,27 +3233,39 @@ describe('TextEditorComponent', () => { item3.style.margin = '' item3.style.width = '' item3.style.wordWrap = 'break-word' - const contentWidthInCharacters = Math.floor(component.getScrollContainerClientWidth() / component.getBaseCharacterWidth()) + const contentWidthInCharacters = Math.floor( + component.getScrollContainerClientWidth() / + component.getBaseCharacterWidth() + ) item3.textContent = 'x'.repeat(contentWidthInCharacters * 2) await component.getNextUpdatePromise() // make the editor wider, so that the decoration doesn't wrap anymore. - component.element.style.width = ( + component.element.style.width = component.getGutterContainerWidth() + component.getScrollContainerClientWidth() * 2 + - verticalScrollbarWidth - ) + 'px' + verticalScrollbarWidth + + 'px' await component.getNextUpdatePromise() expect(component.getRenderedStartRow()).toBe(0) expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2) + getElementHeight(item3)}, - {tileStartRow: 3, height: 3 * component.getLineHeight()} + { + tileStartRow: 0, + height: + 3 * component.getLineHeight() + + getElementHeight(item2) + + getElementHeight(item3) + }, + { tileStartRow: 3, height: 3 * component.getLineHeight() } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(9) @@ -2364,13 +3286,28 @@ describe('TextEditorComponent', () => { expect(component.getRenderedEndRow()).toBe(13) expect(component.getScrollHeight()).toBe( editor.getScreenLineCount() * component.getLineHeight() + - getElementHeight(item2) + getElementHeight(item3) + - getElementHeight(item4) + getElementHeight(item5) + getElementHeight(item6) + getElementHeight(item2) + + getElementHeight(item3) + + getElementHeight(item4) + + getElementHeight(item5) + + getElementHeight(item6) ) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + getElementHeight(item2) + getElementHeight(item3)}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight() + getElementHeight(item4) + getElementHeight(item5)}, + { + tileStartRow: 0, + height: + 3 * component.getLineHeight() + + getElementHeight(item2) + + getElementHeight(item3) + }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { + tileStartRow: 6, + height: + 3 * component.getLineHeight() + + getElementHeight(item4) + + getElementHeight(item5) + } ]) assertLinesAreAlignedWithLineNumbers(component) expect(queryOnScreenLineElements(element).length).toBe(13) @@ -2387,47 +3324,72 @@ describe('TextEditorComponent', () => { }) it('correctly positions line numbers when block decorations are located at tile boundaries', async () => { - const {editor, component, element} = buildComponent({rowsPerTile: 3}) - createBlockDecorationAtScreenRow(editor, 0, {height: 5, position: 'before'}) - createBlockDecorationAtScreenRow(editor, 2, {height: 7, position: 'after'}) - createBlockDecorationAtScreenRow(editor, 3, {height: 9, position: 'before'}) - createBlockDecorationAtScreenRow(editor, 3, {height: 11, position: 'after'}) - createBlockDecorationAtScreenRow(editor, 5, {height: 13, position: 'after'}) + const { editor, component } = buildComponent({ rowsPerTile: 3 }) + createBlockDecorationAtScreenRow(editor, 0, { + height: 5, + position: 'before' + }) + createBlockDecorationAtScreenRow(editor, 2, { + height: 7, + position: 'after' + }) + createBlockDecorationAtScreenRow(editor, 3, { + height: 9, + position: 'before' + }) + createBlockDecorationAtScreenRow(editor, 3, { + height: 11, + position: 'after' + }) + createBlockDecorationAtScreenRow(editor, 5, { + height: 13, + position: 'after' + }) await component.getNextUpdatePromise() assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + 5 + 7}, - {tileStartRow: 3, height: 3 * component.getLineHeight() + 9 + 11 + 13}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() + 5 + 7 }, + { + tileStartRow: 3, + height: 3 * component.getLineHeight() + 9 + 11 + 13 + }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) }) it('removes block decorations whose markers have been destroyed', async () => { - const {editor, component, element} = buildComponent({rowsPerTile: 3}) - const {marker} = createBlockDecorationAtScreenRow(editor, 2, {height: 5, position: 'before'}) + const { editor, component } = buildComponent({ rowsPerTile: 3 }) + const { marker } = createBlockDecorationAtScreenRow(editor, 2, { + height: 5, + position: 'before' + }) await component.getNextUpdatePromise() assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + 5}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() + 5 }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) marker.destroy() await component.getNextUpdatePromise() assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight()}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) }) it('removes block decorations whose markers are invalidated, and adds them back when they become valid again', async () => { - const editor = buildEditor({rowsPerTile: 3, autoHeight: false}) - const {item, decoration, marker} = createBlockDecorationAtScreenRow(editor, 3, {height: 44, position: 'before', invalidate: 'touch'}) - const {component, element} = buildComponent({editor, rowsPerTile: 3}) + const editor = buildEditor({ rowsPerTile: 3, autoHeight: false }) + const { item, decoration, marker } = createBlockDecorationAtScreenRow( + editor, + 3, + { height: 44, position: 'before', invalidate: 'touch' } + ) + const { component } = buildComponent({ editor, rowsPerTile: 3 }) // Invalidating the marker removes the block decoration. editor.getBuffer().deleteRows(2, 3) @@ -2435,9 +3397,9 @@ describe('TextEditorComponent', () => { expect(item.parentElement).toBeNull() assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight()}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) // Moving invalid markers is ignored. @@ -2446,9 +3408,9 @@ describe('TextEditorComponent', () => { expect(item.parentElement).toBeNull() assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight()}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) // Making the marker valid again adds back the block decoration. @@ -2458,9 +3420,9 @@ describe('TextEditorComponent', () => { expect(item.nextSibling).toBe(lineNodeForScreenRow(component, 3)) assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight()}, - {tileStartRow: 3, height: 3 * component.getLineHeight() + 44}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() }, + { tileStartRow: 3, height: 3 * component.getLineHeight() + 44 }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) // Destroying the decoration and invalidating the marker at the same time @@ -2471,30 +3433,34 @@ describe('TextEditorComponent', () => { expect(item.parentElement).toBeNull() assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight()}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) }) it('does not render block decorations when decorating invalid markers', async () => { - const editor = buildEditor({rowsPerTile: 3, autoHeight: false}) - const {component, element} = buildComponent({editor, rowsPerTile: 3}) + const editor = buildEditor({ rowsPerTile: 3, autoHeight: false }) + const { component } = buildComponent({ editor, rowsPerTile: 3 }) - const marker = editor.markScreenPosition([3, 0], {invalidate: 'touch'}) + const marker = editor.markScreenPosition([3, 0], { invalidate: 'touch' }) const item = document.createElement('div') item.style.height = 30 + 'px' item.style.width = 30 + 'px' editor.getBuffer().deleteRows(1, 4) - const decoration = editor.decorateMarker(marker, {type: 'block', item, position: 'before'}) + editor.decorateMarker(marker, { + type: 'block', + item, + position: 'before' + }) await component.getNextUpdatePromise() expect(item.parentElement).toBeNull() assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight()}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) // Making the marker valid again causes the corresponding block decoration @@ -2505,16 +3471,19 @@ describe('TextEditorComponent', () => { expect(item.nextSibling).toBe(lineNodeForScreenRow(component, 2)) assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight() + 30}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() + 30 }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) }) it('does not try to remeasure block decorations whose markers are invalid (regression)', async () => { - const editor = buildEditor({rowsPerTile: 3, autoHeight: false}) - const {component, element} = buildComponent({editor, rowsPerTile: 3}) - const {decoration, marker} = createBlockDecorationAtScreenRow(editor, 2, {height: '12px', invalidate: 'touch'}) + const editor = buildEditor({ rowsPerTile: 3, autoHeight: false }) + const { component } = buildComponent({ editor, rowsPerTile: 3 }) + createBlockDecorationAtScreenRow(editor, 2, { + height: '12px', + invalidate: 'touch' + }) editor.getBuffer().deleteRows(0, 3) await component.getNextUpdatePromise() @@ -2522,19 +3491,21 @@ describe('TextEditorComponent', () => { await setEditorWidthInCharacters(component, 20) assertLinesAreAlignedWithLineNumbers(component) assertTilesAreSizedAndPositionedCorrectly(component, [ - {tileStartRow: 0, height: 3 * component.getLineHeight()}, - {tileStartRow: 3, height: 3 * component.getLineHeight()}, - {tileStartRow: 6, height: 3 * component.getLineHeight()} + { tileStartRow: 0, height: 3 * component.getLineHeight() }, + { tileStartRow: 3, height: 3 * component.getLineHeight() }, + { tileStartRow: 6, height: 3 * component.getLineHeight() } ]) }) it('does not throw exceptions when destroying a block decoration inside a marker change event (regression)', async () => { - const {editor, component} = buildComponent({rowsPerTile: 3}) + const { editor, component } = buildComponent({ rowsPerTile: 3 }) const marker = editor.markScreenPosition([2, 0]) - marker.onDidChange(() => { marker.destroy() }) + marker.onDidChange(() => { + marker.destroy() + }) const item = document.createElement('div') - editor.decorateMarker(marker, {type: 'block', item}) + editor.decorateMarker(marker, { type: 'block', item }) await component.getNextUpdatePromise() expect(item.nextSibling).toBe(lineNodeForScreenRow(component, 2)) @@ -2547,18 +3518,25 @@ describe('TextEditorComponent', () => { }) it('does not attempt to render block decorations located outside the visible range', async () => { - const {editor, component} = buildComponent({autoHeight: false, rowsPerTile: 2}) + const { editor, component } = buildComponent({ + autoHeight: false, + rowsPerTile: 2 + }) await setEditorHeightInLines(component, 2) expect(component.getRenderedStartRow()).toBe(0) expect(component.getRenderedEndRow()).toBe(4) - const marker1 = editor.markScreenRange([[3, 0], [5, 0]], {reversed: false}) + const marker1 = editor.markScreenRange([[3, 0], [5, 0]], { + reversed: false + }) const item1 = document.createElement('div') - editor.decorateMarker(marker1, {type: 'block', item: item1}) + editor.decorateMarker(marker1, { type: 'block', item: item1 }) - const marker2 = editor.markScreenRange([[3, 0], [5, 0]], {reversed: true}) + const marker2 = editor.markScreenRange([[3, 0], [5, 0]], { + reversed: true + }) const item2 = document.createElement('div') - editor.decorateMarker(marker2, {type: 'block', item: item2}) + editor.decorateMarker(marker2, { type: 'block', item: item2 }) await component.getNextUpdatePromise() expect(item1.parentElement).toBeNull() @@ -2573,22 +3551,35 @@ describe('TextEditorComponent', () => { it('measures block decorations correctly when they are added before the component width has been updated', async () => { { - const {editor, component, element} = buildComponent({autoHeight: false, width: 500, attach: false}) + const { editor, component, element } = buildComponent({ + autoHeight: false, + width: 500, + attach: false + }) const marker = editor.markScreenPosition([0, 0]) const item = document.createElement('div') item.textContent = 'block decoration' - const decoration = editor.decorateMarker(marker, {type: 'block', item}) + editor.decorateMarker(marker, { + type: 'block', + item + }) jasmine.attachToDOM(element) assertLinesAreAlignedWithLineNumbers(component) } { - const {editor, component, element} = buildComponent({autoHeight: false, width: 800}) + const { editor, component, element } = buildComponent({ + autoHeight: false, + width: 800 + }) const marker = editor.markScreenPosition([0, 0]) const item = document.createElement('div') item.textContent = 'block decoration that could wrap many times' - const decoration = editor.decorateMarker(marker, {type: 'block', item}) + editor.decorateMarker(marker, { + type: 'block', + item + }) element.style.width = '50px' await component.getNextUpdatePromise() @@ -2597,16 +3588,23 @@ describe('TextEditorComponent', () => { }) it('bases the width of the block decoration measurement area on the editor scroll width', async () => { - const {component, element} = buildComponent({autoHeight: false, width: 150}) - expect(component.refs.blockDecorationMeasurementArea.offsetWidth).toBe(component.getScrollWidth()) + const { component, element } = buildComponent({ + autoHeight: false, + width: 150 + }) + expect(component.refs.blockDecorationMeasurementArea.offsetWidth).toBe( + component.getScrollWidth() + ) element.style.width = '800px' await component.getNextUpdatePromise() - expect(component.refs.blockDecorationMeasurementArea.offsetWidth).toBe(component.getScrollWidth()) + expect(component.refs.blockDecorationMeasurementArea.offsetWidth).toBe( + component.getScrollWidth() + ) }) it('does not change the cursor position when clicking on a block decoration', async () => { - const {editor, component} = buildComponent() + const { editor, component } = buildComponent() const decorationElement = document.createElement('div') decorationElement.textContent = 'Parent' @@ -2614,7 +3612,7 @@ describe('TextEditorComponent', () => { childElement.textContent = 'Child' decorationElement.appendChild(childElement) const marker = editor.markScreenPosition([4, 0]) - editor.decorateMarker(marker, {type: 'block', item: decorationElement}) + editor.decorateMarker(marker, { type: 'block', item: decorationElement }) await component.getNextUpdatePromise() const decorationElementClientRect = decorationElement.getBoundingClientRect() @@ -2639,38 +3637,64 @@ describe('TextEditorComponent', () => { }) it('uses the order property to control the order of block decorations at the same screen row', async () => { - const editor = buildEditor({autoHeight: false}) - const {component, element} = buildComponent({editor}) - element.style.height = 10 * component.getLineHeight() + horizontalScrollbarHeight + 'px' + const editor = buildEditor({ autoHeight: false }) + const { component, element } = buildComponent({ editor }) + element.style.height = + 10 * component.getLineHeight() + horizontalScrollbarHeight + 'px' await component.getNextUpdatePromise() // Order parameters that differ from creation order; that collide; and that are not provided. - const [beforeItems, beforeDecorations] = [30, 20, undefined, 20, 10, undefined].map(order => { - return createBlockDecorationAtScreenRow(editor, 2, {height: 10, position: 'before', order}) - }).reduce((lists, result) => { - lists[0].push(result.item) - lists[1].push(result.decoration) - return lists - }, [[], []]) + const [beforeItems, beforeDecorations] = [ + 30, + 20, + undefined, + 20, + 10, + undefined + ] + .map(order => { + return createBlockDecorationAtScreenRow(editor, 2, { + height: 10, + position: 'before', + order + }) + }) + .reduce((lists, result) => { + lists[0].push(result.item) + lists[1].push(result.decoration) + return lists + }, [[], []]) - const [afterItems, afterDecorations] = [undefined, 1, 6, undefined, 6, 2].map(order => { - return createBlockDecorationAtScreenRow(editor, 2, {height: 10, position: 'after', order}) - }).reduce((lists, result) => { - lists[0].push(result.item) - lists[1].push(result.decoration) - return lists - }, [[], []]) + const [afterItems] = [undefined, 1, 6, undefined, 6, 2] + .map(order => { + return createBlockDecorationAtScreenRow(editor, 2, { + height: 10, + position: 'after', + order + }) + }) + .reduce((lists, result) => { + lists[0].push(result.item) + lists[1].push(result.decoration) + return lists + }, [[], []]) await component.getNextUpdatePromise() - expect(beforeItems[4].previousSibling).toBe(lineNodeForScreenRow(component, 1)) + expect(beforeItems[4].previousSibling).toBe( + lineNodeForScreenRow(component, 1) + ) expect(beforeItems[4].nextSibling).toBe(beforeItems[1]) expect(beforeItems[1].nextSibling).toBe(beforeItems[3]) expect(beforeItems[3].nextSibling).toBe(beforeItems[0]) expect(beforeItems[0].nextSibling).toBe(beforeItems[2]) expect(beforeItems[2].nextSibling).toBe(beforeItems[5]) - expect(beforeItems[5].nextSibling).toBe(lineNodeForScreenRow(component, 2)) - expect(afterItems[1].previousSibling).toBe(lineNodeForScreenRow(component, 2)) + expect(beforeItems[5].nextSibling).toBe( + lineNodeForScreenRow(component, 2) + ) + expect(afterItems[1].previousSibling).toBe( + lineNodeForScreenRow(component, 2) + ) expect(afterItems[1].nextSibling).toBe(afterItems[5]) expect(afterItems[5].nextSibling).toBe(afterItems[2]) expect(afterItems[2].nextSibling).toBe(afterItems[4]) @@ -2678,7 +3702,11 @@ describe('TextEditorComponent', () => { expect(afterItems[0].nextSibling).toBe(afterItems[3]) // Create a decoration somewhere else and move it to the same screen row as the existing decorations - const {item: later, decoration} = createBlockDecorationAtScreenRow(editor, 4, {height: 20, position: 'after', order: 3}) + const { item: later, decoration } = createBlockDecorationAtScreenRow( + editor, + 4, + { height: 20, position: 'after', order: 3 } + ) await component.getNextUpdatePromise() expect(later.previousSibling).toBe(lineNodeForScreenRow(component, 4)) expect(later.nextSibling).toBe(lineNodeForScreenRow(component, 5)) @@ -2691,38 +3719,63 @@ describe('TextEditorComponent', () => { // Move a decoration away from its screen row and ensure the rest maintain their order beforeDecorations[3].getMarker().setHeadScreenPosition([5, 0]) await component.getNextUpdatePromise() - expect(beforeItems[3].previousSibling).toBe(lineNodeForScreenRow(component, 4)) - expect(beforeItems[3].nextSibling).toBe(lineNodeForScreenRow(component, 5)) + expect(beforeItems[3].previousSibling).toBe( + lineNodeForScreenRow(component, 4) + ) + expect(beforeItems[3].nextSibling).toBe( + lineNodeForScreenRow(component, 5) + ) - expect(beforeItems[4].previousSibling).toBe(lineNodeForScreenRow(component, 1)) + expect(beforeItems[4].previousSibling).toBe( + lineNodeForScreenRow(component, 1) + ) expect(beforeItems[4].nextSibling).toBe(beforeItems[1]) expect(beforeItems[1].nextSibling).toBe(beforeItems[0]) expect(beforeItems[0].nextSibling).toBe(beforeItems[2]) expect(beforeItems[2].nextSibling).toBe(beforeItems[5]) - expect(beforeItems[5].nextSibling).toBe(lineNodeForScreenRow(component, 2)) - }); + expect(beforeItems[5].nextSibling).toBe( + lineNodeForScreenRow(component, 2) + ) + }) - function createBlockDecorationAtScreenRow(editor, screenRow, {height, margin, marginTop, marginBottom, position, order, invalidate}) { - const marker = editor.markScreenPosition([screenRow, 0], {invalidate: invalidate || 'never'}) + function createBlockDecorationAtScreenRow ( + editor, + screenRow, + { height, margin, marginTop, marginBottom, position, order, invalidate } + ) { + const marker = editor.markScreenPosition([screenRow, 0], { + invalidate: invalidate || 'never' + }) const item = document.createElement('div') item.style.height = height + 'px' if (margin != null) item.style.margin = margin + 'px' if (marginTop != null) item.style.marginTop = marginTop + 'px' if (marginBottom != null) item.style.marginBottom = marginBottom + 'px' item.style.width = 30 + 'px' - const decoration = editor.decorateMarker(marker, {type: 'block', item, position, order}) - return {item, decoration, marker} + const decoration = editor.decorateMarker(marker, { + type: 'block', + item, + position, + order + }) + return { item, decoration, marker } } function assertTilesAreSizedAndPositionedCorrectly (component, tiles) { let top = 0 for (let tile of tiles) { - const linesTileElement = lineNodeForScreenRow(component, tile.tileStartRow).parentElement + const linesTileElement = lineNodeForScreenRow( + component, + tile.tileStartRow + ).parentElement const linesTileBoundingRect = linesTileElement.getBoundingClientRect() expect(linesTileBoundingRect.height).toBe(tile.height) expect(linesTileBoundingRect.top).toBe(top) - const lineNumbersTileElement = lineNumberNodeForScreenRow(component, tile.tileStartRow).parentElement + const lineNumbersTileElement = lineNumberNodeForScreenRow( + component, + tile.tileStartRow + ).parentElement const lineNumbersTileBoundingRect = lineNumbersTileElement.getBoundingClientRect() expect(lineNumbersTileBoundingRect.height).toBe(tile.height) expect(lineNumbersTileBoundingRect.top).toBe(top) @@ -2737,27 +3790,37 @@ describe('TextEditorComponent', () => { for (let row = startRow; row < endRow; row++) { const lineNode = lineNodeForScreenRow(component, row) const lineNumberNode = lineNumberNodeForScreenRow(component, row) - expect(lineNumberNode.getBoundingClientRect().top).toBe(lineNode.getBoundingClientRect().top) + expect(lineNumberNode.getBoundingClientRect().top).toBe( + lineNode.getBoundingClientRect().top + ) } } }) describe('cursor decorations', () => { it('allows default cursors to be customized', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() editor.addCursorAtScreenPosition([1, 0]) - const [cursorMarker1, cursorMarker2] = editor.getCursors().map(c => c.getMarker()) + const [cursorMarker1, cursorMarker2] = editor + .getCursors() + .map(c => c.getMarker()) - editor.decorateMarker(cursorMarker1, {type: 'cursor', class: 'a'}) - editor.decorateMarker(cursorMarker2, {type: 'cursor', class: 'b', style: {visibility: 'hidden'}}) - editor.decorateMarker(cursorMarker2, {type: 'cursor', style: {backgroundColor: 'red'}}) + editor.decorateMarker(cursorMarker1, { type: 'cursor', class: 'a' }) + editor.decorateMarker(cursorMarker2, { + type: 'cursor', + class: 'b', + style: { visibility: 'hidden' } + }) + editor.decorateMarker(cursorMarker2, { + type: 'cursor', + style: { backgroundColor: 'red' } + }) await component.getNextUpdatePromise() const cursorNodes = element.querySelectorAll('.cursor') expect(cursorNodes.length).toBe(2) - expect(cursorNodes[0].className).toBe('cursor a') expect(cursorNodes[1].className).toBe('cursor b') expect(cursorNodes[1].style.visibility).toBe('hidden') @@ -2765,9 +3828,9 @@ describe('TextEditorComponent', () => { }) it('allows markers that are not actually associated with cursors to be decorated as if they were cursors', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() const marker = editor.markScreenPosition([1, 0]) - editor.decorateMarker(marker, {type: 'cursor', class: 'a'}) + editor.decorateMarker(marker, { type: 'cursor', class: 'a' }) await component.getNextUpdatePromise() const cursorNodes = element.querySelectorAll('.cursor') @@ -2779,30 +3842,60 @@ describe('TextEditorComponent', () => { describe('text decorations', () => { it('injects spans with custom class names and inline styles based on text decorations', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2}) + const { component, element, editor } = buildComponent({ rowsPerTile: 2 }) const markerLayer = editor.addMarkerLayer() const marker1 = markerLayer.markBufferRange([[0, 2], [2, 7]]) const marker2 = markerLayer.markBufferRange([[0, 2], [3, 8]]) const marker3 = markerLayer.markBufferRange([[1, 13], [2, 7]]) - editor.decorateMarker(marker1, {type: 'text', class: 'a', style: {color: 'red'}}) - editor.decorateMarker(marker2, {type: 'text', class: 'b', style: {color: 'blue'}}) - editor.decorateMarker(marker3, {type: 'text', class: 'c', style: {color: 'green'}}) + editor.decorateMarker(marker1, { + type: 'text', + class: 'a', + style: { color: 'red' } + }) + editor.decorateMarker(marker2, { + type: 'text', + class: 'b', + style: { color: 'blue' } + }) + editor.decorateMarker(marker3, { + type: 'text', + class: 'c', + style: { color: 'green' } + }) await component.getNextUpdatePromise() - expect(textContentOnRowMatchingSelector(component, 0, '.a')).toBe(editor.lineTextForScreenRow(0).slice(2)) - expect(textContentOnRowMatchingSelector(component, 1, '.a')).toBe(editor.lineTextForScreenRow(1)) - expect(textContentOnRowMatchingSelector(component, 2, '.a')).toBe(editor.lineTextForScreenRow(2).slice(0, 7)) + expect(textContentOnRowMatchingSelector(component, 0, '.a')).toBe( + editor.lineTextForScreenRow(0).slice(2) + ) + expect(textContentOnRowMatchingSelector(component, 1, '.a')).toBe( + editor.lineTextForScreenRow(1) + ) + expect(textContentOnRowMatchingSelector(component, 2, '.a')).toBe( + editor.lineTextForScreenRow(2).slice(0, 7) + ) expect(textContentOnRowMatchingSelector(component, 3, '.a')).toBe('') - expect(textContentOnRowMatchingSelector(component, 0, '.b')).toBe(editor.lineTextForScreenRow(0).slice(2)) - expect(textContentOnRowMatchingSelector(component, 1, '.b')).toBe(editor.lineTextForScreenRow(1)) - expect(textContentOnRowMatchingSelector(component, 2, '.b')).toBe(editor.lineTextForScreenRow(2)) - expect(textContentOnRowMatchingSelector(component, 3, '.b')).toBe(editor.lineTextForScreenRow(3).slice(0, 8)) + expect(textContentOnRowMatchingSelector(component, 0, '.b')).toBe( + editor.lineTextForScreenRow(0).slice(2) + ) + expect(textContentOnRowMatchingSelector(component, 1, '.b')).toBe( + editor.lineTextForScreenRow(1) + ) + expect(textContentOnRowMatchingSelector(component, 2, '.b')).toBe( + editor.lineTextForScreenRow(2) + ) + expect(textContentOnRowMatchingSelector(component, 3, '.b')).toBe( + editor.lineTextForScreenRow(3).slice(0, 8) + ) expect(textContentOnRowMatchingSelector(component, 0, '.c')).toBe('') - expect(textContentOnRowMatchingSelector(component, 1, '.c')).toBe(editor.lineTextForScreenRow(1).slice(13)) - expect(textContentOnRowMatchingSelector(component, 2, '.c')).toBe(editor.lineTextForScreenRow(2).slice(0, 7)) + expect(textContentOnRowMatchingSelector(component, 1, '.c')).toBe( + editor.lineTextForScreenRow(1).slice(13) + ) + expect(textContentOnRowMatchingSelector(component, 2, '.c')).toBe( + editor.lineTextForScreenRow(2).slice(0, 7) + ) expect(textContentOnRowMatchingSelector(component, 3, '.c')).toBe('') for (const span of element.querySelectorAll('.a:not(.c)')) { @@ -2817,11 +3910,16 @@ describe('TextEditorComponent', () => { marker2.setHeadScreenPosition([3, 10]) await component.getNextUpdatePromise() - expect(textContentOnRowMatchingSelector(component, 3, '.b')).toBe(editor.lineTextForScreenRow(3).slice(0, 10)) + expect(textContentOnRowMatchingSelector(component, 3, '.b')).toBe( + editor.lineTextForScreenRow(3).slice(0, 10) + ) }) it('correctly handles text decorations starting before the first rendered row and/or ending after the last rendered row', async () => { - const {component, element, editor} = buildComponent({autoHeight: false, rowsPerTile: 1}) + const { component, element, editor } = buildComponent({ + autoHeight: false, + rowsPerTile: 1 + }) element.style.height = 4 * component.getLineHeight() + 'px' await component.getNextUpdatePromise() await setScrollTop(component, 4 * component.getLineHeight()) @@ -2831,11 +3929,13 @@ describe('TextEditorComponent', () => { const markerLayer = editor.addMarkerLayer() const marker1 = markerLayer.markBufferRange([[0, 0], [4, 5]]) const marker2 = markerLayer.markBufferRange([[7, 2], [10, 8]]) - editor.decorateMarker(marker1, {type: 'text', class: 'a'}) - editor.decorateMarker(marker2, {type: 'text', class: 'b'}) + editor.decorateMarker(marker1, { type: 'text', class: 'a' }) + editor.decorateMarker(marker2, { type: 'text', class: 'b' }) await component.getNextUpdatePromise() - expect(textContentOnRowMatchingSelector(component, 4, '.a')).toBe(editor.lineTextForScreenRow(4).slice(0, 5)) + expect(textContentOnRowMatchingSelector(component, 4, '.a')).toBe( + editor.lineTextForScreenRow(4).slice(0, 5) + ) expect(textContentOnRowMatchingSelector(component, 5, '.a')).toBe('') expect(textContentOnRowMatchingSelector(component, 6, '.a')).toBe('') expect(textContentOnRowMatchingSelector(component, 7, '.a')).toBe('') @@ -2844,17 +3944,21 @@ describe('TextEditorComponent', () => { expect(textContentOnRowMatchingSelector(component, 4, '.b')).toBe('') expect(textContentOnRowMatchingSelector(component, 5, '.b')).toBe('') expect(textContentOnRowMatchingSelector(component, 6, '.b')).toBe('') - expect(textContentOnRowMatchingSelector(component, 7, '.b')).toBe(editor.lineTextForScreenRow(7).slice(2)) - expect(textContentOnRowMatchingSelector(component, 8, '.b')).toBe(editor.lineTextForScreenRow(8)) + expect(textContentOnRowMatchingSelector(component, 7, '.b')).toBe( + editor.lineTextForScreenRow(7).slice(2) + ) + expect(textContentOnRowMatchingSelector(component, 8, '.b')).toBe( + editor.lineTextForScreenRow(8) + ) }) it('does not create empty spans when a text decoration contains a row but another text decoration starts or ends at the beginning of it', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() const markerLayer = editor.addMarkerLayer() const marker1 = markerLayer.markBufferRange([[0, 2], [4, 0]]) const marker2 = markerLayer.markBufferRange([[2, 0], [5, 8]]) - editor.decorateMarker(marker1, {type: 'text', class: 'a'}) - editor.decorateMarker(marker2, {type: 'text', class: 'b'}) + editor.decorateMarker(marker1, { type: 'text', class: 'a' }) + editor.decorateMarker(marker2, { type: 'text', class: 'b' }) await component.getNextUpdatePromise() for (const decorationSpan of element.querySelectorAll('.a, .b')) { expect(decorationSpan.textContent).not.toBe('') @@ -2862,9 +3966,9 @@ describe('TextEditorComponent', () => { }) it('does not create empty text nodes when a text decoration ends right after a text tag', async () => { - const {component, element, editor} = buildComponent() + const { component, editor } = buildComponent() const marker = editor.markBufferRange([[0, 8], [0, 29]]) - editor.decorateMarker(marker, {type: 'text', class: 'a'}) + editor.decorateMarker(marker, { type: 'text', class: 'a' }) await component.getNextUpdatePromise() for (const textNode of textNodesForScreenRow(component, 0)) { expect(textNode.textContent).not.toBe('') @@ -2872,8 +3976,10 @@ describe('TextEditorComponent', () => { }) function textContentOnRowMatchingSelector (component, row, selector) { - return Array.from(lineNodeForScreenRow(component, row).querySelectorAll(selector)) - .map((span) => span.textContent) + return Array.from( + lineNodeForScreenRow(component, row).querySelectorAll(selector) + ) + .map(span => span.textContent) .join('') } }) @@ -2883,10 +3989,12 @@ describe('TextEditorComponent', () => { describe('when there is only one cursor', () => { it('positions the cursor on single-click or when middle-clicking', async () => { for (const button of [0, 1]) { - const {component, element, editor} = buildComponent() - const {lineHeight} = component.measurements + const { component, editor } = buildComponent() + const { lineHeight } = component.measurements - editor.setCursorScreenPosition([Infinity, Infinity], {autoscroll: false}) + editor.setCursorScreenPosition([Infinity, Infinity], { + autoscroll: false + }) component.didMouseDownOnContent({ detail: 1, button, @@ -2896,27 +4004,48 @@ describe('TextEditorComponent', () => { expect(editor.getCursorScreenPosition()).toEqual([0, 0]) const maxRow = editor.getLastScreenRow() - editor.setCursorScreenPosition([Infinity, Infinity], {autoscroll: false}) + editor.setCursorScreenPosition([Infinity, Infinity], { + autoscroll: false + }) component.didMouseDownOnContent({ detail: 1, button, - clientX: clientLeftForCharacter(component, maxRow, editor.lineLengthForScreenRow(maxRow)) + 1, + clientX: + clientLeftForCharacter( + component, + maxRow, + editor.lineLengthForScreenRow(maxRow) + ) + 1, clientY: clientTopForLine(component, maxRow) + 1 }) - expect(editor.getCursorScreenPosition()).toEqual([maxRow, editor.lineLengthForScreenRow(maxRow)]) + expect(editor.getCursorScreenPosition()).toEqual([ + maxRow, + editor.lineLengthForScreenRow(maxRow) + ]) component.didMouseDownOnContent({ detail: 1, button, - clientX: clientLeftForCharacter(component, 0, editor.lineLengthForScreenRow(0)) + 1, + clientX: + clientLeftForCharacter( + component, + 0, + editor.lineLengthForScreenRow(0) + ) + 1, clientY: clientTopForLine(component, 0) + lineHeight / 2 }) - expect(editor.getCursorScreenPosition()).toEqual([0, editor.lineLengthForScreenRow(0)]) + expect(editor.getCursorScreenPosition()).toEqual([ + 0, + editor.lineLengthForScreenRow(0) + ]) component.didMouseDownOnContent({ detail: 1, button, - clientX: (clientLeftForCharacter(component, 3, 0) + clientLeftForCharacter(component, 3, 1)) / 2, + clientX: + (clientLeftForCharacter(component, 3, 0) + + clientLeftForCharacter(component, 3, 1)) / + 2, clientY: clientTopForLine(component, 1) + lineHeight / 2 }) expect(editor.getCursorScreenPosition()).toEqual([1, 0]) @@ -2924,7 +4053,10 @@ describe('TextEditorComponent', () => { component.didMouseDownOnContent({ detail: 1, button, - clientX: (clientLeftForCharacter(component, 3, 14) + clientLeftForCharacter(component, 3, 15)) / 2, + clientX: + (clientLeftForCharacter(component, 3, 14) + + clientLeftForCharacter(component, 3, 15)) / + 2, clientY: clientTopForLine(component, 3) + lineHeight / 2 }) expect(editor.getCursorScreenPosition()).toEqual([3, 14]) @@ -2932,7 +4064,11 @@ describe('TextEditorComponent', () => { component.didMouseDownOnContent({ detail: 1, button, - clientX: (clientLeftForCharacter(component, 3, 14) + clientLeftForCharacter(component, 3, 15)) / 2 + 1, + clientX: + (clientLeftForCharacter(component, 3, 14) + + clientLeftForCharacter(component, 3, 15)) / + 2 + + 1, clientY: clientTopForLine(component, 3) + lineHeight / 2 }) expect(editor.getCursorScreenPosition()).toEqual([3, 15]) @@ -2943,7 +4079,10 @@ describe('TextEditorComponent', () => { component.didMouseDownOnContent({ detail: 1, button, - clientX: (clientLeftForCharacter(component, 3, 14) + clientLeftForCharacter(component, 3, 16)) / 2, + clientX: + (clientLeftForCharacter(component, 3, 14) + + clientLeftForCharacter(component, 3, 16)) / + 2, clientY: clientTopForLine(component, 3) + lineHeight / 2 }) expect(editor.getCursorScreenPosition()).toEqual([3, 14]) @@ -2951,7 +4090,11 @@ describe('TextEditorComponent', () => { component.didMouseDownOnContent({ detail: 1, button, - clientX: (clientLeftForCharacter(component, 3, 14) + clientLeftForCharacter(component, 3, 16)) / 2 + 1, + clientX: + (clientLeftForCharacter(component, 3, 14) + + clientLeftForCharacter(component, 3, 16)) / + 2 + + 1, clientY: clientTopForLine(component, 3) + lineHeight / 2 }) expect(editor.getCursorScreenPosition()).toEqual([3, 16]) @@ -2963,26 +4106,59 @@ describe('TextEditorComponent', () => { describe('when the input is for the primary mouse button', () => { it('selects words on double-click', () => { - const {component, editor} = buildComponent() - const {clientX, clientY} = clientPositionForCharacter(component, 1, 16) - component.didMouseDownOnContent({detail: 1, button: 0, clientX, clientY}) - component.didMouseDownOnContent({detail: 2, button: 0, clientX, clientY}) + const { component, editor } = buildComponent() + const { clientX, clientY } = clientPositionForCharacter( + component, + 1, + 16 + ) + component.didMouseDownOnContent({ + detail: 1, + button: 0, + clientX, + clientY + }) + component.didMouseDownOnContent({ + detail: 2, + button: 0, + clientX, + clientY + }) expect(editor.getSelectedScreenRange()).toEqual([[1, 13], [1, 21]]) expect(editor.testAutoscrollRequests).toEqual([]) }) it('selects lines on triple-click', () => { - const {component, editor} = buildComponent() - const {clientX, clientY} = clientPositionForCharacter(component, 1, 16) - component.didMouseDownOnContent({detail: 1, button: 0, clientX, clientY}) - component.didMouseDownOnContent({detail: 2, button: 0, clientX, clientY}) - component.didMouseDownOnContent({detail: 3, button: 0, clientX, clientY}) + const { component, editor } = buildComponent() + const { clientX, clientY } = clientPositionForCharacter( + component, + 1, + 16 + ) + component.didMouseDownOnContent({ + detail: 1, + button: 0, + clientX, + clientY + }) + component.didMouseDownOnContent({ + detail: 2, + button: 0, + clientX, + clientY + }) + component.didMouseDownOnContent({ + detail: 3, + button: 0, + clientX, + clientY + }) expect(editor.getSelectedScreenRange()).toEqual([[1, 0], [2, 0]]) expect(editor.testAutoscrollRequests).toEqual([]) }) it('adds or removes cursors when holding cmd or ctrl when single-clicking', () => { - const {component, editor} = buildComponent({platform: 'darwin'}) + const { component, editor } = buildComponent({ platform: 'darwin' }) expect(editor.getCursorScreenPositions()).toEqual([[0, 0]]) // add cursor at 1, 16 @@ -3016,7 +4192,9 @@ describe('TextEditorComponent', () => { expect(editor.getCursorScreenPositions()).toEqual([[1, 16]]) // cmd-clicking within a selection destroys it - editor.addSelectionForScreenRange([[2, 10], [2, 15]], {autoscroll: false}) + editor.addSelectionForScreenRange([[2, 10], [2, 15]], { + autoscroll: false + }) expect(editor.getSelectedScreenRanges()).toEqual([ [[1, 16], [1, 16]], [[2, 10], [2, 15]] @@ -3028,9 +4206,7 @@ describe('TextEditorComponent', () => { metaKey: true }) ) - expect(editor.getSelectedScreenRanges()).toEqual([ - [[1, 16], [1, 16]] - ]) + expect(editor.getSelectedScreenRanges()).toEqual([[[1, 16], [1, 16]]]) // ctrl-click does not add cursors on macOS, nor does it move the cursor component.didMouseDownOnContent( @@ -3040,13 +4216,11 @@ describe('TextEditorComponent', () => { ctrlKey: true }) ) - expect(editor.getSelectedScreenRanges()).toEqual([ - [[1, 16], [1, 16]] - ]) + expect(editor.getSelectedScreenRanges()).toEqual([[[1, 16], [1, 16]]]) // ctrl-click adds cursors on platforms *other* than macOS component.props.platform = 'win32' - editor.setCursorScreenPosition([1, 4], {autoscroll: false}) + editor.setCursorScreenPosition([1, 4], { autoscroll: false }) component.didMouseDownOnContent( Object.assign(clientPositionForCharacter(component, 1, 16), { detail: 1, @@ -3060,8 +4234,8 @@ describe('TextEditorComponent', () => { }) it('adds word selections when holding cmd or ctrl when double-clicking', () => { - const {component, editor} = buildComponent() - editor.addCursorAtScreenPosition([1, 16], {autoscroll: false}) + const { component, editor } = buildComponent() + editor.addCursorAtScreenPosition([1, 16], { autoscroll: false }) expect(editor.getCursorScreenPositions()).toEqual([[0, 0], [1, 16]]) component.didMouseDownOnContent( @@ -3086,14 +4260,36 @@ describe('TextEditorComponent', () => { }) it('adds line selections when holding cmd or ctrl when triple-clicking', () => { - const {component, editor} = buildComponent() - editor.addCursorAtScreenPosition([1, 16], {autoscroll: false}) + const { component, editor } = buildComponent() + editor.addCursorAtScreenPosition([1, 16], { autoscroll: false }) expect(editor.getCursorScreenPositions()).toEqual([[0, 0], [1, 16]]) - const {clientX, clientY} = clientPositionForCharacter(component, 1, 16) - component.didMouseDownOnContent({detail: 1, button: 0, metaKey: true, clientX, clientY}) - component.didMouseDownOnContent({detail: 2, button: 0, metaKey: true, clientX, clientY}) - component.didMouseDownOnContent({detail: 3, button: 0, metaKey: true, clientX, clientY}) + const { clientX, clientY } = clientPositionForCharacter( + component, + 1, + 16 + ) + component.didMouseDownOnContent({ + detail: 1, + button: 0, + metaKey: true, + clientX, + clientY + }) + component.didMouseDownOnContent({ + detail: 2, + button: 0, + metaKey: true, + clientX, + clientY + }) + component.didMouseDownOnContent({ + detail: 3, + button: 0, + metaKey: true, + clientX, + clientY + }) expect(editor.getSelectedScreenRanges()).toEqual([ [[0, 0], [0, 0]], @@ -3103,73 +4299,111 @@ describe('TextEditorComponent', () => { }) it('expands the last selection on shift-click', () => { - const {component, element, editor} = buildComponent() + const { component, editor } = buildComponent() - editor.setCursorScreenPosition([2, 18], {autoscroll: false}) - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - shiftKey: true - }, clientPositionForCharacter(component, 1, 4))) + editor.setCursorScreenPosition([2, 18], { autoscroll: false }) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0, + shiftKey: true + }, + clientPositionForCharacter(component, 1, 4) + ) + ) expect(editor.getSelectedScreenRange()).toEqual([[1, 4], [2, 18]]) - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - shiftKey: true - }, clientPositionForCharacter(component, 4, 4))) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0, + shiftKey: true + }, + clientPositionForCharacter(component, 4, 4) + ) + ) expect(editor.getSelectedScreenRange()).toEqual([[2, 18], [4, 4]]) // reorients word-wise selections to keep the word selected regardless of // where the subsequent shift-click occurs - editor.setCursorScreenPosition([2, 18], {autoscroll: false}) - editor.getLastSelection().selectWord({autoscroll: false}) - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - shiftKey: true - }, clientPositionForCharacter(component, 1, 4))) + editor.setCursorScreenPosition([2, 18], { autoscroll: false }) + editor.getLastSelection().selectWord({ autoscroll: false }) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0, + shiftKey: true + }, + clientPositionForCharacter(component, 1, 4) + ) + ) expect(editor.getSelectedScreenRange()).toEqual([[1, 2], [2, 20]]) - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - shiftKey: true - }, clientPositionForCharacter(component, 3, 11))) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0, + shiftKey: true + }, + clientPositionForCharacter(component, 3, 11) + ) + ) expect(editor.getSelectedScreenRange()).toEqual([[2, 14], [3, 13]]) // reorients line-wise selections to keep the line selected regardless of // where the subsequent shift-click occurs - editor.setCursorScreenPosition([2, 18], {autoscroll: false}) - editor.getLastSelection().selectLine(null, {autoscroll: false}) - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - shiftKey: true - }, clientPositionForCharacter(component, 1, 4))) + editor.setCursorScreenPosition([2, 18], { autoscroll: false }) + editor.getLastSelection().selectLine(null, { autoscroll: false }) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0, + shiftKey: true + }, + clientPositionForCharacter(component, 1, 4) + ) + ) expect(editor.getSelectedScreenRange()).toEqual([[1, 0], [3, 0]]) - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - shiftKey: true - }, clientPositionForCharacter(component, 3, 11))) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0, + shiftKey: true + }, + clientPositionForCharacter(component, 3, 11) + ) + ) expect(editor.getSelectedScreenRange()).toEqual([[2, 0], [4, 0]]) expect(editor.testAutoscrollRequests).toEqual([]) }) it('expands the last selection on drag', () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() spyOn(component, 'handleMouseDragUntilMouseUp') - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - }, clientPositionForCharacter(component, 1, 4))) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0 + }, + clientPositionForCharacter(component, 1, 4) + ) + ) { - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[0][0] + const { + didDrag, + didStopDragging + } = component.handleMouseDragUntilMouseUp.argsForCall[0][0] didDrag(clientPositionForCharacter(component, 8, 8)) expect(editor.getSelectedScreenRange()).toEqual([[1, 4], [8, 8]]) didDrag(clientPositionForCharacter(component, 4, 8)) @@ -3180,13 +4414,21 @@ describe('TextEditorComponent', () => { // Click-drag a second selection... selections are not merged until the // drag stops. - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - metaKey: 1, - }, clientPositionForCharacter(component, 8, 8))) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0, + metaKey: 1 + }, + clientPositionForCharacter(component, 8, 8) + ) + ) { - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[1][0] + const { + didDrag, + didStopDragging + } = component.handleMouseDragUntilMouseUp.argsForCall[1][0] didDrag(clientPositionForCharacter(component, 2, 8)) expect(editor.getSelectedScreenRanges()).toEqual([ [[1, 4], [4, 8]], @@ -3203,26 +4445,36 @@ describe('TextEditorComponent', () => { [[2, 8], [8, 8]] ]) didStopDragging() - expect(editor.getSelectedScreenRanges()).toEqual([ - [[1, 4], [8, 8]] - ]) + expect(editor.getSelectedScreenRanges()).toEqual([[[1, 4], [8, 8]]]) } }) it('expands the selection word-wise on double-click-drag', () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() spyOn(component, 'handleMouseDragUntilMouseUp') - component.didMouseDownOnContent(Object.assign({ - detail: 1, - button: 0, - }, clientPositionForCharacter(component, 1, 4))) - component.didMouseDownOnContent(Object.assign({ - detail: 2, - button: 0, - }, clientPositionForCharacter(component, 1, 4))) + component.didMouseDownOnContent( + Object.assign( + { + detail: 1, + button: 0 + }, + clientPositionForCharacter(component, 1, 4) + ) + ) + component.didMouseDownOnContent( + Object.assign( + { + detail: 2, + button: 0 + }, + clientPositionForCharacter(component, 1, 4) + ) + ) - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[1][0] + const { + didDrag + } = component.handleMouseDragUntilMouseUp.argsForCall[1][0] didDrag(clientPositionForCharacter(component, 0, 8)) expect(editor.getSelectedScreenRange()).toEqual([[0, 4], [1, 5]]) didDrag(clientPositionForCharacter(component, 2, 10)) @@ -3230,15 +4482,27 @@ describe('TextEditorComponent', () => { }) it('expands the selection line-wise on triple-click-drag', () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() spyOn(component, 'handleMouseDragUntilMouseUp') - const tripleClickPosition = clientPositionForCharacter(component, 2, 8) - component.didMouseDownOnContent(Object.assign({detail: 1, button: 0}, tripleClickPosition)) - component.didMouseDownOnContent(Object.assign({detail: 2, button: 0}, tripleClickPosition)) - component.didMouseDownOnContent(Object.assign({detail: 3, button: 0}, tripleClickPosition)) + const tripleClickPosition = clientPositionForCharacter( + component, + 2, + 8 + ) + component.didMouseDownOnContent( + Object.assign({ detail: 1, button: 0 }, tripleClickPosition) + ) + component.didMouseDownOnContent( + Object.assign({ detail: 2, button: 0 }, tripleClickPosition) + ) + component.didMouseDownOnContent( + Object.assign({ detail: 3, button: 0 }, tripleClickPosition) + ) - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[2][0] + const { + didDrag + } = component.handleMouseDragUntilMouseUp.argsForCall[2][0] didDrag(clientPositionForCharacter(component, 1, 8)) expect(editor.getSelectedScreenRange()).toEqual([[1, 0], [3, 0]]) didDrag(clientPositionForCharacter(component, 4, 10)) @@ -3246,19 +4510,32 @@ describe('TextEditorComponent', () => { }) it('destroys folds when clicking on their fold markers', async () => { - const {component, element, editor} = buildComponent() + const { component, element, editor } = buildComponent() editor.foldBufferRow(1) await component.getNextUpdatePromise() const target = element.querySelector('.fold-marker') - const {clientX, clientY} = clientPositionForCharacter(component, 1, editor.lineLengthForScreenRow(1)) - component.didMouseDownOnContent({detail: 1, button: 0, target, clientX, clientY}) + const { clientX, clientY } = clientPositionForCharacter( + component, + 1, + editor.lineLengthForScreenRow(1) + ) + component.didMouseDownOnContent({ + detail: 1, + button: 0, + target, + clientX, + clientY + }) expect(editor.isFoldedAtBufferRow(1)).toBe(false) expect(editor.getCursorScreenPosition()).toEqual([0, 0]) }) it('autoscrolls the content when dragging near the edge of the scroll container', async () => { - const {component, element, editor} = buildComponent({width: 200, height: 200}) + const { component } = buildComponent({ + width: 200, + height: 200 + }) spyOn(component, 'handleMouseDragUntilMouseUp') let previousScrollTop = 0 @@ -3266,7 +4543,9 @@ describe('TextEditorComponent', () => { function assertScrolledDownAndRight () { expect(component.getScrollTop()).toBeGreaterThan(previousScrollTop) previousScrollTop = component.getScrollTop() - expect(component.getScrollLeft()).toBeGreaterThan(previousScrollLeft) + expect(component.getScrollLeft()).toBeGreaterThan( + previousScrollLeft + ) previousScrollLeft = component.getScrollLeft() } @@ -3277,26 +4556,45 @@ describe('TextEditorComponent', () => { previousScrollLeft = component.getScrollLeft() } - component.didMouseDownOnContent({detail: 1, button: 0, clientX: 100, clientY: 100}) - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[0][0] + component.didMouseDownOnContent({ + detail: 1, + button: 0, + clientX: 100, + clientY: 100 + }) + const { + didDrag + } = component.handleMouseDragUntilMouseUp.argsForCall[0][0] - didDrag({clientX: 199, clientY: 199}) + didDrag({ clientX: 199, clientY: 199 }) assertScrolledDownAndRight() - didDrag({clientX: 199, clientY: 199}) + didDrag({ clientX: 199, clientY: 199 }) assertScrolledDownAndRight() - didDrag({clientX: 199, clientY: 199}) + didDrag({ clientX: 199, clientY: 199 }) assertScrolledDownAndRight() - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) assertScrolledUpAndLeft() - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) assertScrolledUpAndLeft() - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) assertScrolledUpAndLeft() // Don't artificially update scroll position beyond possible values expect(component.getScrollTop()).toBe(0) expect(component.getScrollLeft()).toBe(0) - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) expect(component.getScrollTop()).toBe(0) expect(component.getScrollLeft()).toBe(0) @@ -3305,22 +4603,25 @@ describe('TextEditorComponent', () => { setScrollTop(component, maxScrollTop) await setScrollLeft(component, maxScrollLeft) - didDrag({clientX: 199, clientY: 199}) - didDrag({clientX: 199, clientY: 199}) - didDrag({clientX: 199, clientY: 199}) + didDrag({ clientX: 199, clientY: 199 }) + didDrag({ clientX: 199, clientY: 199 }) + didDrag({ clientX: 199, clientY: 199 }) expect(component.getScrollTop()).toBe(maxScrollTop) expect(component.getScrollLeft()).toBe(maxScrollLeft) }) }) it('pastes the previously selected text when clicking the middle mouse button on Linux', async () => { - spyOn(electron.ipcRenderer, 'send').andCallFake(function (eventName, selectedText) { + spyOn(electron.ipcRenderer, 'send').andCallFake(function ( + eventName, + selectedText + ) { if (eventName === 'write-text-to-selection-clipboard') { clipboard.writeText(selectedText, 'selection') } }) - const {component, editor} = buildComponent({platform: 'linux'}) + const { component, editor } = buildComponent({ platform: 'linux' }) // Middle mouse pasting. editor.setSelectedBufferRange([[1, 6], [1, 10]]) @@ -3352,13 +4653,19 @@ describe('TextEditorComponent', () => { }) it('does not paste into a read only editor when clicking the middle mouse button on Linux', async () => { - spyOn(electron.ipcRenderer, 'send').andCallFake(function (eventName, selectedText) { + spyOn(electron.ipcRenderer, 'send').andCallFake(function ( + eventName, + selectedText + ) { if (eventName === 'write-text-to-selection-clipboard') { clipboard.writeText(selectedText, 'selection') } }) - const {component, editor} = buildComponent({platform: 'linux', readOnly: true}) + const { component, editor } = buildComponent({ + platform: 'linux', + readOnly: true + }) // Select the word 'sort' on line 2 and copy to clipboard editor.setSelectedBufferRange([[1, 6], [1, 10]]) @@ -3379,7 +4686,7 @@ describe('TextEditorComponent', () => { describe('on the line number gutter', () => { it('selects all buffer rows intersecting the clicked screen row when a line number is clicked', async () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() spyOn(component, 'handleMouseDragUntilMouseUp') editor.setSoftWrapped(true) await component.getNextUpdatePromise() @@ -3407,7 +4714,7 @@ describe('TextEditorComponent', () => { }) it('adds new selections when a line number is meta-clicked', async () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() editor.setSoftWrapped(true) await component.getNextUpdatePromise() @@ -3450,7 +4757,7 @@ describe('TextEditorComponent', () => { }) it('expands the last selection when a line number is shift-clicked', async () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() spyOn(component, 'handleMouseDragUntilMouseUp') editor.setSoftWrapped(true) await component.getNextUpdatePromise() @@ -3473,7 +4780,10 @@ describe('TextEditorComponent', () => { ]) // Original selection is preserved when shift-click-dragging - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[0][0] + const { + didDrag, + didStopDragging + } = component.handleMouseDragUntilMouseUp.argsForCall[0][0] didDrag({ clientY: clientTopForLine(component, 1) }) @@ -3487,13 +4797,11 @@ describe('TextEditorComponent', () => { }) didStopDragging() - expect(editor.getSelectedBufferRanges()).toEqual([ - [[2, 10], [8, 0]] - ]) + expect(editor.getSelectedBufferRanges()).toEqual([[[2, 10], [8, 0]]]) }) it('expands the selection when dragging', async () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() spyOn(component, 'handleMouseDragUntilMouseUp') editor.setSoftWrapped(true) await component.getNextUpdatePromise() @@ -3510,7 +4818,10 @@ describe('TextEditorComponent', () => { clientY: clientTopForLine(component, 2) }) - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[0][0] + const { + didDrag, + didStopDragging + } = component.handleMouseDragUntilMouseUp.argsForCall[0][0] didDrag({ clientY: clientTopForLine(component, 1) @@ -3538,21 +4849,29 @@ describe('TextEditorComponent', () => { ]) didStopDragging() - expect(editor.getSelectedScreenRanges()).toEqual([ - [[2, 0], [4, 4]] - ]) + expect(editor.getSelectedScreenRanges()).toEqual([[[2, 0], [4, 4]]]) }) it('toggles folding when clicking on the right icon of a foldable line number', async () => { - const {component, element, editor} = buildComponent() - let target = element.querySelectorAll('.line-number')[1].querySelector('.icon-right') + const { component, element, editor } = buildComponent() + let target = element + .querySelectorAll('.line-number')[1] + .querySelector('.icon-right') expect(editor.isFoldedAtScreenRow(1)).toBe(false) - component.didMouseDownOnLineNumberGutter({target, button: 0, clientY: clientTopForLine(component, 1)}) + component.didMouseDownOnLineNumberGutter({ + target, + button: 0, + clientY: clientTopForLine(component, 1) + }) expect(editor.isFoldedAtScreenRow(1)).toBe(true) await component.getNextUpdatePromise() - component.didMouseDownOnLineNumberGutter({target, button: 0, clientY: clientTopForLine(component, 1)}) + component.didMouseDownOnLineNumberGutter({ + target, + button: 0, + clientY: clientTopForLine(component, 1) + }) await component.getNextUpdatePromise() expect(editor.isFoldedAtScreenRow(1)).toBe(false) @@ -3560,14 +4879,22 @@ describe('TextEditorComponent', () => { await component.getNextUpdatePromise() expect(editor.isFoldedAtScreenRow(5)).toBe(true) - target = element.querySelectorAll('.line-number')[4].querySelector('.icon-right') - component.didMouseDownOnLineNumberGutter({target, button: 0, clientY: clientTopForLine(component, 4)}) + target = element + .querySelectorAll('.line-number')[4] + .querySelector('.icon-right') + component.didMouseDownOnLineNumberGutter({ + target, + button: 0, + clientY: clientTopForLine(component, 4) + }) expect(editor.isFoldedAtScreenRow(4)).toBe(false) }) it('autoscrolls when dragging near the top or bottom of the gutter', async () => { - const {component, editor} = buildComponent({width: 200, height: 200}) - const {scrollContainer} = component.refs + const { component } = buildComponent({ + width: 200, + height: 200 + }) spyOn(component, 'handleMouseDragUntilMouseUp') let previousScrollTop = 0 @@ -3586,26 +4913,45 @@ describe('TextEditorComponent', () => { previousScrollLeft = component.getScrollLeft() } - component.didMouseDownOnLineNumberGutter({detail: 1, button: 0, clientX: 0, clientY: 100}) - const {didDrag, didStopDragging} = component.handleMouseDragUntilMouseUp.argsForCall[0][0] - didDrag({clientX: 199, clientY: 199}) + component.didMouseDownOnLineNumberGutter({ + detail: 1, + button: 0, + clientX: 0, + clientY: 100 + }) + const { + didDrag + } = component.handleMouseDragUntilMouseUp.argsForCall[0][0] + didDrag({ clientX: 199, clientY: 199 }) assertScrolledDown() - didDrag({clientX: 199, clientY: 199}) + didDrag({ clientX: 199, clientY: 199 }) assertScrolledDown() - didDrag({clientX: 199, clientY: 199}) + didDrag({ clientX: 199, clientY: 199 }) assertScrolledDown() - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) assertScrolledUp() - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) assertScrolledUp() - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) assertScrolledUp() // Don't artificially update scroll measurements beyond the minimum or // maximum possible scroll positions expect(component.getScrollTop()).toBe(0) expect(component.getScrollLeft()).toBe(0) - didDrag({clientX: component.getGutterContainerWidth() + 1, clientY: 1}) + didDrag({ + clientX: component.getGutterContainerWidth() + 1, + clientY: 1 + }) expect(component.getScrollTop()).toBe(0) expect(component.getScrollLeft()).toBe(0) @@ -3614,9 +4960,9 @@ describe('TextEditorComponent', () => { setScrollTop(component, maxScrollTop) await setScrollLeft(component, maxScrollLeft) - didDrag({clientX: 199, clientY: 199}) - didDrag({clientX: 199, clientY: 199}) - didDrag({clientX: 199, clientY: 199}) + didDrag({ clientX: 199, clientY: 199 }) + didDrag({ clientX: 199, clientY: 199 }) + didDrag({ clientX: 199, clientY: 199 }) expect(component.getScrollTop()).toBe(maxScrollTop) expect(component.getScrollLeft()).toBe(maxScrollLeft) }) @@ -3624,13 +4970,17 @@ describe('TextEditorComponent', () => { describe('on the scrollbars', () => { it('delegates the mousedown events to the parent component unless the mousedown was on the actual scrollbar', async () => { - const {component, element, editor} = buildComponent({height: 100}) + const { component, editor } = buildComponent({ height: 100 }) await setEditorWidthInCharacters(component, 6) const verticalScrollbar = component.refs.verticalScrollbar const horizontalScrollbar = component.refs.horizontalScrollbar - const leftEdgeOfVerticalScrollbar = verticalScrollbar.element.getBoundingClientRect().right - verticalScrollbarWidth - const topEdgeOfHorizontalScrollbar = horizontalScrollbar.element.getBoundingClientRect().bottom - horizontalScrollbarHeight + const leftEdgeOfVerticalScrollbar = + verticalScrollbar.element.getBoundingClientRect().right - + verticalScrollbarWidth + const topEdgeOfHorizontalScrollbar = + horizontalScrollbar.element.getBoundingClientRect().bottom - + horizontalScrollbarHeight verticalScrollbar.didMouseDown({ button: 0, @@ -3669,7 +5019,7 @@ describe('TextEditorComponent', () => { describe('paste event', () => { it("prevents the browser's default processing for the event on Linux", () => { - const {component} = buildComponent({platform: 'linux'}) + const { component } = buildComponent({ platform: 'linux' }) const event = { preventDefault: () => {} } spyOn(event, 'preventDefault') @@ -3680,194 +5030,293 @@ describe('TextEditorComponent', () => { describe('keyboard input', () => { it('handles inserted accented characters via the press-and-hold menu on macOS correctly', () => { - const {editor, component, element} = buildComponent({text: '', chromeVersion: 57}) + const { editor, component } = buildComponent({ + text: '', + chromeVersion: 57 + }) editor.insertText('x') editor.setCursorBufferPosition([0, 1]) // Simulate holding the A key to open the press-and-hold menu, // then closing it via ESC. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'KeyA'}) - component.didKeyup({code: 'KeyA'}) - component.didKeydown({code: 'Escape'}) - component.didKeyup({code: 'Escape'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeyup({ code: 'KeyA' }) + component.didKeydown({ code: 'Escape' }) + component.didKeyup({ code: 'Escape' }) expect(editor.getText()).toBe('xa') // Ensure another "a" can be typed correctly. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyA'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyA' }) expect(editor.getText()).toBe('xaa') editor.undo() expect(editor.getText()).toBe('x') // Simulate holding the A key to open the press-and-hold menu, // then selecting an alternative by typing a number. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'KeyA'}) - component.didKeyup({code: 'KeyA'}) - component.didKeydown({code: 'Digit2'}) - component.didKeyup({code: 'Digit2'}) - component.didTextInput({data: 'á', stopPropagation: () => {}, preventDefault: () => {}}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeyup({ code: 'KeyA' }) + component.didKeydown({ code: 'Digit2' }) + component.didKeyup({ code: 'Digit2' }) + component.didTextInput({ + data: 'á', + stopPropagation: () => {}, + preventDefault: () => {} + }) expect(editor.getText()).toBe('xá') // Ensure another "a" can be typed correctly. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyA'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyA' }) expect(editor.getText()).toBe('xáa') editor.undo() expect(editor.getText()).toBe('x') // Simulate holding the A key to open the press-and-hold menu, // then selecting an alternative by clicking on it. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'KeyA'}) - component.didKeyup({code: 'KeyA'}) - component.didTextInput({data: 'á', stopPropagation: () => {}, preventDefault: () => {}}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeyup({ code: 'KeyA' }) + component.didTextInput({ + data: 'á', + stopPropagation: () => {}, + preventDefault: () => {} + }) expect(editor.getText()).toBe('xá') // Ensure another "a" can be typed correctly. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyA'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyA' }) expect(editor.getText()).toBe('xáa') editor.undo() expect(editor.getText()).toBe('x') // Simulate holding the A key to open the press-and-hold menu, // cycling through the alternatives with the arrows, then selecting one of them with Enter. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'KeyA'}) - component.didKeyup({code: 'KeyA'}) - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionStart({data: ''}) - component.didCompositionUpdate({data: 'à'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeyup({ code: 'KeyA' }) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionStart({ data: '' }) + component.didCompositionUpdate({ data: 'à' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xà') - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionUpdate({data: 'á'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionUpdate({ data: 'á' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xá') - component.didKeydown({code: 'Enter'}) - component.didCompositionUpdate({data: 'á'}) - component.didTextInput({data: 'á', stopPropagation: () => {}, preventDefault: () => {}}) - component.didCompositionEnd({data: 'á', target: component.refs.cursorsAndInput.refs.hiddenInput}) - component.didKeyup({code: 'Enter'}) + component.didKeydown({ code: 'Enter' }) + component.didCompositionUpdate({ data: 'á' }) + component.didTextInput({ + data: 'á', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didCompositionEnd({ + data: 'á', + target: component.refs.cursorsAndInput.refs.hiddenInput + }) + component.didKeyup({ code: 'Enter' }) expect(editor.getText()).toBe('xá') // Ensure another "a" can be typed correctly. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyA'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyA' }) expect(editor.getText()).toBe('xáa') editor.undo() expect(editor.getText()).toBe('x') // Simulate holding the A key to open the press-and-hold menu, // cycling through the alternatives with the arrows, then closing it via ESC. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'KeyA'}) - component.didKeyup({code: 'KeyA'}) - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionStart({data: ''}) - component.didCompositionUpdate({data: 'à'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeyup({ code: 'KeyA' }) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionStart({ data: '' }) + component.didCompositionUpdate({ data: 'à' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xà') - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionUpdate({data: 'á'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionUpdate({ data: 'á' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xá') - component.didKeydown({code: 'Escape'}) - component.didCompositionUpdate({data: 'a'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didCompositionEnd({data: 'a', target: component.refs.cursorsAndInput.refs.hiddenInput}) - component.didKeyup({code: 'Escape'}) + component.didKeydown({ code: 'Escape' }) + component.didCompositionUpdate({ data: 'a' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didCompositionEnd({ + data: 'a', + target: component.refs.cursorsAndInput.refs.hiddenInput + }) + component.didKeyup({ code: 'Escape' }) expect(editor.getText()).toBe('xa') // Ensure another "a" can be typed correctly. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyA'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyA' }) expect(editor.getText()).toBe('xaa') editor.undo() expect(editor.getText()).toBe('x') // Simulate pressing the O key and holding the A key to open the press-and-hold menu right before releasing the O key, // cycling through the alternatives with the arrows, then closing it via ESC. - component.didKeydown({code: 'KeyO'}) - component.didKeypress({code: 'KeyO'}) - component.didTextInput({data: 'o', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyO'}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionStart({data: ''}) - component.didCompositionUpdate({data: 'à'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'KeyO' }) + component.didKeypress({ code: 'KeyO' }) + component.didTextInput({ + data: 'o', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyO' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionStart({ data: '' }) + component.didCompositionUpdate({ data: 'à' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xoà') - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionUpdate({data: 'á'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionUpdate({ data: 'á' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xoá') - component.didKeydown({code: 'Escape'}) - component.didCompositionUpdate({data: 'a'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didCompositionEnd({data: 'a', target: component.refs.cursorsAndInput.refs.hiddenInput}) - component.didKeyup({code: 'Escape'}) + component.didKeydown({ code: 'Escape' }) + component.didCompositionUpdate({ data: 'a' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didCompositionEnd({ + data: 'a', + target: component.refs.cursorsAndInput.refs.hiddenInput + }) + component.didKeyup({ code: 'Escape' }) expect(editor.getText()).toBe('xoa') // Ensure another "a" can be typed correctly. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyA'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyA' }) editor.undo() expect(editor.getText()).toBe('x') // Simulate holding the A key to open the press-and-hold menu, // cycling through the alternatives with the arrows, then closing it by changing focus. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeydown({code: 'KeyA'}) - component.didKeydown({code: 'KeyA'}) - component.didKeyup({code: 'KeyA'}) - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionStart({data: ''}) - component.didCompositionUpdate({data: 'à'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeydown({ code: 'KeyA' }) + component.didKeydown({ code: 'KeyA' }) + component.didKeyup({ code: 'KeyA' }) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionStart({ data: '' }) + component.didCompositionUpdate({ data: 'à' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xà') - component.didKeydown({code: 'ArrowRight'}) - component.didCompositionUpdate({data: 'á'}) - component.didKeyup({code: 'ArrowRight'}) + component.didKeydown({ code: 'ArrowRight' }) + component.didCompositionUpdate({ data: 'á' }) + component.didKeyup({ code: 'ArrowRight' }) expect(editor.getText()).toBe('xá') - component.didCompositionUpdate({data: 'á'}) - component.didTextInput({data: 'á', stopPropagation: () => {}, preventDefault: () => {}}) - component.didCompositionEnd({data: 'á', target: component.refs.cursorsAndInput.refs.hiddenInput}) + component.didCompositionUpdate({ data: 'á' }) + component.didTextInput({ + data: 'á', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didCompositionEnd({ + data: 'á', + target: component.refs.cursorsAndInput.refs.hiddenInput + }) expect(editor.getText()).toBe('xá') // Ensure another "a" can be typed correctly. - component.didKeydown({code: 'KeyA'}) - component.didKeypress({code: 'KeyA'}) - component.didTextInput({data: 'a', stopPropagation: () => {}, preventDefault: () => {}}) - component.didKeyup({code: 'KeyA'}) + component.didKeydown({ code: 'KeyA' }) + component.didKeypress({ code: 'KeyA' }) + component.didTextInput({ + data: 'a', + stopPropagation: () => {}, + preventDefault: () => {} + }) + component.didKeyup({ code: 'KeyA' }) expect(editor.getText()).toBe('xáa') editor.undo() expect(editor.getText()).toBe('x') @@ -3876,9 +5325,12 @@ describe('TextEditorComponent', () => { describe('styling changes', () => { it('updates the rendered content based on new measurements when the font dimensions change', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 1, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 1, + autoHeight: false + }) await setEditorHeightInLines(component, 3) - editor.setCursorScreenPosition([1, 29], {autoscroll: false}) + editor.setCursorScreenPosition([1, 29], { autoscroll: false }) await component.getNextUpdatePromise() let cursorNode = element.querySelector('.cursor') @@ -3901,26 +5353,49 @@ describe('TextEditorComponent', () => { element.style.fontSize = initialFontSize - 5 + 'px' TextEditor.didUpdateStyles() await component.getNextUpdatePromise() - expect(editor.getDefaultCharWidth()).toBeLessThan(initialBaseCharacterWidth) - expect(editor.getDoubleWidthCharWidth()).toBeLessThan(initialDoubleCharacterWidth) - expect(editor.getHalfWidthCharWidth()).toBeLessThan(initialHalfCharacterWidth) - expect(editor.getKoreanCharWidth()).toBeLessThan(initialKoreanCharacterWidth) - expect(queryOnScreenLineElements(element).length).toBeGreaterThan(initialRenderedLineCount) + expect(editor.getDefaultCharWidth()).toBeLessThan( + initialBaseCharacterWidth + ) + expect(editor.getDoubleWidthCharWidth()).toBeLessThan( + initialDoubleCharacterWidth + ) + expect(editor.getHalfWidthCharWidth()).toBeLessThan( + initialHalfCharacterWidth + ) + expect(editor.getKoreanCharWidth()).toBeLessThan( + initialKoreanCharacterWidth + ) + expect(queryOnScreenLineElements(element).length).toBeGreaterThan( + initialRenderedLineCount + ) verifyCursorPosition(component, cursorNode, 1, 29) element.style.fontSize = initialFontSize + 10 + 'px' TextEditor.didUpdateStyles() await component.getNextUpdatePromise() - expect(editor.getDefaultCharWidth()).toBeGreaterThan(initialBaseCharacterWidth) - expect(editor.getDoubleWidthCharWidth()).toBeGreaterThan(initialDoubleCharacterWidth) - expect(editor.getHalfWidthCharWidth()).toBeGreaterThan(initialHalfCharacterWidth) - expect(editor.getKoreanCharWidth()).toBeGreaterThan(initialKoreanCharacterWidth) - expect(queryOnScreenLineElements(element).length).toBeLessThan(initialRenderedLineCount) + expect(editor.getDefaultCharWidth()).toBeGreaterThan( + initialBaseCharacterWidth + ) + expect(editor.getDoubleWidthCharWidth()).toBeGreaterThan( + initialDoubleCharacterWidth + ) + expect(editor.getHalfWidthCharWidth()).toBeGreaterThan( + initialHalfCharacterWidth + ) + expect(editor.getKoreanCharWidth()).toBeGreaterThan( + initialKoreanCharacterWidth + ) + expect(queryOnScreenLineElements(element).length).toBeLessThan( + initialRenderedLineCount + ) verifyCursorPosition(component, cursorNode, 1, 29) }) it('maintains the scrollTopRow and scrollLeftColumn when the font size changes', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 1, autoHeight: false}) + const { component, element } = buildComponent({ + rowsPerTile: 1, + autoHeight: false + }) await setEditorHeightInLines(component, 3) await setEditorWidthInCharacters(component, 20) component.setScrollTopRow(4) @@ -3940,19 +5415,25 @@ describe('TextEditorComponent', () => { }) it('gracefully handles the editor being hidden after a styling change', async () => { - const {component, element, editor} = buildComponent({autoHeight: false}) - element.style.fontSize = parseInt(getComputedStyle(element).fontSize) + 5 + 'px' + const { component, element } = buildComponent({ + autoHeight: false + }) + element.style.fontSize = + parseInt(getComputedStyle(element).fontSize) + 5 + 'px' TextEditor.didUpdateStyles() element.style.display = 'none' await component.getNextUpdatePromise() }) it('does not throw an exception when the editor is soft-wrapped and changing the font size changes also the longest screen line', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 3, + autoHeight: false + }) editor.setText( 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do\n' + - 'eiusmod tempor incididunt ut labore et dolore magna' + - 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation' + 'eiusmod tempor incididunt ut labore et dolore magna' + + 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation' ) editor.setSoftWrapped(true) await setEditorHeightInLines(component, 2) @@ -3965,12 +5446,15 @@ describe('TextEditorComponent', () => { }) it('updates the width of the lines div based on the longest screen line', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 1, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 1, + autoHeight: false + }) editor.setText( 'Lorem ipsum dolor sit\n' + - 'amet, consectetur adipisicing\n' + - 'elit, sed do\n' + - 'eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation' + 'amet, consectetur adipisicing\n' + + 'elit, sed do\n' + + 'eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation' ) await setEditorHeightInLines(component, 2) @@ -3983,7 +5467,7 @@ describe('TextEditorComponent', () => { const actualWidth = element.querySelector('.lines').style.width const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(3, Infinity)).left + - component.getBaseCharacterWidth() + component.getBaseCharacterWidth() ) expect(actualWidth).toBe(expectedWidth + 'px') }) @@ -3993,26 +5477,36 @@ describe('TextEditorComponent', () => { let editorElementWasUpdatedSynchronously beforeEach(() => { - editorElementWasUpdatedSynchronously = TextEditorElement.prototype.updatedSynchronously + editorElementWasUpdatedSynchronously = + TextEditorElement.prototype.updatedSynchronously }) afterEach(() => { - TextEditorElement.prototype.setUpdatedSynchronously(editorElementWasUpdatedSynchronously) + TextEditorElement.prototype.setUpdatedSynchronously( + editorElementWasUpdatedSynchronously + ) }) it('updates synchronously when updatedSynchronously is true', () => { const editor = buildEditor() - const {element} = new TextEditorComponent({model: editor, updatedSynchronously: true}) + const { element } = new TextEditorComponent({ + model: editor, + updatedSynchronously: true + }) jasmine.attachToDOM(element) editor.setText('Lorem ipsum dolor') - expect(queryOnScreenLineElements(element).map(l => l.textContent)).toEqual([ - editor.lineTextForScreenRow(0) - ]) + expect( + queryOnScreenLineElements(element).map(l => l.textContent) + ).toEqual([editor.lineTextForScreenRow(0)]) }) it('does not throw an exception on attachment when setting the soft-wrap column', () => { - const {component, element, editor} = buildComponent({width: 435, attach: false, updatedSynchronously: true}) + const { element, editor } = buildComponent({ + width: 435, + attach: false, + updatedSynchronously: true + }) editor.setSoftWrapped(true) spyOn(window, 'onerror').andCallThrough() jasmine.attachToDOM(element) // should not throw an exception @@ -4026,14 +5520,14 @@ describe('TextEditorComponent', () => { jasmine.attachToDOM(element) editor.setText('Lorem ipsum dolor') - expect(queryOnScreenLineElements(element).map(l => l.textContent)).toEqual([ - editor.lineTextForScreenRow(0) - ]) + expect( + queryOnScreenLineElements(element).map(l => l.textContent) + ).toEqual([editor.lineTextForScreenRow(0)]) }) it('measures dimensions synchronously when measureDimensions is called on the component', () => { TextEditorElement.prototype.setUpdatedSynchronously(true) - const editor = buildEditor({autoHeight: false}) + const editor = buildEditor({ autoHeight: false }) const element = editor.element jasmine.attachToDOM(element) @@ -4046,62 +5540,113 @@ describe('TextEditorComponent', () => { describe('pixelPositionForScreenPosition(point)', () => { it('returns the pixel position for the given point, regardless of whether or not it is currently on screen', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, autoHeight: false}) + const { component, editor } = buildComponent({ + rowsPerTile: 2, + autoHeight: false + }) await setEditorHeightInLines(component, 3) await setScrollTop(component, 3 * component.getLineHeight()) - const {component: referenceComponent} = buildComponent() + const { component: referenceComponent } = buildComponent() const referenceContentRect = referenceComponent.refs.content.getBoundingClientRect() { - const {top, left} = component.pixelPositionForScreenPosition({row: 0, column: 0}) - expect(top).toBe(clientTopForLine(referenceComponent, 0) - referenceContentRect.top) - expect(left).toBe(clientLeftForCharacter(referenceComponent, 0, 0) - referenceContentRect.left) + const { top, left } = component.pixelPositionForScreenPosition({ + row: 0, + column: 0 + }) + expect(top).toBe( + clientTopForLine(referenceComponent, 0) - referenceContentRect.top + ) + expect(left).toBe( + clientLeftForCharacter(referenceComponent, 0, 0) - + referenceContentRect.left + ) } { - const {top, left} = component.pixelPositionForScreenPosition({row: 0, column: 5}) - expect(top).toBe(clientTopForLine(referenceComponent, 0) - referenceContentRect.top) - expect(left).toBe(clientLeftForCharacter(referenceComponent, 0, 5) - referenceContentRect.left) + const { top, left } = component.pixelPositionForScreenPosition({ + row: 0, + column: 5 + }) + expect(top).toBe( + clientTopForLine(referenceComponent, 0) - referenceContentRect.top + ) + expect(left).toBe( + clientLeftForCharacter(referenceComponent, 0, 5) - + referenceContentRect.left + ) } { - const {top, left} = component.pixelPositionForScreenPosition({row: 12, column: 1}) - expect(top).toBe(clientTopForLine(referenceComponent, 12) - referenceContentRect.top) - expect(left).toBe(clientLeftForCharacter(referenceComponent, 12, 1) - referenceContentRect.left) + const { top, left } = component.pixelPositionForScreenPosition({ + row: 12, + column: 1 + }) + expect(top).toBe( + clientTopForLine(referenceComponent, 12) - referenceContentRect.top + ) + expect(left).toBe( + clientLeftForCharacter(referenceComponent, 12, 1) - + referenceContentRect.left + ) } // Measuring a currently rendered line while an autoscroll that causes // that line to go off-screen is in progress. { editor.setCursorScreenPosition([10, 0]) - const {top, left} = component.pixelPositionForScreenPosition({row: 3, column: 5}) - expect(top).toBe(clientTopForLine(referenceComponent, 3) - referenceContentRect.top) - expect(left).toBe(clientLeftForCharacter(referenceComponent, 3, 5) - referenceContentRect.left) + const { top, left } = component.pixelPositionForScreenPosition({ + row: 3, + column: 5 + }) + expect(top).toBe( + clientTopForLine(referenceComponent, 3) - referenceContentRect.top + ) + expect(left).toBe( + clientLeftForCharacter(referenceComponent, 3, 5) - + referenceContentRect.left + ) } }) it('does not get the component into an inconsistent state when the model has unflushed changes (regression)', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, autoHeight: false, text: ''}) + const { component, editor } = buildComponent({ + rowsPerTile: 2, + autoHeight: false, + text: '' + }) await setEditorHeightInLines(component, 10) - const updatePromise = editor.getBuffer().append("hi\n") - component.screenPositionForPixelPosition({top: 800, left: 1}) + const updatePromise = editor.getBuffer().append('hi\n') + component.screenPositionForPixelPosition({ top: 800, left: 1 }) await updatePromise }) it('does not shift cursors downward or render off-screen content when measuring off-screen lines (regression)', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, autoHeight: false}) + const { component, element } = buildComponent({ + rowsPerTile: 2, + autoHeight: false + }) await setEditorHeightInLines(component, 3) - const {top, left} = component.pixelPositionForScreenPosition({row: 12, column: 1}) + component.pixelPositionForScreenPosition({ + row: 12, + column: 1 + }) - expect(element.querySelector('.cursor').getBoundingClientRect().top).toBe(component.refs.lineTiles.getBoundingClientRect().top) - expect(element.querySelector('.line[data-screen-row="12"]').style.visibility).toBe('hidden') + expect(element.querySelector('.cursor').getBoundingClientRect().top).toBe( + component.refs.lineTiles.getBoundingClientRect().top + ) + expect( + element.querySelector('.line[data-screen-row="12"]').style.visibility + ).toBe('hidden') // Ensure previously measured off screen lines don't have any weird // styling when they come on screen in the next frame await setEditorHeightInLines(component, 13) - const previouslyMeasuredLineElement = element.querySelector('.line[data-screen-row="12"]') + const previouslyMeasuredLineElement = element.querySelector( + '.line[data-screen-row="12"]' + ) expect(previouslyMeasuredLineElement.style.display).toBe('') expect(previouslyMeasuredLineElement.style.visibility).toBe('') }) @@ -4109,54 +5654,79 @@ describe('TextEditorComponent', () => { describe('screenPositionForPixelPosition', () => { it('returns the screen position for the given pixel position, regardless of whether or not it is currently on screen', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 2, autoHeight: false}) + const { component, editor } = buildComponent({ + rowsPerTile: 2, + autoHeight: false + }) await setEditorHeightInLines(component, 3) await setScrollTop(component, 3 * component.getLineHeight()) - const {component: referenceComponent} = buildComponent() + const { component: referenceComponent } = buildComponent() { - const pixelPosition = referenceComponent.pixelPositionForScreenPosition({row: 0, column: 0}) + const pixelPosition = referenceComponent.pixelPositionForScreenPosition( + { row: 0, column: 0 } + ) pixelPosition.top += component.getLineHeight() / 3 pixelPosition.left += component.getBaseCharacterWidth() / 3 - expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual([0, 0]) + expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual( + [0, 0] + ) } { - const pixelPosition = referenceComponent.pixelPositionForScreenPosition({row: 0, column: 5}) + const pixelPosition = referenceComponent.pixelPositionForScreenPosition( + { row: 0, column: 5 } + ) pixelPosition.top += component.getLineHeight() / 3 pixelPosition.left += component.getBaseCharacterWidth() / 3 - expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual([0, 5]) + expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual( + [0, 5] + ) } { - const pixelPosition = referenceComponent.pixelPositionForScreenPosition({row: 5, column: 7}) + const pixelPosition = referenceComponent.pixelPositionForScreenPosition( + { row: 5, column: 7 } + ) pixelPosition.top += component.getLineHeight() / 3 pixelPosition.left += component.getBaseCharacterWidth() / 3 - expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual([5, 7]) + expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual( + [5, 7] + ) } { - const pixelPosition = referenceComponent.pixelPositionForScreenPosition({row: 12, column: 1}) + const pixelPosition = referenceComponent.pixelPositionForScreenPosition( + { row: 12, column: 1 } + ) pixelPosition.top += component.getLineHeight() / 3 pixelPosition.left += component.getBaseCharacterWidth() / 3 - expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual([12, 1]) + expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual( + [12, 1] + ) } // Measuring a currently rendered line while an autoscroll that causes // that line to go off-screen is in progress. { - const pixelPosition = referenceComponent.pixelPositionForScreenPosition({row: 3, column: 4}) + const pixelPosition = referenceComponent.pixelPositionForScreenPosition( + { row: 3, column: 4 } + ) pixelPosition.top += component.getLineHeight() / 3 pixelPosition.left += component.getBaseCharacterWidth() / 3 editor.setCursorBufferPosition([10, 0]) - expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual([3, 4]) + expect(component.screenPositionForPixelPosition(pixelPosition)).toEqual( + [3, 4] + ) } }) }) describe('model methods that delegate to the component / element', () => { it('delegates setHeight and getHeight to the component', async () => { - const {component, element, editor} = buildComponent({autoHeight: false}) + const { component, editor } = buildComponent({ + autoHeight: false + }) spyOn(Grim, 'deprecate') expect(editor.getHeight()).toBe(component.getScrollContainerHeight()) expect(Grim.deprecate.callCount).toBe(1) @@ -4168,7 +5738,7 @@ describe('TextEditorComponent', () => { }) it('delegates setWidth and getWidth to the component', async () => { - const {component, element, editor} = buildComponent() + const { component, editor } = buildComponent() spyOn(Grim, 'deprecate') expect(editor.getWidth()).toBe(component.getScrollContainerWidth()) expect(Grim.deprecate.callCount).toBe(1) @@ -4180,42 +5750,67 @@ describe('TextEditorComponent', () => { }) it('delegates getFirstVisibleScreenRow, getLastVisibleScreenRow, and getVisibleRowRange to the component', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 3, + autoHeight: false + }) element.style.height = 4 * component.measurements.lineHeight + 'px' await component.getNextUpdatePromise() await setScrollTop(component, 5 * component.getLineHeight()) - expect(editor.getFirstVisibleScreenRow()).toBe(component.getFirstVisibleRow()) - expect(editor.getLastVisibleScreenRow()).toBe(component.getLastVisibleRow()) - expect(editor.getVisibleRowRange()).toEqual([component.getFirstVisibleRow(), component.getLastVisibleRow()]) + expect(editor.getFirstVisibleScreenRow()).toBe( + component.getFirstVisibleRow() + ) + expect(editor.getLastVisibleScreenRow()).toBe( + component.getLastVisibleRow() + ) + expect(editor.getVisibleRowRange()).toEqual([ + component.getFirstVisibleRow(), + component.getLastVisibleRow() + ]) }) it('assigns scrollTop on the component when calling setFirstVisibleScreenRow', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) - element.style.height = 4 * component.measurements.lineHeight + horizontalScrollbarHeight + 'px' + const { component, element, editor } = buildComponent({ + rowsPerTile: 3, + autoHeight: false + }) + element.style.height = + 4 * component.measurements.lineHeight + horizontalScrollbarHeight + 'px' await component.getNextUpdatePromise() expect(component.getMaxScrollTop() / component.getLineHeight()).toBe(9) - expect(component.refs.verticalScrollbar.element.scrollTop).toBe(0 * component.getLineHeight()) + expect(component.refs.verticalScrollbar.element.scrollTop).toBe( + 0 * component.getLineHeight() + ) editor.setFirstVisibleScreenRow(1) expect(component.getFirstVisibleRow()).toBe(1) await component.getNextUpdatePromise() - expect(component.refs.verticalScrollbar.element.scrollTop).toBe(1 * component.getLineHeight()) + expect(component.refs.verticalScrollbar.element.scrollTop).toBe( + 1 * component.getLineHeight() + ) editor.setFirstVisibleScreenRow(5) expect(component.getFirstVisibleRow()).toBe(5) await component.getNextUpdatePromise() - expect(component.refs.verticalScrollbar.element.scrollTop).toBe(5 * component.getLineHeight()) + expect(component.refs.verticalScrollbar.element.scrollTop).toBe( + 5 * component.getLineHeight() + ) editor.setFirstVisibleScreenRow(11) expect(component.getFirstVisibleRow()).toBe(9) await component.getNextUpdatePromise() - expect(component.refs.verticalScrollbar.element.scrollTop).toBe(9 * component.getLineHeight()) + expect(component.refs.verticalScrollbar.element.scrollTop).toBe( + 9 * component.getLineHeight() + ) }) it('delegates setFirstVisibleScreenColumn and getFirstVisibleScreenColumn to the component', async () => { - const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) + const { component, element, editor } = buildComponent({ + rowsPerTile: 3, + autoHeight: false + }) element.style.width = 30 * component.getBaseCharacterWidth() + 'px' await component.getNextUpdatePromise() expect(editor.getFirstVisibleScreenColumn()).toBe(0) @@ -4224,27 +5819,38 @@ describe('TextEditorComponent', () => { setScrollLeft(component, 5.5 * component.getBaseCharacterWidth()) expect(editor.getFirstVisibleScreenColumn()).toBe(5) await component.getNextUpdatePromise() - expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo(5.5 * component.getBaseCharacterWidth(), -1) + expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo( + 5.5 * component.getBaseCharacterWidth(), + -1 + ) editor.setFirstVisibleScreenColumn(12) - expect(component.getScrollLeft()).toBeCloseTo(12 * component.getBaseCharacterWidth(), -1) + expect(component.getScrollLeft()).toBeCloseTo( + 12 * component.getBaseCharacterWidth(), + -1 + ) await component.getNextUpdatePromise() - expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo(12 * component.getBaseCharacterWidth(), -1) + expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo( + 12 * component.getBaseCharacterWidth(), + -1 + ) }) }) describe('handleMouseDragUntilMouseUp', () => { it('repeatedly schedules `didDrag` calls on new animation frames after moving the mouse, and calls `didStopDragging` on mouseup', async () => { - const {component} = buildComponent() + const { component } = buildComponent() let dragEvents let dragging = false component.handleMouseDragUntilMouseUp({ - didDrag: (event) => { + didDrag: event => { dragging = true dragEvents.push(event) }, - didStopDragging: () => { dragging = false } + didStopDragging: () => { + dragging = false + } }) expect(dragging).toBe(false) @@ -4282,13 +5888,17 @@ describe('TextEditorComponent', () => { }) it('calls `didStopDragging` if the user interacts with the keyboard while dragging', async () => { - const {component, editor} = buildComponent() + const { component, editor } = buildComponent() let dragging = false function startDragging () { component.handleMouseDragUntilMouseUp({ - didDrag: (event) => { dragging = true }, - didStopDragging: () => { dragging = false } + didDrag: event => { + dragging = true + }, + didStopDragging: () => { + dragging = false + } }) } @@ -4302,7 +5912,7 @@ describe('TextEditorComponent', () => { expect(dragging).toBe(true) // Keyboard interaction prevents users from dragging further. - component.didKeydown({code: 'KeyX'}) + component.didKeydown({ code: 'KeyX' }) expect(dragging).toBe(false) window.dispatchEvent(new MouseEvent('mousemove')) @@ -4314,31 +5924,42 @@ describe('TextEditorComponent', () => { window.dispatchEvent(new MouseEvent('mousemove')) await getNextAnimationFramePromise() expect(dragging).toBe(true) - component.didKeydown({key: 'Control'}) - component.didKeydown({key: 'Alt'}) - component.didKeydown({key: 'Shift'}) - component.didKeydown({key: 'Meta'}) + component.didKeydown({ key: 'Control' }) + component.didKeydown({ key: 'Alt' }) + component.didKeydown({ key: 'Shift' }) + component.didKeydown({ key: 'Meta' }) expect(dragging).toBe(true) }) function getNextAnimationFramePromise () { - return new Promise((resolve) => requestAnimationFrame(resolve)) + return new Promise(resolve => requestAnimationFrame(resolve)) } }) }) function buildEditor (params = {}) { const text = params.text != null ? params.text : SAMPLE_TEXT - const buffer = new TextBuffer({text}) - const editorParams = {buffer, readOnly: params.readOnly} + const buffer = new TextBuffer({ text }) + const editorParams = { buffer, readOnly: params.readOnly } if (params.height != null) params.autoHeight = false - for (const paramName of ['mini', 'autoHeight', 'autoWidth', 'lineNumberGutterVisible', 'showLineNumbers', 'placeholderText', 'softWrapped', 'scrollSensitivity']) { + for (const paramName of [ + 'mini', + 'autoHeight', + 'autoWidth', + 'lineNumberGutterVisible', + 'showLineNumbers', + 'placeholderText', + 'softWrapped', + 'scrollSensitivity' + ]) { if (params[paramName] != null) editorParams[paramName] = params[paramName] } atom.grammars.autoAssignLanguageMode(buffer) const editor = new TextEditor(editorParams) editor.testAutoscrollRequests = [] - editor.onDidRequestAutoscroll((request) => { editor.testAutoscrollRequests.push(request) }) + editor.onDidRequestAutoscroll(request => { + editor.testAutoscrollRequests.push(request) + }) editors.push(editor) return editor } @@ -4352,7 +5973,7 @@ function buildComponent (params = {}) { platform: params.platform, chromeVersion: params.chromeVersion }) - const {element} = component + const { element } = component if (!editor.getAutoHeight()) { element.style.height = params.height ? params.height + 'px' : '600px' } @@ -4360,15 +5981,18 @@ function buildComponent (params = {}) { element.style.width = params.width ? params.width + 'px' : '800px' } if (params.attach !== false) jasmine.attachToDOM(element) - return {component, element, editor} + return { component, element, editor } } function getEditorWidthInBaseCharacters (component) { - return Math.round(component.getScrollContainerWidth() / component.getBaseCharacterWidth()) + return Math.round( + component.getScrollContainerWidth() / component.getBaseCharacterWidth() + ) } -async function setEditorHeightInLines(component, heightInLines) { - component.element.style.height = component.getLineHeight() * heightInLines + 'px' +async function setEditorHeightInLines (component, heightInLines) { + component.element.style.height = + component.getLineHeight() * heightInLines + 'px' await component.getNextUpdatePromise() } @@ -4384,7 +6008,9 @@ async function setEditorWidthInCharacters (component, widthInCharacters) { function verifyCursorPosition (component, cursorNode, row, column) { const rect = cursorNode.getBoundingClientRect() expect(Math.round(rect.top)).toBe(clientTopForLine(component, row)) - expect(Math.round(rect.left)).toBe(Math.round(clientLeftForCharacter(component, row, column))) + expect(Math.round(rect.left)).toBe( + Math.round(clientLeftForCharacter(component, row, column)) + ) } function clientTopForLine (component, row) { @@ -4420,7 +6046,8 @@ function clientPositionForCharacter (component, row, column) { } function lineNumberNodeForScreenRow (component, row) { - const gutterElement = component.refs.gutterContainer.refs.lineNumberGutter.element + const gutterElement = + component.refs.gutterContainer.refs.lineNumberGutter.element const tileStartRow = component.tileStartRowForRow(row) const tileIndex = component.renderedTileStartRows.indexOf(tileStartRow) return gutterElement.children[tileIndex + 1].children[row - tileStartRow] @@ -4428,7 +6055,8 @@ function lineNumberNodeForScreenRow (component, row) { function lineNodeForScreenRow (component, row) { const renderedScreenLine = component.renderedScreenLineForRow(row) - return component.lineComponentsByScreenLineId.get(renderedScreenLine.id).element + return component.lineComponentsByScreenLineId.get(renderedScreenLine.id) + .element } function textNodesForScreenRow (component, row) { @@ -4485,14 +6113,12 @@ function getElementHeight (element) { return height } -function getNextTickPromise () { - return new Promise((resolve) => process.nextTick(resolve)) -} - function queryOnScreenLineNumberElements (element) { return Array.from(element.querySelectorAll('.line-number:not(.dummy)')) } function queryOnScreenLineElements (element) { - return Array.from(element.querySelectorAll('.line:not(.dummy):not([data-off-screen])')) + return Array.from( + element.querySelectorAll('.line:not(.dummy):not([data-off-screen])') + ) } diff --git a/spec/text-editor-element-spec.js b/spec/text-editor-element-spec.js index d6c33e7ad..0d3f23c04 100644 --- a/spec/text-editor-element-spec.js +++ b/spec/text-editor-element-spec.js @@ -1,4 +1,3 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise, timeoutPromise} = require('./async-spec-helpers') const TextEditor = require('../src/text-editor') const TextEditorElement = require('../src/text-editor-element') @@ -9,7 +8,8 @@ describe('TextEditorElement', () => { jasmineContent = document.body.querySelector('#jasmine-content') // Force scrollbars to be visible regardless of local system configuration const scrollbarStyle = document.createElement('style') - scrollbarStyle.textContent = 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }' + scrollbarStyle.textContent = + 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }' jasmine.attachToDOM(scrollbarStyle) }) @@ -53,7 +53,7 @@ describe('TextEditorElement', () => { }) it("only assigns 'placeholder-text' on the model if the attribute is present", () => { - const editor = new TextEditor({placeholderText: 'placeholder'}) + const editor = new TextEditor({ placeholderText: 'placeholder' }) editor.getElement() expect(editor.getPlaceholderText()).toBe('placeholder') }) @@ -70,8 +70,8 @@ describe('TextEditorElement', () => { expect(element.getModel().isLineNumberGutterVisible()).toBe(false) }) - it("honors the 'readonly' attribute", async function() { - jasmineContent.innerHTML = "" + it("honors the 'readonly' attribute", async function () { + jasmineContent.innerHTML = '' const element = jasmineContent.firstChild expect(element.getComponent().isInputEnabled()).toBe(false) @@ -108,11 +108,10 @@ describe('TextEditorElement', () => { describe('when the model is assigned', () => it("adds the 'mini' attribute if .isMini() returns true on the model", async () => { const element = buildTextEditorElement() - element.getModel().update({mini: true}) + element.getModel().update({ mini: true }) await atom.views.getNextUpdatePromise() expect(element.hasAttribute('mini')).toBe(true) - }) - ) + })) describe('when the editor is attached to the DOM', () => it('mounts the component and unmounts when removed from the dom', () => { @@ -125,8 +124,7 @@ describe('TextEditorElement', () => { jasmine.attachToDOM(element) expect(element.component.attached).toBe(true) - }) - ) + })) describe('when the editor is detached from the DOM and then reattached', () => { it('does not render duplicate line numbers', () => { @@ -145,17 +143,23 @@ describe('TextEditorElement', () => { it('does not render duplicate decorations in custom gutters', () => { const editor = new TextEditor() editor.setText('1\n2\n3') - editor.addGutter({name: 'test-gutter'}) + editor.addGutter({ name: 'test-gutter' }) const marker = editor.markBufferRange([[0, 0], [2, 0]]) - editor.decorateMarker(marker, {type: 'gutter', gutterName: 'test-gutter'}) + editor.decorateMarker(marker, { + type: 'gutter', + gutterName: 'test-gutter' + }) const element = editor.getElement() jasmine.attachToDOM(element) - const initialDecorationCount = element.querySelectorAll('.decoration').length + const initialDecorationCount = element.querySelectorAll('.decoration') + .length element.remove() jasmine.attachToDOM(element) - expect(element.querySelectorAll('.decoration').length).toBe(initialDecorationCount) + expect(element.querySelectorAll('.decoration').length).toBe( + initialDecorationCount + ) }) it('can be re-focused using the previous `document.activeElement`', () => { @@ -194,7 +198,9 @@ describe('TextEditorElement', () => { it("doesn't trigger a blur event on the editor element when focusing an already focused editor element", () => { let blurCalled = false const element = buildTextEditorElement() - element.addEventListener('blur', () => { blurCalled = true }) + element.addEventListener('blur', () => { + blurCalled = true + }) jasmineContent.appendChild(element) expect(document.activeElement).toBe(document.body) @@ -216,13 +222,15 @@ describe('TextEditorElement', () => { } } - document.registerElement('element-that-focuses-child', - {prototype: ElementThatFocusesChild.prototype} - ) + document.registerElement('element-that-focuses-child', { + prototype: ElementThatFocusesChild.prototype + }) it('proxies the focus event to the hidden input', () => { const element = buildTextEditorElement() - const parentElement = document.createElement('element-that-focuses-child') + const parentElement = document.createElement( + 'element-that-focuses-child' + ) parentElement.appendChild(element) jasmineContent.appendChild(parentElement) expect(document.activeElement).toBe(element.querySelector('input')) @@ -236,7 +244,7 @@ describe('TextEditorElement', () => { parentElement.style.width = '0px' parentElement.style.height = '0px' - const element = buildTextEditorElement({attach: false}) + const element = buildTextEditorElement({ attach: false }) parentElement.appendChild(element) jasmineContent.appendChild(parentElement) @@ -249,7 +257,7 @@ describe('TextEditorElement', () => { describe('::setModel', () => { describe('when the element does not have an editor yet', () => { it('uses the supplied one', () => { - const element = buildTextEditorElement({attach: false}) + const element = buildTextEditorElement({ attach: false }) const editor = new TextEditor() element.setModel(editor) jasmine.attachToDOM(element) @@ -260,7 +268,7 @@ describe('TextEditorElement', () => { describe('when the element already has an editor', () => { it('unbinds it and then swaps it with the supplied one', async () => { - const element = buildTextEditorElement({attach: true}) + const element = buildTextEditorElement({ attach: true }) const previousEditor = element.getModel() expect(previousEditor.element).toBe(element) @@ -275,7 +283,7 @@ describe('TextEditorElement', () => { describe('::onDidAttach and ::onDidDetach', () => it('invokes callbacks when the element is attached and detached', () => { - const element = buildTextEditorElement({attach: false}) + const element = buildTextEditorElement({ attach: false }) const attachedCallback = jasmine.createSpy('attachedCallback') const detachedCallback = jasmine.createSpy('detachedCallback') @@ -292,8 +300,7 @@ describe('TextEditorElement', () => { expect(attachedCallback).not.toHaveBeenCalled() expect(detachedCallback).toHaveBeenCalled() - }) - ) + })) describe('::setUpdatedSynchronously', () => { it('controls whether the text editor is updated synchronously', () => { @@ -318,7 +325,7 @@ describe('TextEditorElement', () => { describe('::getDefaultCharacterWidth', () => { it('returns 0 before the element is attached', () => { - const element = buildTextEditorElement({attach: false}) + const element = buildTextEditorElement({ attach: false }) expect(element.getDefaultCharacterWidth()).toBe(0) }) @@ -341,7 +348,7 @@ describe('TextEditorElement', () => { const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight() expect(element.getMaxScrollTop()).toBe(0) - await editor.update({autoHeight: false}) + await editor.update({ autoHeight: false }) element.style.height = 100 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() @@ -354,13 +361,12 @@ describe('TextEditorElement', () => { element.style.height = 200 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() expect(element.getMaxScrollTop()).toBe(0) - }) - ) + })) describe('::setScrollTop and ::setScrollLeft', () => { it('changes the scroll position', async () => { - element = buildTextEditorElement() - element.getModel().update({autoHeight: false}) + const element = buildTextEditorElement() + element.getModel().update({ autoHeight: false }) element.getModel().setText('lorem\nipsum\ndolor\nsit\namet') element.setHeight(20) await element.getNextUpdatePromise() @@ -387,8 +393,7 @@ describe('TextEditorElement', () => { element.getModel().setMini(false) await element.getNextUpdatePromise() expect(element.hasAttribute('mini')).toBe(false) - }) - ) + })) describe('::intersectsVisibleRowRange(start, end)', () => { it('returns true if the given row range intersects the visible row range', async () => { @@ -396,7 +401,7 @@ describe('TextEditorElement', () => { const editor = element.getModel() const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight() - editor.update({autoHeight: false}) + editor.update({ autoHeight: false }) element.getModel().setText('x\n'.repeat(20)) element.style.height = 120 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() @@ -419,7 +424,7 @@ describe('TextEditorElement', () => { const editor = element.getModel() const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight() - editor.update({autoHeight: false}) + editor.update({ autoHeight: false }) element.getModel().setText('xxxxxxxxxxxxxxxxxxxxxx\n'.repeat(20)) element.style.height = 120 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() @@ -445,7 +450,7 @@ describe('TextEditorElement', () => { beforeEach(async () => { element = buildTextEditorElement() - element.getModel().update({autoHeight: false}) + element.getModel().update({ autoHeight: false }) element.getModel().setText('lorem\nipsum\ndolor\nsit\namet') element.setHeight(20) await element.getNextUpdatePromise() @@ -456,7 +461,9 @@ describe('TextEditorElement', () => { describe('::onDidChangeScrollTop(callback)', () => it('triggers even when subscribing before attaching the element', () => { const positions = [] - const subscription1 = element.onDidChangeScrollTop(p => positions.push(p)) + const subscription1 = element.onDidChangeScrollTop(p => + positions.push(p) + ) element.onDidChangeScrollTop(p => positions.push(p)) positions.length = 0 @@ -475,13 +482,14 @@ describe('TextEditorElement', () => { positions.length = 0 element.setScrollTop(30) expect(positions).toEqual([30]) - }) - ) + })) describe('::onDidChangeScrollLeft(callback)', () => it('triggers even when subscribing before attaching the element', () => { const positions = [] - const subscription1 = element.onDidChangeScrollLeft(p => positions.push(p)) + const subscription1 = element.onDidChangeScrollLeft(p => + positions.push(p) + ) element.onDidChangeScrollLeft(p => positions.push(p)) positions.length = 0 @@ -500,7 +508,6 @@ describe('TextEditorElement', () => { positions.length = 0 element.setScrollLeft(30) expect(positions).toEqual([30]) - }) - ) + })) }) }) diff --git a/spec/text-editor-registry-spec.js b/spec/text-editor-registry-spec.js index 4c6d680eb..4175b863b 100644 --- a/spec/text-editor-registry-spec.js +++ b/spec/text-editor-registry-spec.js @@ -1,8 +1,7 @@ const TextEditorRegistry = require('../src/text-editor-registry') const TextEditor = require('../src/text-editor') const TextBuffer = require('text-buffer') -const {Point, Range} = TextBuffer -const {it, fit, ffit, fffit} = require('./async-spec-helpers') +const { Point, Range } = TextBuffer const dedent = require('dedent') describe('TextEditorRegistry', function () { @@ -15,11 +14,13 @@ describe('TextEditorRegistry', function () { assert: atom.assert, config: atom.config, grammarRegistry: atom.grammars, - packageManager: {deferredActivationHooks: null} + packageManager: { deferredActivationHooks: null } }) - editor = new TextEditor({autoHeight: false}) - expect(atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar')).toBe(true) + editor = new TextEditor({ autoHeight: false }) + expect( + atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar') + ).toBe(true) }) afterEach(function () { @@ -68,9 +69,11 @@ describe('TextEditorRegistry', function () { await atom.packages.activatePackage('language-javascript') await atom.packages.activatePackage('language-c') - atom.config.set('editor.tabLength', 8, {scope: '.source.js'}) + atom.config.set('editor.tabLength', 8, { scope: '.source.js' }) - const editor = registry.build({buffer: new TextBuffer({filePath: 'test.js'})}) + const editor = registry.build({ + buffer: new TextBuffer({ filePath: 'test.js' }) + }) expect(editor.getTabLength()).toBe(8) }) }) @@ -87,14 +90,22 @@ describe('TextEditorRegistry', function () { registry.maintainConfig(editor2) await initialPackageActivation - expect(editor.getRootScopeDescriptor().getScopesArray()).toEqual(['text.plain.null-grammar']) - expect(editor2.getRootScopeDescriptor().getScopesArray()).toEqual(['source.js']) + expect(editor.getRootScopeDescriptor().getScopesArray()).toEqual([ + 'text.plain.null-grammar' + ]) + expect(editor2.getRootScopeDescriptor().getScopesArray()).toEqual([ + 'source.js' + ]) expect(editor.getEncoding()).toBe('utf8') expect(editor2.getEncoding()).toBe('utf8') - atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.text.plain.null-grammar'}) - atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.source.js'}) + atom.config.set('core.fileEncoding', 'utf16le', { + scopeSelector: '.text.plain.null-grammar' + }) + atom.config.set('core.fileEncoding', 'utf16be', { + scopeSelector: '.source.js' + }) expect(editor.getEncoding()).toBe('utf16le') expect(editor2.getEncoding()).toBe('utf16be') @@ -131,23 +142,29 @@ describe('TextEditorRegistry', function () { expect(editor.getEncoding()).toBe('utf16be') }) - it('updates the editor\'s settings when its grammar changes', async function () { + it("updates the editor's settings when its grammar changes", async function () { await atom.packages.activatePackage('language-javascript') registry.maintainConfig(editor) await initialPackageActivation - atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.source.js'}) + atom.config.set('core.fileEncoding', 'utf16be', { + scopeSelector: '.source.js' + }) expect(editor.getEncoding()).toBe('utf8') - atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.source.js'}) + atom.config.set('core.fileEncoding', 'utf16le', { + scopeSelector: '.source.js' + }) expect(editor.getEncoding()).toBe('utf8') atom.grammars.assignLanguageMode(editor, 'source.js') await initialPackageActivation expect(editor.getEncoding()).toBe('utf16le') - atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.source.js'}) + atom.config.set('core.fileEncoding', 'utf16be', { + scopeSelector: '.source.js' + }) expect(editor.getEncoding()).toBe('utf16be') atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar') @@ -155,7 +172,7 @@ describe('TextEditorRegistry', function () { expect(editor.getEncoding()).toBe('utf8') }) - it('preserves editor settings that haven\'t changed between previous and current language modes', async function () { + it("preserves editor settings that haven't changed between previous and current language modes", async function () { await atom.packages.activatePackage('language-javascript') registry.maintainConfig(editor) @@ -182,8 +199,12 @@ describe('TextEditorRegistry', function () { await initialPackageActivation expect(editor.getEncoding()).toBe('utf8') - atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.text.plain.null-grammar'}) - atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.source.js'}) + atom.config.set('core.fileEncoding', 'utf16be', { + scopeSelector: '.text.plain.null-grammar' + }) + atom.config.set('core.fileEncoding', 'utf16le', { + scopeSelector: '.source.js' + }) expect(editor.getEncoding()).toBe('utf16be') editor.setEncoding('utf8') @@ -194,13 +215,15 @@ describe('TextEditorRegistry', function () { expect(editor.getEncoding()).toBe('utf16le') }) - it('returns a disposable that can be used to stop the registry from updating the editor\'s config', async function () { + it("returns a disposable that can be used to stop the registry from updating the editor's config", async function () { await atom.packages.activatePackage('language-javascript') const previousSubscriptionCount = getSubscriptionCount(editor) const disposable = registry.maintainConfig(editor) await initialPackageActivation - expect(getSubscriptionCount(editor)).toBeGreaterThan(previousSubscriptionCount) + expect(getSubscriptionCount(editor)).toBeGreaterThan( + previousSubscriptionCount + ) expect(registry.editorsWithMaintainedConfig.size).toBe(1) atom.config.set('core.fileEncoding', 'utf16be') @@ -217,7 +240,7 @@ describe('TextEditorRegistry', function () { }) it('sets the encoding based on the config', async function () { - editor.update({encoding: 'utf8'}) + editor.update({ encoding: 'utf8' }) expect(editor.getEncoding()).toBe('utf8') atom.config.set('core.fileEncoding', 'utf16le') @@ -230,7 +253,7 @@ describe('TextEditorRegistry', function () { }) it('sets the tab length based on the config', async function () { - editor.update({tabLength: 4}) + editor.update({ tabLength: 4 }) expect(editor.getTabLength()).toBe(4) atom.config.set('editor.tabLength', 8) @@ -257,13 +280,12 @@ describe('TextEditorRegistry', function () { }) describe('when the "tabType" config setting is "auto"', function () { - it('enables or disables soft tabs based on the editor\'s content', async function () { + it("enables or disables soft tabs based on the editor's content", async function () { await initialPackageActivation await atom.packages.activatePackage('language-javascript') atom.grammars.assignLanguageMode(editor, 'source.js') atom.config.set('editor.tabType', 'auto') await initialPackageActivation - const languageMode = editor.getBuffer().getLanguageMode() editor.setText(dedent` { @@ -273,24 +295,30 @@ describe('TextEditorRegistry', function () { let disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(true) + /* eslint-disable no-tabs */ editor.setText(dedent` { hello; } `) + /* eslint-enable no-tabs */ disposable.dispose() disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(false) - editor.setTextInBufferRange(new Range(Point.ZERO, Point.ZERO), dedent` + editor.setTextInBufferRange( + new Range(Point.ZERO, Point.ZERO), + dedent` /* * Comment with a leading space. */ - ` + '\n') + ` + '\n' + ) disposable.dispose() disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(false) + /* eslint-disable no-tabs */ editor.setText(dedent` /* * Comment with a leading space. @@ -300,6 +328,7 @@ describe('TextEditorRegistry', function () { hello; } `) + /* eslint-enable no-tabs */ disposable.dispose() disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(false) @@ -335,7 +364,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables soft tabs based on the config', async function () { - editor.update({softTabs: true}) + editor.update({ softTabs: true }) expect(editor.getSoftTabs()).toBe(true) atom.config.set('editor.tabType', 'hard') @@ -352,7 +381,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables atomic soft tabs based on the config', async function () { - editor.update({atomicSoftTabs: true}) + editor.update({ atomicSoftTabs: true }) expect(editor.hasAtomicSoftTabs()).toBe(true) atom.config.set('editor.atomicSoftTabs', false) @@ -365,7 +394,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables cursor on selection visibility based on the config', async function () { - editor.update({showCursorOnSelection: true}) + editor.update({ showCursorOnSelection: true }) expect(editor.getShowCursorOnSelection()).toBe(true) atom.config.set('editor.showCursorOnSelection', false) @@ -378,7 +407,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables line numbers based on the config', async function () { - editor.update({showLineNumbers: true}) + editor.update({ showLineNumbers: true }) expect(editor.showLineNumbers).toBe(true) atom.config.set('editor.showLineNumbers', false) @@ -391,8 +420,8 @@ describe('TextEditorRegistry', function () { }) it('sets the invisibles based on the config', async function () { - const invisibles1 = {'tab': 'a', 'cr': false, eol: false, space: false} - const invisibles2 = {'tab': 'b', 'cr': false, eol: false, space: false} + const invisibles1 = { tab: 'a', cr: false, eol: false, space: false } + const invisibles2 = { tab: 'b', cr: false, eol: false, space: false } editor.update({ showInvisibles: true, @@ -414,7 +443,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables the indent guide based on the config', async function () { - editor.update({showIndentGuide: true}) + editor.update({ showIndentGuide: true }) expect(editor.doesShowIndentGuide()).toBe(true) atom.config.set('editor.showIndentGuide', false) @@ -427,7 +456,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables soft wrap based on the config', async function () { - editor.update({softWrapped: true}) + editor.update({ softWrapped: true }) expect(editor.isSoftWrapped()).toBe(true) atom.config.set('editor.softWrap', false) @@ -440,7 +469,7 @@ describe('TextEditorRegistry', function () { }) it('sets the soft wrap indent length based on the config', async function () { - editor.update({softWrapHangingIndentLength: 4}) + editor.update({ softWrapHangingIndentLength: 4 }) expect(editor.getSoftWrapHangingIndentLength()).toBe(4) atom.config.set('editor.softWrapHangingIndent', 2) @@ -457,7 +486,7 @@ describe('TextEditorRegistry', function () { softWrapped: true, preferredLineLength: 80, editorWidthInChars: 120, - softWrapAtPreferredLineLength: true, + softWrapAtPreferredLineLength: true }) expect(editor.getSoftWrapColumn()).toBe(80) @@ -475,7 +504,7 @@ describe('TextEditorRegistry', function () { it('allows for custom definition of maximum soft wrap based on config', async function () { editor.update({ softWrapped: false, - maxScreenLineLength: 1500, + maxScreenLineLength: 1500 }) expect(editor.getSoftWrapColumn()).toBe(1500) @@ -488,7 +517,7 @@ describe('TextEditorRegistry', function () { }) it('sets the preferred line length based on the config', async function () { - editor.update({preferredLineLength: 80}) + editor.update({ preferredLineLength: 80 }) expect(editor.getPreferredLineLength()).toBe(80) atom.config.set('editor.preferredLineLength', 110) @@ -501,7 +530,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables auto-indent based on the config', async function () { - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) expect(editor.shouldAutoIndent()).toBe(true) atom.config.set('editor.autoIndent', false) @@ -514,7 +543,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables auto-indent-on-paste based on the config', async function () { - editor.update({autoIndentOnPaste: true}) + editor.update({ autoIndentOnPaste: true }) expect(editor.shouldAutoIndentOnPaste()).toBe(true) atom.config.set('editor.autoIndentOnPaste', false) @@ -527,7 +556,7 @@ describe('TextEditorRegistry', function () { }) it('enables or disables scrolling past the end of the buffer based on the config', async function () { - editor.update({scrollPastEnd: true}) + editor.update({ scrollPastEnd: true }) expect(editor.getScrollPastEnd()).toBe(true) atom.config.set('editor.scrollPastEnd', false) @@ -540,7 +569,7 @@ describe('TextEditorRegistry', function () { }) it('sets the undo grouping interval based on the config', async function () { - editor.update({undoGroupingInterval: 300}) + editor.update({ undoGroupingInterval: 300 }) expect(editor.getUndoGroupingInterval()).toBe(300) atom.config.set('editor.undoGroupingInterval', 600) @@ -553,7 +582,7 @@ describe('TextEditorRegistry', function () { }) it('sets the scroll sensitivity based on the config', async function () { - editor.update({scrollSensitivity: 50}) + editor.update({ scrollSensitivity: 50 }) expect(editor.getScrollSensitivity()).toBe(50) atom.config.set('editor.scrollSensitivity', 60) @@ -567,7 +596,7 @@ describe('TextEditorRegistry', function () { describe('when called twice with a given editor', function () { it('does nothing the second time', async function () { - editor.update({scrollSensitivity: 50}) + editor.update({ scrollSensitivity: 50 }) const disposable1 = registry.maintainConfig(editor) const disposable2 = registry.maintainConfig(editor) @@ -589,10 +618,12 @@ describe('TextEditorRegistry', function () { }) function getSubscriptionCount (editor) { - return editor.emitter.getTotalListenerCount() + + return ( + editor.emitter.getTotalListenerCount() + editor.tokenizedBuffer.emitter.getTotalListenerCount() + editor.buffer.emitter.getTotalListenerCount() + editor.displayLayer.emitter.getTotalListenerCount() + ) } function retainedEditorCount (registry) { diff --git a/spec/text-editor-spec.js b/spec/text-editor-spec.js index 908ea8082..4f4390561 100644 --- a/spec/text-editor-spec.js +++ b/spec/text-editor-spec.js @@ -1,10 +1,8 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise, timeoutPromise} = require('./async-spec-helpers') - const fs = require('fs') const path = require('path') const temp = require('temp').track() const dedent = require('dedent') -const {clipboard} = require('electron') +const { clipboard } = require('electron') const TextEditor = require('../src/text-editor') const TextBuffer = require('text-buffer') const TextMateLanguageMode = require('../src/text-mate-language-mode') @@ -16,7 +14,7 @@ describe('TextEditor', () => { beforeEach(async () => { editor = await atom.workspace.open('sample.js') buffer = editor.buffer - editor.update({autoIndent: false}) + editor.update({ autoIndent: false }) lineLengths = buffer.getLines().map(line => line.length) await atom.packages.activatePackage('language-javascript') }) @@ -24,8 +22,8 @@ describe('TextEditor', () => { it('generates unique ids for each editor', async () => { // Deserialized editors are initialized with the serialized id. We can // initialize an editor with what we expect to be the next id: - const deserialized = new TextEditor({id: editor.id+1}) - expect(deserialized.id).toEqual(editor.id+1) + const deserialized = new TextEditor({ id: editor.id + 1 }) + expect(deserialized.id).toEqual(editor.id + 1) // The id generator should skip the id used up by the deserialized one: const fresh = new TextEditor() @@ -35,7 +33,7 @@ describe('TextEditor', () => { describe('when the editor is deserialized', () => { it('restores selections and folds based on markers in the buffer', async () => { editor.setSelectedBufferRange([[1, 2], [3, 4]]) - editor.addSelectionForBufferRange([[5, 6], [7, 5]], {reversed: true}) + editor.addSelectionForBufferRange([[5, 6], [7, 5]], { reversed: true }) editor.foldBufferRow(4) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -43,12 +41,19 @@ describe('TextEditor', () => { const editor2 = TextEditor.deserialize(editor.serialize(), { assert: atom.assert, textEditors: atom.textEditors, - project: {bufferForIdSync () { return buffer2 }} + project: { + bufferForIdSync () { + return buffer2 + } + } }) expect(editor2.id).toBe(editor.id) expect(editor2.getBuffer().getPath()).toBe(editor.getBuffer().getPath()) - expect(editor2.getSelectedBufferRanges()).toEqual([[[1, 2], [3, 4]], [[5, 6], [7, 5]]]) + expect(editor2.getSelectedBufferRanges()).toEqual([ + [[1, 2], [3, 4]], + [[5, 6], [7, 5]] + ]) expect(editor2.getSelections()[1].isReversed()).toBeTruthy() expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy() editor2.destroy() @@ -62,7 +67,7 @@ describe('TextEditor', () => { softWrapped: true, softWrapAtPreferredLineLength: true, softWrapHangingIndentLength: 8, - invisibles: {space: 'S'}, + invisibles: { space: 'S' }, showInvisibles: true, editorWidthInChars: 120 }) @@ -73,25 +78,39 @@ describe('TextEditor', () => { const editor2 = TextEditor.deserialize(editor.serialize(), { assert: atom.assert, textEditors: atom.textEditors, - project: {bufferForIdSync () { return buffer2 }} + project: { + bufferForIdSync () { + return buffer2 + } + } }) expect(editor2.getSoftTabs()).toBe(editor.getSoftTabs()) expect(editor2.hasAtomicSoftTabs()).toBe(editor.hasAtomicSoftTabs()) expect(editor2.getTabLength()).toBe(editor.getTabLength()) expect(editor2.getSoftWrapColumn()).toBe(editor.getSoftWrapColumn()) - expect(editor2.getSoftWrapHangingIndentLength()).toBe(editor.getSoftWrapHangingIndentLength()) + expect(editor2.getSoftWrapHangingIndentLength()).toBe( + editor.getSoftWrapHangingIndentLength() + ) expect(editor2.getInvisibles()).toEqual(editor.getInvisibles()) - expect(editor2.getEditorWidthInChars()).toBe(editor.getEditorWidthInChars()) + expect(editor2.getEditorWidthInChars()).toBe( + editor.getEditorWidthInChars() + ) expect(editor2.displayLayer.tabLength).toBe(editor2.getTabLength()) - expect(editor2.displayLayer.softWrapColumn).toBe(editor2.getSoftWrapColumn()) + expect(editor2.displayLayer.softWrapColumn).toBe( + editor2.getSoftWrapColumn() + ) }) it('ignores buffers with retired IDs', () => { const editor2 = TextEditor.deserialize(editor.serialize(), { assert: atom.assert, textEditors: atom.textEditors, - project: {bufferForIdSync () { return null }} + project: { + bufferForIdSync () { + return null + } + } }) expect(editor2).toBeNull() @@ -109,9 +128,9 @@ describe('TextEditor', () => { element.setWidth(100) jasmine.attachToDOM(element) - editor.update({showCursorOnSelection: false}) + editor.update({ showCursorOnSelection: false }) editor.setSelectedBufferRange([[1, 2], [3, 4]]) - editor.addSelectionForBufferRange([[5, 6], [7, 8]], {reversed: true}) + editor.addSelectionForBufferRange([[5, 6], [7, 8]], { reversed: true }) editor.setScrollTopRow(3) expect(editor.getScrollTopRow()).toBe(3) editor.setScrollLeftColumn(4) @@ -125,7 +144,9 @@ describe('TextEditor', () => { element2.setWidth(100) jasmine.attachToDOM(element2) expect(editor2.id).not.toBe(editor.id) - expect(editor2.getSelectedBufferRanges()).toEqual(editor.getSelectedBufferRanges()) + expect(editor2.getSelectedBufferRanges()).toEqual( + editor.getSelectedBufferRanges() + ) expect(editor2.getSelections()[1].isReversed()).toBeTruthy() expect(editor2.getScrollTopRow()).toBe(3) expect(editor2.getScrollLeftColumn()).toBe(4) @@ -136,9 +157,13 @@ describe('TextEditor', () => { // editor2 can now diverge from its origin edit session editor2.getLastSelection().setBufferRange([[2, 1], [4, 3]]) - expect(editor2.getSelectedBufferRanges()).not.toEqual(editor.getSelectedBufferRanges()) + expect(editor2.getSelectedBufferRanges()).not.toEqual( + editor.getSelectedBufferRanges() + ) editor2.unfoldBufferRow(4) - expect(editor2.isFoldedAtBufferRow(4)).not.toBe(editor.isFoldedAtBufferRow(4)) + expect(editor2.isFoldedAtBufferRow(4)).not.toBe( + editor.isFoldedAtBufferRow(4) + ) }) }) @@ -147,8 +172,8 @@ describe('TextEditor', () => { let changeSpy const { element } = editor // force element initialization element.setUpdatedSynchronously(false) - editor.update({showInvisibles: true}) - editor.onDidChange(changeSpy = jasmine.createSpy('onDidChange')) + editor.update({ showInvisibles: true }) + editor.onDidChange((changeSpy = jasmine.createSpy('onDidChange'))) const returnedPromise = editor.update({ tabLength: 6, @@ -194,8 +219,12 @@ describe('TextEditor', () => { }) it("returns ' — ' when opened files have identical file names", async () => { - const editor1 = await atom.workspace.open(path.join('sample-theme-1', 'readme')) - const editor2 = await atom.workspace.open(path.join('sample-theme-2', 'readme')) + const editor1 = await atom.workspace.open( + path.join('sample-theme-1', 'readme') + ) + const editor2 = await atom.workspace.open( + path.join('sample-theme-2', 'readme') + ) expect(editor1.getLongTitle()).toBe('readme \u2014 sample-theme-1') expect(editor2.getLongTitle()).toBe('readme \u2014 sample-theme-2') }) @@ -210,10 +239,16 @@ describe('TextEditor', () => { }) it("returns ' — ' when opened files have identical file and same parent dir name", async () => { - const editor1 = await atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'main.js')) - const editor2 = await atom.workspace.open(path.join('sample-theme-2', 'src', 'js', 'plugin', 'main.js')) + const editor1 = await atom.workspace.open( + path.join('sample-theme-2', 'src', 'js', 'main.js') + ) + const editor2 = await atom.workspace.open( + path.join('sample-theme-2', 'src', 'js', 'plugin', 'main.js') + ) expect(editor1.getLongTitle()).toBe('main.js \u2014 js') - expect(editor2.getLongTitle()).toBe(`main.js \u2014 ${path.join('js', 'plugin')}`) + expect(editor2.getLongTitle()).toBe( + `main.js \u2014 ${path.join('js', 'plugin')}` + ) }) it('returns the filename when the editor is not in the workspace', async () => { @@ -298,7 +333,9 @@ describe('TextEditor', () => { it('emits an event with the old position, new position, and the cursor that moved', () => { const cursorCallback = jasmine.createSpy('cursor-changed-position') - const editorCallback = jasmine.createSpy('editor-changed-cursor-position') + const editorCallback = jasmine.createSpy( + 'editor-changed-cursor-position' + ) editor.getLastCursor().onDidChangePosition(cursorCallback) editor.onDidChangeCursorPosition(editorCallback) @@ -321,7 +358,7 @@ describe('TextEditor', () => { describe('.setCursorScreenPosition(screenPosition)', () => { it('clears a goal column established by vertical movement', () => { // set a goal column by moving down - editor.setCursorScreenPosition({row: 3, column: lineLengths[3]}) + editor.setCursorScreenPosition({ row: 3, column: lineLengths[3] }) editor.moveDown() expect(editor.getCursorScreenPosition().column).not.toBe(6) @@ -336,7 +373,7 @@ describe('TextEditor', () => { it('merges multiple cursors', () => { editor.setCursorScreenPosition([0, 0]) editor.addCursorAtScreenPosition([0, 1]) - const [cursor1, cursor2] = editor.getCursors() + const [cursor1] = editor.getCursors() editor.setCursorScreenPosition([4, 7]) expect(editor.getCursors().length).toBe(1) expect(editor.getCursors()).toEqual([cursor1]) @@ -367,7 +404,7 @@ describe('TextEditor', () => { it('retains the goal column across lines of differing length', () => { expect(lineLengths[6]).toBeGreaterThan(32) - editor.setCursorScreenPosition({row: 6, column: 32}) + editor.setCursorScreenPosition({ row: 6, column: 32 }) editor.moveUp() expect(editor.getCursorScreenPosition().column).toBe(lineLengths[5]) @@ -402,7 +439,7 @@ describe('TextEditor', () => { it('merges cursors when they overlap', () => { editor.addCursorAtScreenPosition([1, 0]) - const [cursor1, cursor2] = editor.getCursors() + const [cursor1] = editor.getCursors() editor.moveUp() expect(editor.getCursors()).toEqual([cursor1]) @@ -432,7 +469,7 @@ describe('TextEditor', () => { }) it('retains the goal column across lines of differing length', () => { - editor.setCursorScreenPosition({row: 3, column: lineLengths[3]}) + editor.setCursorScreenPosition({ row: 3, column: lineLengths[3] }) editor.moveDown() expect(editor.getCursorScreenPosition().column).toBe(lineLengths[4]) @@ -450,12 +487,20 @@ describe('TextEditor', () => { const lastLine = buffer.lineForRow(lastLineIndex) expect(lastLine.length).toBeGreaterThan(0) - editor.setCursorScreenPosition({row: lastLineIndex, column: editor.getTabLength()}) + editor.setCursorScreenPosition({ + row: lastLineIndex, + column: editor.getTabLength() + }) editor.moveDown() - expect(editor.getCursorScreenPosition()).toEqual({row: lastLineIndex, column: lastLine.length}) + expect(editor.getCursorScreenPosition()).toEqual({ + row: lastLineIndex, + column: lastLine.length + }) editor.moveUp() - expect(editor.getCursorScreenPosition().column).toBe(editor.getTabLength()) + expect(editor.getCursorScreenPosition().column).toBe( + editor.getTabLength() + ) }) it('retains a goal column of 0 when moving back up', () => { @@ -463,7 +508,7 @@ describe('TextEditor', () => { const lastLine = buffer.lineForRow(lastLineIndex) expect(lastLine.length).toBeGreaterThan(0) - editor.setCursorScreenPosition({row: lastLineIndex, column: 0}) + editor.setCursorScreenPosition({ row: lastLineIndex, column: 0 }) editor.moveDown() editor.moveUp() expect(editor.getCursorScreenPosition().column).toBe(0) @@ -495,7 +540,7 @@ describe('TextEditor', () => { it('merges cursors when they overlap', () => { editor.setCursorScreenPosition([12, 2]) editor.addCursorAtScreenPosition([11, 2]) - const [cursor1, cursor2] = editor.getCursors() + const [cursor1] = editor.getCursors() editor.moveDown() expect(editor.getCursors()).toEqual([cursor1]) @@ -531,9 +576,12 @@ describe('TextEditor', () => { describe('when the cursor is in the first column', () => { describe('when there is a previous line', () => { it('wraps to the end of the previous line', () => { - editor.setCursorScreenPosition({row: 1, column: 0}) + editor.setCursorScreenPosition({ row: 1, column: 0 }) editor.moveLeft() - expect(editor.getCursorScreenPosition()).toEqual({row: 0, column: buffer.lineForRow(0).length}) + expect(editor.getCursorScreenPosition()).toEqual({ + row: 0, + column: buffer.lineForRow(0).length + }) }) it('moves the cursor by one row up and n columns to the left', () => { @@ -567,9 +615,12 @@ describe('TextEditor', () => { describe('when the cursor is on the first line', () => { it('remains in the same position (0,0)', () => { - editor.setCursorScreenPosition({row: 0, column: 0}) + editor.setCursorScreenPosition({ row: 0, column: 0 }) editor.moveLeft() - expect(editor.getCursorScreenPosition()).toEqual({row: 0, column: 0}) + expect(editor.getCursorScreenPosition()).toEqual({ + row: 0, + column: 0 + }) }) it('remains in the same position (0,0) when columnCount is specified', () => { @@ -606,7 +657,7 @@ describe('TextEditor', () => { editor.setCursorScreenPosition([0, 0]) editor.addCursorAtScreenPosition([0, 1]) - const [cursor1, cursor2] = editor.getCursors() + const [cursor1] = editor.getCursors() editor.moveLeft() expect(editor.getCursors()).toEqual([cursor1]) expect(cursor1.getBufferPosition()).toEqual([0, 0]) @@ -667,7 +718,7 @@ describe('TextEditor', () => { const lastLine = buffer.lineForRow(lastLineIndex) expect(lastLine.length).toBeGreaterThan(0) - const lastPosition = {row: lastLineIndex, column: lastLine.length} + const lastPosition = { row: lastLineIndex, column: lastLine.length } editor.setCursorScreenPosition(lastPosition) editor.moveRight() @@ -692,7 +743,7 @@ describe('TextEditor', () => { it('merges cursors when they overlap', () => { editor.setCursorScreenPosition([12, 2]) editor.addCursorAtScreenPosition([12, 1]) - const [cursor1, cursor2] = editor.getCursors() + const [cursor1] = editor.getCursors() editor.moveRight() expect(editor.getCursors()).toEqual([cursor1]) @@ -840,7 +891,7 @@ describe('TextEditor', () => { describe('when invisible characters are enabled with soft tabs', () => { it('moves to the first character of the current line without being confused by the invisible characters', () => { - editor.update({showInvisibles: true}) + editor.update({ showInvisibles: true }) editor.setCursorScreenPosition([1, 7]) editor.moveToFirstCharacterOfLine() expect(editor.getCursorBufferPosition()).toEqual([1, 2]) @@ -851,8 +902,10 @@ describe('TextEditor', () => { describe('when invisible characters are enabled with hard tabs', () => { it('moves to the first character of the current line without being confused by the invisible characters', () => { - editor.update({showInvisibles: true}) - buffer.setTextInRange([[1, 0], [1, Infinity]], '\t\t\ta', {normalizeLineEndings: false}) + editor.update({ showInvisibles: true }) + buffer.setTextInRange([[1, 0], [1, Infinity]], '\t\t\ta', { + normalizeLineEndings: false + }) editor.setCursorScreenPosition([1, 7]) editor.moveToFirstCharacterOfLine() @@ -863,7 +916,7 @@ describe('TextEditor', () => { }) }) - it("clears the goal column", () => { + it('clears the goal column', () => { editor.setText('first\n\nthird') editor.setCursorScreenPosition([0, 3]) editor.moveDown() @@ -1332,7 +1385,9 @@ describe('TextEditor', () => { describe('.getCurrentParagraphBufferRange()', () => { it('returns the buffer range of the current paragraph, delimited by blank lines or the beginning / end of the file', () => { - buffer.setText(' ' + dedent` + buffer.setText( + ' ' + + dedent` I am the first paragraph, bordered by the beginning of the file @@ -1344,17 +1399,27 @@ describe('TextEditor', () => { I am the last paragraph, bordered by the end of the file.\ - `) + ` + ) // in a paragraph editor.setCursorBufferPosition([1, 7]) - expect(editor.getCurrentParagraphBufferRange()).toEqual([[0, 0], [2, 8]]) + expect(editor.getCurrentParagraphBufferRange()).toEqual([ + [0, 0], + [2, 8] + ]) editor.setCursorBufferPosition([7, 1]) - expect(editor.getCurrentParagraphBufferRange()).toEqual([[5, 0], [7, 3]]) + expect(editor.getCurrentParagraphBufferRange()).toEqual([ + [5, 0], + [7, 3] + ]) editor.setCursorBufferPosition([9, 10]) - expect(editor.getCurrentParagraphBufferRange()).toEqual([[9, 0], [10, 32]]) + expect(editor.getCurrentParagraphBufferRange()).toEqual([ + [9, 0], + [10, 32] + ]) // between paragraphs editor.setCursorBufferPosition([3, 1]) @@ -1415,7 +1480,9 @@ describe('TextEditor', () => { describe('getCursorAtScreenPosition(screenPosition)', () => { it('returns the cursor at the given screenPosition', () => { const cursor1 = editor.addCursorAtScreenPosition([0, 2]) - const cursor2 = editor.getCursorAtScreenPosition(cursor1.getScreenPosition()) + const cursor2 = editor.getCursorAtScreenPosition( + cursor1.getScreenPosition() + ) expect(cursor2).toBe(cursor1) }) }) @@ -1423,9 +1490,14 @@ describe('TextEditor', () => { describe('::getCursorScreenPositions()', () => { it('returns the cursor positions in the order they were added', () => { editor.foldBufferRow(4) - const cursor1 = editor.addCursorAtBufferPosition([8, 5]) - const cursor2 = editor.addCursorAtBufferPosition([3, 5]) - expect(editor.getCursorScreenPositions()).toEqual([[0, 0], [5, 5], [3, 5]]) + editor.addCursorAtBufferPosition([8, 5]) + editor.addCursorAtBufferPosition([3, 5]) + + expect(editor.getCursorScreenPositions()).toEqual([ + [0, 0], + [5, 5], + [3, 5] + ]) }) }) @@ -1434,7 +1506,11 @@ describe('TextEditor', () => { const originalCursor = editor.getLastCursor() const cursor1 = editor.addCursorAtBufferPosition([8, 5]) const cursor2 = editor.addCursorAtBufferPosition([4, 5]) - expect(editor.getCursorsOrderedByBufferPosition()).toEqual([originalCursor, cursor2, cursor1]) + expect(editor.getCursorsOrderedByBufferPosition()).toEqual([ + originalCursor, + cursor2, + cursor1 + ]) }) }) @@ -1476,7 +1552,10 @@ describe('TextEditor', () => { describe('.getLastSelection()', () => { it('creates a new selection at (0, 0) if the last selection has been destroyed', () => { editor.getLastSelection().destroy() - expect(editor.getLastSelection().getBufferRange()).toEqual([[0, 0], [0, 0]]) + expect(editor.getLastSelection().getBufferRange()).toEqual([ + [0, 0], + [0, 0] + ]) }) it("doesn't get stuck in a infinite loop when called from ::onDidAddCursor after the last selection has been destroyed (regression)", () => { @@ -1486,7 +1565,10 @@ describe('TextEditor', () => { callCount++ editor.getLastSelection() }) - expect(editor.getLastSelection().getBufferRange()).toEqual([[0, 0], [0, 0]]) + expect(editor.getLastSelection().getBufferRange()).toEqual([ + [0, 0], + [0, 0] + ]) expect(callCount).toBe(1) }) }) @@ -1494,7 +1576,10 @@ describe('TextEditor', () => { describe('.getSelections()', () => { it('creates a new selection at (0, 0) if the last selection has been destroyed', () => { editor.getLastSelection().destroy() - expect(editor.getSelections()[0].getBufferRange()).toEqual([[0, 0], [0, 0]]) + expect(editor.getSelections()[0].getBufferRange()).toEqual([ + [0, 0], + [0, 0] + ]) }) }) @@ -1503,7 +1588,9 @@ describe('TextEditor', () => { let rangeChangedHandler editor.setSelectedBufferRange([[3, 0], [4, 5]]) - editor.onDidChangeSelectionRange(rangeChangedHandler = jasmine.createSpy()) + editor.onDidChangeSelectionRange( + (rangeChangedHandler = jasmine.createSpy()) + ) editor.selectToBufferPosition([6, 2]) expect(rangeChangedHandler).toHaveBeenCalled() @@ -1541,8 +1628,12 @@ describe('TextEditor', () => { }) it('merges selections when they intersect when moving down', () => { - editor.setSelectedBufferRanges([[[0, 9], [0, 13]], [[1, 10], [1, 20]], [[2, 15], [3, 25]]]) - const [selection1, selection2, selection3] = editor.getSelections() + editor.setSelectedBufferRanges([ + [[0, 9], [0, 13]], + [[1, 10], [1, 20]], + [[2, 15], [3, 25]] + ]) + const [selection1] = editor.getSelections() editor.selectDown() expect(editor.getSelections()).toEqual([selection1]) @@ -1551,8 +1642,11 @@ describe('TextEditor', () => { }) it('merges selections when they intersect when moving up', () => { - editor.setSelectedBufferRanges([[[0, 9], [0, 13]], [[1, 10], [1, 20]]], {reversed: true}) - const [selection1, selection2] = editor.getSelections() + editor.setSelectedBufferRanges( + [[[0, 9], [0, 13]], [[1, 10], [1, 20]]], + { reversed: true } + ) + const [selection1] = editor.getSelections() editor.selectUp() expect(editor.getSelections().length).toBe(1) @@ -1562,8 +1656,11 @@ describe('TextEditor', () => { }) it('merges selections when they intersect when moving left', () => { - editor.setSelectedBufferRanges([[[0, 9], [0, 13]], [[0, 13], [1, 20]]], {reversed: true}) - const [selection1, selection2] = editor.getSelections() + editor.setSelectedBufferRanges( + [[[0, 9], [0, 13]], [[0, 13], [1, 20]]], + { reversed: true } + ) + const [selection1] = editor.getSelections() editor.selectLeft() expect(editor.getSelections()).toEqual([selection1]) @@ -1573,7 +1670,7 @@ describe('TextEditor', () => { it('merges selections when they intersect when moving right', () => { editor.setSelectedBufferRanges([[[0, 9], [0, 14]], [[0, 14], [1, 20]]]) - const [selection1, selection2] = editor.getSelections() + const [selection1] = editor.getSelections() editor.selectRight() expect(editor.getSelections()).toEqual([selection1]) @@ -1583,7 +1680,10 @@ describe('TextEditor', () => { describe('when counts are passed into the selection functions', () => { it("expands each selection to its cursor's new location", () => { - editor.setSelectedBufferRanges([[[0, 9], [0, 13]], [[3, 16], [3, 21]]]) + editor.setSelectedBufferRanges([ + [[0, 9], [0, 13]], + [[3, 16], [3, 21]] + ]) const [selection1, selection2] = editor.getSelections() editor.selectRight(2) @@ -1687,8 +1787,8 @@ describe('TextEditor', () => { editor.selectToScreenPosition([4, 11]) selections = editor.getSelections() - expect(selections.length).toBe(1); - [selection1] = selections + expect(selections.length).toBe(1) + ;[selection1] = selections expect(selection1.getScreenRange()).toEqual([[3, 10], [7, 4]]) expect(selection1.isReversed()).toBeTruthy() }) @@ -1701,7 +1801,10 @@ describe('TextEditor', () => { editor.selectToTop() expect(editor.getCursors().length).toBe(1) expect(editor.getCursorBufferPosition()).toEqual([0, 0]) - expect(editor.getLastSelection().getBufferRange()).toEqual([[0, 0], [11, 2]]) + expect(editor.getLastSelection().getBufferRange()).toEqual([ + [0, 0], + [11, 2] + ]) expect(editor.getLastSelection().isReversed()).toBeTruthy() }) }) @@ -1713,7 +1816,10 @@ describe('TextEditor', () => { editor.selectToBottom() expect(editor.getCursors().length).toBe(1) expect(editor.getCursorBufferPosition()).toEqual([12, 2]) - expect(editor.getLastSelection().getBufferRange()).toEqual([[9, 3], [12, 2]]) + expect(editor.getLastSelection().getBufferRange()).toEqual([ + [9, 3], + [12, 2] + ]) expect(editor.getLastSelection().isReversed()).toBeFalsy() }) }) @@ -1721,7 +1827,9 @@ describe('TextEditor', () => { describe('.selectAll()', () => { it('selects the entire buffer', () => { editor.selectAll() - expect(editor.getLastSelection().getBufferRange()).toEqual(buffer.getRange()) + expect(editor.getLastSelection().getBufferRange()).toEqual( + buffer.getRange() + ) }) }) @@ -1772,7 +1880,9 @@ describe('TextEditor', () => { editor.setCursorScreenPosition([1, 2]) editor.selectLinesContainingCursors() expect(editor.getSelectedBufferRange()).toEqual([[1, 0], [2, 0]]) - expect(editor.getSelectedText()).toBe(' var sort = function(items) {\n') + expect(editor.getSelectedText()).toBe( + ' var sort = function(items) {\n' + ) editor.setCursorScreenPosition([12, 2]) editor.selectLinesContainingCursors() @@ -1867,7 +1977,12 @@ describe('TextEditor', () => { editor.selectToPreviousWordBoundary() expect(editor.getSelections().length).toBe(4) - const [selection1, selection2, selection3, selection4] = editor.getSelections() + const [ + selection1, + selection2, + selection3, + selection4 + ] = editor.getSelections() expect(selection1.getBufferRange()).toEqual([[0, 8], [0, 4]]) expect(selection1.isReversed()).toBeTruthy() expect(selection2.getBufferRange()).toEqual([[2, 0], [1, 30]]) @@ -1889,7 +2004,12 @@ describe('TextEditor', () => { editor.selectToNextWordBoundary() expect(editor.getSelections().length).toBe(4) - const [selection1, selection2, selection3, selection4] = editor.getSelections() + const [ + selection1, + selection2, + selection3, + selection4 + ] = editor.getSelections() expect(selection1.getBufferRange()).toEqual([[0, 8], [0, 13]]) expect(selection1.isReversed()).toBeFalsy() expect(selection2.getBufferRange()).toEqual([[2, 40], [3, 0]]) @@ -1912,7 +2032,12 @@ describe('TextEditor', () => { editor.addCursorAtBufferPosition([1, 7]) editor.addCursorAtBufferPosition([2, 5]) editor.addCursorAtBufferPosition([3, 3]) - const [selection1, selection2, selection3, selection4] = editor.getSelections() + const [ + selection1, + selection2, + selection3, + selection4 + ] = editor.getSelections() editor.selectToPreviousSubwordBoundary() expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 5]]) @@ -1937,7 +2062,12 @@ describe('TextEditor', () => { editor.addCursorAtBufferPosition([1, 7]) editor.addCursorAtBufferPosition([2, 2]) editor.addCursorAtBufferPosition([3, 1]) - const [selection1, selection2, selection3, selection4] = editor.getSelections() + const [ + selection1, + selection2, + selection3, + selection4 + ] = editor.getSelections() editor.selectToNextSubwordBoundary() expect(selection1.getBufferRange()).toEqual([[0, 1], [0, 4]]) @@ -2083,13 +2213,23 @@ describe('TextEditor', () => { editor.setCursorBufferPosition([0, 1]) editor.addCursorAtBufferPosition([0, 12]) - const scopeDescriptors = editor.getCursors().map(c => c.getScopeDescriptor()) + const scopeDescriptors = editor + .getCursors() + .map(c => c.getScopeDescriptor()) expect(scopeDescriptors[0].getScopesArray()).toEqual(['source.js']) - expect(scopeDescriptors[1].getScopesArray()).toEqual(['source.js', 'string.quoted']) + expect(scopeDescriptors[1].getScopesArray()).toEqual([ + 'source.js', + 'string.quoted' + ]) - spyOn(editor.getBuffer().getLanguageMode(), 'getNonWordCharacters').andCallFake(function (position) { - const result = '/\()"\':,.;<>~!@#$%^&*|+=[]{}`?' - const scopes = this.scopeDescriptorForPosition(position).getScopesArray() + spyOn( + editor.getBuffer().getLanguageMode(), + 'getNonWordCharacters' + ).andCallFake(function (position) { + const result = '/()"\':,.;<>~!@#$%^&*|+=[]{}`?' + const scopes = this.scopeDescriptorForPosition( + position + ).getScopesArray() if (scopes.some(scope => scope.startsWith('string'))) { return result } else { @@ -2122,8 +2262,8 @@ describe('TextEditor', () => { expect(selection2.getBufferRange()).toEqual([[1, 2], [1, 7]]) expect(selection2.isReversed()).toBeTruthy() - editor.selectToFirstCharacterOfLine(); - [selection1, selection2] = editor.getSelections() + editor.selectToFirstCharacterOfLine() + ;[selection1, selection2] = editor.getSelections() expect(selection1.getBufferRange()).toEqual([[0, 0], [0, 5]]) expect(selection1.isReversed()).toBeTruthy() expect(selection2.getBufferRange()).toEqual([[1, 0], [1, 7]]) @@ -2134,7 +2274,10 @@ describe('TextEditor', () => { describe('.setSelectedBufferRanges(ranges)', () => { it('clears existing selections and creates selections for each of the given ranges', () => { editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[4, 4], [5, 5]]]) - expect(editor.getSelectedBufferRanges()).toEqual([[[2, 2], [3, 3]], [[4, 4], [5, 5]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[2, 2], [3, 3]], + [[4, 4], [5, 5]] + ]) editor.setSelectedBufferRanges([[[5, 5], [6, 6]]]) expect(editor.getSelectedBufferRanges()).toEqual([[[5, 5], [6, 6]]]) @@ -2147,14 +2290,17 @@ describe('TextEditor', () => { it('does not merge non-empty adjacent selections', () => { editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[3, 3], [5, 5]]]) - expect(editor.getSelectedBufferRanges()).toEqual([[[2, 2], [3, 3]], [[3, 3], [5, 5]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[2, 2], [3, 3]], + [[3, 3], [5, 5]] + ]) }) it('recycles existing selection instances', () => { selection = editor.getLastSelection() editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[4, 4], [5, 5]]]) - const [selection1, selection2] = editor.getSelections() + const [selection1] = editor.getSelections() expect(selection1).toBe(selection) expect(selection1.getBufferRange()).toEqual([[2, 2], [3, 3]]) }) @@ -2183,7 +2329,9 @@ describe('TextEditor', () => { editor.setSelectedBufferRange([[0, 0], [0, 0]]) editor.foldBufferRowRange(1, 4) editor.foldBufferRowRange(6, 8) - editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 0], [6, 1]]], {preserveFolds: true}) + editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 0], [6, 1]]], { + preserveFolds: true + }) expect(editor.isFoldedAtBufferRow(1)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() }) @@ -2195,7 +2343,10 @@ describe('TextEditor', () => { it('clears existing selections and creates selections for each of the given ranges', () => { editor.setSelectedScreenRanges([[[3, 4], [3, 7]], [[5, 4], [5, 7]]]) - expect(editor.getSelectedBufferRanges()).toEqual([[[3, 4], [3, 7]], [[8, 4], [8, 7]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[3, 4], [3, 7]], + [[8, 4], [8, 7]] + ]) editor.setSelectedScreenRanges([[[6, 2], [6, 4]]]) expect(editor.getSelectedScreenRanges()).toEqual([[[6, 2], [6, 4]]]) @@ -2213,7 +2364,7 @@ describe('TextEditor', () => { selection = editor.getLastSelection() editor.setSelectedScreenRanges([[[2, 2], [3, 4]], [[4, 4], [5, 5]]]) - const [selection1, selection2] = editor.getSelections() + const [selection1] = editor.getSelections() expect(selection1).toBe(selection) expect(selection1.getScreenRange()).toEqual([[2, 2], [3, 4]]) }) @@ -2241,7 +2392,10 @@ describe('TextEditor', () => { describe('.addSelectionForBufferRange(bufferRange)', () => { it('adds a selection for the specified buffer range', () => { editor.addSelectionForBufferRange([[3, 4], [5, 6]]) - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 0]], [[3, 4], [5, 6]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 0], [0, 0]], + [[3, 4], [5, 6]] + ]) }) }) @@ -2316,7 +2470,10 @@ describe('TextEditor', () => { }) it('takes atomic tokens into account', async () => { - editor = await atom.workspace.open('sample-with-tabs-and-leading-comment.coffee', {autoIndent: false}) + editor = await atom.workspace.open( + 'sample-with-tabs-and-leading-comment.coffee', + { autoIndent: false } + ) editor.setSelectedBufferRange([[2, 1], [2, 3]]) editor.addSelectionBelow() expect(editor.getSelectedBufferRanges()).toEqual([ @@ -2392,7 +2549,9 @@ describe('TextEditor', () => { editor.setCursorBufferPosition([0, 1]) let addedSelectionCount = 0 - editor.onDidAddSelection(() => { addedSelectionCount++ }) + editor.onDidAddSelection(() => { + addedSelectionCount++ + }) editor.addSelectionBelow() editor.addSelectionBelow() @@ -2451,7 +2610,10 @@ describe('TextEditor', () => { }) it('takes atomic tokens into account', async () => { - editor = await atom.workspace.open('sample-with-tabs-and-leading-comment.coffee', {autoIndent: false}) + editor = await atom.workspace.open( + 'sample-with-tabs-and-leading-comment.coffee', + { autoIndent: false } + ) editor.setSelectedBufferRange([[3, 1], [3, 2]]) editor.addSelectionAbove() expect(editor.getSelectedBufferRanges()).toEqual([ @@ -2527,7 +2689,9 @@ describe('TextEditor', () => { editor.setCursorBufferPosition([4, 1]) let addedSelectionCount = 0 - editor.onDidAddSelection(() => { addedSelectionCount++ }) + editor.onDidAddSelection(() => { + addedSelectionCount++ + }) editor.addSelectionAbove() editor.addSelectionAbove() @@ -2565,7 +2729,12 @@ describe('TextEditor', () => { const selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]]) const selection3 = editor.addSelectionForBufferRange([[8, 4], [8, 10]]) const selection4 = editor.addSelectionForBufferRange([[1, 6], [1, 10]]) - expect(editor.getSelections()).toEqual([selection, selection2, selection3, selection4]) + expect(editor.getSelections()).toEqual([ + selection, + selection2, + selection3, + selection4 + ]) return [selection, selection2, selection3, selection4] } @@ -2582,7 +2751,10 @@ describe('TextEditor', () => { expect(editor.getSelections()).toEqual([selection1]) expect(autoscrollEvents).toEqual([ - {screenRange: selection1.getScreenRange(), options: {center: true, reversed: false}} + { + screenRange: selection1.getScreenRange(), + options: { center: true, reversed: false } + } ]) }) }) @@ -2620,7 +2792,9 @@ describe('TextEditor', () => { expect(editor2.getText()).toBe(editor.getText()) editor.setSelectedBufferRanges([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) editor2.setSelectedBufferRanges([[[8, 7], [6, 5]], [[4, 3], [2, 1]]]) - expect(editor2.getSelectedBufferRanges()).not.toEqual(editor.getSelectedBufferRanges()) + expect(editor2.getSelectedBufferRanges()).not.toEqual( + editor.getSelectedBufferRanges() + ) }) }) @@ -2629,13 +2803,15 @@ describe('TextEditor', () => { it('moves the line under the cursor up', () => { editor.setCursorBufferPosition([1, 0]) editor.moveLineUp() - expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe(' var sort = function(items) {') + expect(editor.getTextInBufferRange([[0, 0], [0, 30]])).toBe( + ' var sort = function(items) {' + ) expect(editor.indentationForBufferRow(0)).toBe(1) expect(editor.indentationForBufferRow(1)).toBe(0) }) it("updates the line's indentation when the the autoIndent setting is true", () => { - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.setCursorBufferPosition([1, 0]) editor.moveLineUp() expect(editor.indentationForBufferRow(0)).toBe(0) @@ -2646,24 +2822,35 @@ describe('TextEditor', () => { describe('when the selection spans a single line', () => { describe('when there is no fold in the preceeding row', () => it('moves the line to the preceding row', () => { - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) editor.setSelectedBufferRange([[3, 2], [3, 9]]) editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[2, 2], [2, 9]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') - }) - ) + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) + })) describe('when the cursor is at the beginning of a fold', () => it('moves the line to the previous row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRange([[4, 2], [4, 9]], {preserveFolds: true}) + editor.setSelectedBufferRange([[4, 2], [4, 9]], { + preserveFolds: true + }) expect(editor.getSelectedBufferRange()).toEqual([[4, 2], [4, 9]]) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2675,20 +2862,25 @@ describe('TextEditor', () => { editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[3, 2], [3, 9]]) - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(7)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(7)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() - }) - ) + })) describe('when the preceding row consists of folded code', () => it('moves the line above the folded row and perseveres the correct folds', () => { - expect(editor.lineTextForBufferRow(8)).toBe(' return sort(left).concat(pivot).concat(sort(right));') + expect(editor.lineTextForBufferRow(8)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) expect(editor.lineTextForBufferRow(9)).toBe(' };') editor.foldBufferRowRange(4, 7) @@ -2703,38 +2895,57 @@ describe('TextEditor', () => { editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[4, 0], [4, 4]]) - expect(editor.lineTextForBufferRow(4)).toBe(' return sort(left).concat(pivot).concat(sort(right));') - expect(editor.lineTextForBufferRow(5)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' while(items.length > 0) {' + ) expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() - }) - ) + })) }) describe('when the selection spans multiple lines', () => { it('moves the lines spanned by the selection to the preceding row', () => { - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.setSelectedBufferRange([[3, 2], [4, 9]]) editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[2, 2], [3, 9]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(4)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' if (items.length <= 1) return items;' + ) }) describe("when the selection's end intersects a fold", () => it('moves the lines to the previous row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRange([[3, 2], [4, 9]], {preserveFolds: true}) + editor.setSelectedBufferRange([[3, 2], [4, 9]], { + preserveFolds: true + }) expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2746,9 +2957,15 @@ describe('TextEditor', () => { editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[2, 2], [3, 9]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(7)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(7)).toBe( + ' if (items.length <= 1) return items;' + ) expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() @@ -2756,15 +2973,18 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() - }) - ) + })) describe("when the selection's start intersects a fold", () => it('moves the lines to the previous row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRange([[4, 2], [8, 9]], {preserveFolds: true}) + editor.setSelectedBufferRange([[4, 2], [8, 9]], { + preserveFolds: true + }) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -2776,9 +2996,15 @@ describe('TextEditor', () => { editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[3, 2], [7, 9]]) - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(7)).toBe(' return sort(left).concat(pivot).concat(sort(right));') - expect(editor.lineTextForBufferRow(8)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(7)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) + expect(editor.lineTextForBufferRow(8)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -2786,29 +3012,42 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - }) - ) + })) }) describe('when the selection spans multiple lines, but ends at column 0', () => { it('does not move the last line of the selection', () => { - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.setSelectedBufferRange([[3, 2], [4, 0]]) editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[2, 2], [3, 0]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) }) }) describe('when the preceeding row is a folded row', () => { it('moves the lines spanned by the selection to the preceeding row, but preserves the folded code', () => { - expect(editor.lineTextForBufferRow(8)).toBe(' return sort(left).concat(pivot).concat(sort(right));') + expect(editor.lineTextForBufferRow(8)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) expect(editor.lineTextForBufferRow(9)).toBe(' };') editor.foldBufferRowRange(4, 7) @@ -2822,9 +3061,13 @@ describe('TextEditor', () => { editor.moveLineUp() expect(editor.getSelectedBufferRange()).toEqual([[4, 0], [5, 2]]) - expect(editor.lineTextForBufferRow(4)).toBe(' return sort(left).concat(pivot).concat(sort(right));') + expect(editor.lineTextForBufferRow(4)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) expect(editor.lineTextForBufferRow(5)).toBe(' };') - expect(editor.lineTextForBufferRow(6)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(6)).toBe( + ' while(items.length > 0) {' + ) expect(editor.isFoldedAtBufferRow(5)).toBeFalsy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() @@ -2839,28 +3082,49 @@ describe('TextEditor', () => { describe('when all the selections span different lines', () => { describe('when there is no folds', () => it('moves all lines that are spanned by a selection to the preceding row', () => { - editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + editor.setSelectedBufferRanges([ + [[1, 2], [1, 9]], + [[3, 2], [3, 9]], + [[5, 2], [5, 9]] + ]) editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 2], [0, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]]) - expect(editor.lineTextForBufferRow(0)).toBe(' var sort = function(items) {') - expect(editor.lineTextForBufferRow(1)).toBe('var quicksort = function () {') - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(4)).toBe(' current = items.shift();') - expect(editor.lineTextForBufferRow(5)).toBe(' while(items.length > 0) {') - }) - ) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 2], [0, 9]], + [[2, 2], [2, 9]], + [[4, 2], [4, 9]] + ]) + expect(editor.lineTextForBufferRow(0)).toBe( + ' var sort = function(items) {' + ) + expect(editor.lineTextForBufferRow(1)).toBe( + 'var quicksort = function () {' + ) + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' current = items.shift();' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' while(items.length > 0) {' + ) + })) describe('when one selection intersects a fold', () => it('moves the lines to the previous row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRanges([ - [[2, 2], [2, 9]], - [[4, 2], [4, 9]] - ], {preserveFolds: true}) + editor.setSelectedBufferRanges( + [[[2, 2], [2, 9]], [[4, 2], [4, 9]]], + { preserveFolds: true } + ) expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() @@ -2878,10 +3142,18 @@ describe('TextEditor', () => { [[3, 2], [3, 9]] ]) - expect(editor.lineTextForBufferRow(1)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(2)).toBe(' var sort = function(items) {') - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(7)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(1)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(2)).toBe( + ' var sort = function(items) {' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(7)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.isFoldedAtBufferRow(1)).toBeFalsy() expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() @@ -2891,8 +3163,7 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - }) - ) + })) describe('when there is a fold', () => it('moves all lines that spanned by a selection to preceding row, preserving all folds', () => { @@ -2904,24 +3175,35 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - editor.setSelectedBufferRanges([[[8, 0], [8, 3]], [[11, 0], [11, 5]]]) + editor.setSelectedBufferRanges([ + [[8, 0], [8, 3]], + [[11, 0], [11, 5]] + ]) editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual([[[4, 0], [4, 3]], [[10, 0], [10, 5]]]) - expect(editor.lineTextForBufferRow(4)).toBe(' return sort(left).concat(pivot).concat(sort(right));') - expect(editor.lineTextForBufferRow(10)).toBe(' return sort(Array.apply(this, arguments));') + expect(editor.getSelectedBufferRanges()).toEqual([ + [[4, 0], [4, 3]], + [[10, 0], [10, 5]] + ]) + expect(editor.lineTextForBufferRow(4)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) + expect(editor.lineTextForBufferRow(10)).toBe( + ' return sort(Array.apply(this, arguments));' + ) expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() - }) - ) + })) }) describe('when there are many folds', () => { beforeEach(async () => { - editor = await atom.workspace.open('sample-with-many-folds.js', {autoIndent: false}) + editor = await atom.workspace.open('sample-with-many-folds.js', { + autoIndent: false + }) }) describe('and many selections intersects folded rows', () => @@ -2929,10 +3211,10 @@ describe('TextEditor', () => { editor.foldBufferRowRange(2, 4) editor.foldBufferRowRange(7, 9) - editor.setSelectedBufferRanges([ - [[1, 0], [5, 4]], - [[7, 0], [7, 4]] - ], {preserveFolds: true}) + editor.setSelectedBufferRanges( + [[[1, 0], [5, 4]], [[7, 0], [7, 4]]], + { preserveFolds: true } + ) editor.moveLineUp() @@ -2951,27 +3233,42 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() - }) - ) + })) }) describe('when some of the selections span the same lines', () => { it('moves lines that contain multiple selections correctly', () => { - editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) + editor.setSelectedBufferRanges([ + [[3, 2], [3, 9]], + [[3, 12], [3, 13]] + ]) editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual([[[2, 2], [2, 9]], [[2, 12], [2, 13]]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.getSelectedBufferRanges()).toEqual([ + [[2, 2], [2, 9]], + [[2, 12], [2, 13]] + ]) + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) }) }) describe('when one of the selections spans line 0', () => { it("doesn't move any lines, since line 0 can't move", () => { - editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]]) + editor.setSelectedBufferRanges([ + [[0, 2], [1, 9]], + [[2, 2], [2, 9]], + [[4, 2], [4, 9]] + ]) editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[4, 2], [4, 9]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 2], [1, 9]], + [[2, 2], [2, 9]], + [[4, 2], [4, 9]] + ]) expect(buffer.isModified()).toBe(false) }) }) @@ -2979,11 +3276,19 @@ describe('TextEditor', () => { describe('when one of the selections spans the last line, and it is empty', () => { it("doesn't move any lines, since the last line can't move", () => { buffer.append('\n') - editor.setSelectedBufferRanges([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]]) + editor.setSelectedBufferRanges([ + [[0, 2], [1, 9]], + [[2, 2], [2, 9]], + [[13, 0], [13, 0]] + ]) editor.moveLineUp() - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 2], [1, 9]], [[2, 2], [2, 9]], [[13, 0], [13, 0]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 2], [1, 9]], + [[2, 2], [2, 9]], + [[13, 0], [13, 0]] + ]) }) }) }) @@ -2993,13 +3298,15 @@ describe('TextEditor', () => { it('moves the line under the cursor down', () => { editor.setCursorBufferPosition([0, 0]) editor.moveLineDown() - expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe('var quicksort = function () {') + expect(editor.getTextInBufferRange([[1, 0], [1, 31]])).toBe( + 'var quicksort = function () {' + ) expect(editor.indentationForBufferRow(0)).toBe(1) expect(editor.indentationForBufferRow(1)).toBe(0) }) it("updates the line's indentation when the editor.autoIndent setting is true", () => { - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.setCursorBufferPosition([0, 0]) editor.moveLineDown() expect(editor.indentationForBufferRow(0)).toBe(1) @@ -3010,24 +3317,35 @@ describe('TextEditor', () => { describe('when the selection spans a single line', () => { describe('when there is no fold in the following row', () => it('moves the line to the following row', () => { - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) editor.setSelectedBufferRange([[2, 2], [2, 9]]) editor.moveLineDown() expect(editor.getSelectedBufferRange()).toEqual([[3, 2], [3, 9]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') - }) - ) + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) + })) describe('when the cursor is at the beginning of a fold', () => it('moves the line to the following row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRange([[4, 2], [4, 9]], {preserveFolds: true}) + editor.setSelectedBufferRange([[4, 2], [4, 9]], { + preserveFolds: true + }) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -3038,21 +3356,28 @@ describe('TextEditor', () => { editor.moveLineDown() expect(editor.getSelectedBufferRange()).toEqual([[5, 2], [5, 9]]) - expect(editor.lineTextForBufferRow(4)).toBe(' return sort(left).concat(pivot).concat(sort(right));') - expect(editor.lineTextForBufferRow(5)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' while(items.length > 0) {' + ) expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeTruthy() expect(editor.isFoldedAtBufferRow(9)).toBeFalsy() - }) - ) + })) describe('when the following row is a folded row', () => it('moves the line below the folded row and preserves the fold', () => { - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) @@ -3066,56 +3391,87 @@ describe('TextEditor', () => { editor.moveLineDown() expect(editor.getSelectedBufferRange()).toEqual([[7, 0], [7, 4]]) - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() - expect(editor.lineTextForBufferRow(7)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - }) - ) + expect(editor.lineTextForBufferRow(7)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + })) }) describe('when the selection spans multiple lines', () => { it('moves the lines spanned by the selection to the following row', () => { - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.setSelectedBufferRange([[2, 2], [3, 9]]) editor.moveLineDown() expect(editor.getSelectedBufferRange()).toEqual([[3, 2], [4, 9]]) - expect(editor.lineTextForBufferRow(2)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(4)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(2)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) }) }) describe('when the selection spans multiple lines, but ends at column 0', () => { it('does not move the last line of the selection', () => { - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.setSelectedBufferRange([[2, 2], [3, 0]]) editor.moveLineDown() expect(editor.getSelectedBufferRange()).toEqual([[3, 2], [4, 0]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) }) }) describe("when the selection's end intersects a fold", () => { it('moves the lines to the following row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRange([[3, 2], [4, 9]], {preserveFolds: true}) + editor.setSelectedBufferRange([[3, 2], [4, 9]], { + preserveFolds: true + }) expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -3127,9 +3483,15 @@ describe('TextEditor', () => { editor.moveLineDown() expect(editor.getSelectedBufferRange()).toEqual([[4, 2], [5, 9]]) - expect(editor.lineTextForBufferRow(3)).toBe(' return sort(left).concat(pivot).concat(sort(right));') - expect(editor.lineTextForBufferRow(4)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(5)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(3)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' while(items.length > 0) {' + ) expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -3142,10 +3504,14 @@ describe('TextEditor', () => { describe("when the selection's start intersects a fold", () => { it('moves the lines to the following row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRange([[4, 2], [8, 9]], {preserveFolds: true}) + editor.setSelectedBufferRange([[4, 2], [8, 9]], { + preserveFolds: true + }) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -3158,8 +3524,12 @@ describe('TextEditor', () => { expect(editor.getSelectedBufferRange()).toEqual([[5, 2], [9, 9]]) expect(editor.lineTextForBufferRow(4)).toBe(' };') - expect(editor.lineTextForBufferRow(5)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(9)).toBe(' return sort(left).concat(pivot).concat(sort(right));') + expect(editor.lineTextForBufferRow(5)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(9)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) expect(editor.isFoldedAtBufferRow(4)).toBeFalsy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() @@ -3173,8 +3543,12 @@ describe('TextEditor', () => { describe('when the following row is a folded row', () => { it('moves the lines spanned by the selection to the following row, but preserves the folded code', () => { - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) editor.foldBufferRowRange(4, 7) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() @@ -3187,14 +3561,18 @@ describe('TextEditor', () => { editor.moveLineDown() expect(editor.getSelectedBufferRange()).toEqual([[6, 0], [7, 2]]) - expect(editor.lineTextForBufferRow(2)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' while(items.length > 0) {' + ) expect(editor.isFoldedAtBufferRow(1)).toBeFalsy() expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() - expect(editor.lineTextForBufferRow(6)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(6)).toBe( + ' if (items.length <= 1) return items;' + ) }) }) @@ -3202,7 +3580,9 @@ describe('TextEditor', () => { it('appends line ending to last line and moves the lines spanned by the selection to the preceeding row', () => { expect(editor.lineTextForBufferRow(9)).toBe(' };') expect(editor.lineTextForBufferRow(10)).toBe('') - expect(editor.lineTextForBufferRow(11)).toBe(' return sort(Array.apply(this, arguments));') + expect(editor.lineTextForBufferRow(11)).toBe( + ' return sort(Array.apply(this, arguments));' + ) expect(editor.lineTextForBufferRow(12)).toBe('};') editor.setSelectedBufferRange([[10, 0], [12, 2]]) @@ -3210,7 +3590,9 @@ describe('TextEditor', () => { expect(editor.getSelectedBufferRange()).toEqual([[9, 0], [11, 2]]) expect(editor.lineTextForBufferRow(9)).toBe('') - expect(editor.lineTextForBufferRow(10)).toBe(' return sort(Array.apply(this, arguments));') + expect(editor.lineTextForBufferRow(10)).toBe( + ' return sort(Array.apply(this, arguments));' + ) expect(editor.lineTextForBufferRow(11)).toBe('};') expect(editor.lineTextForBufferRow(12)).toBe(' };') }) @@ -3221,22 +3603,43 @@ describe('TextEditor', () => { describe('when all the selections span different lines', () => { describe('when there is no folds', () => it('moves all lines that are spanned by a selection to the following row', () => { - editor.setSelectedBufferRanges([[[1, 2], [1, 9]], [[3, 2], [3, 9]], [[5, 2], [5, 9]]]) + editor.setSelectedBufferRanges([ + [[1, 2], [1, 9]], + [[3, 2], [3, 9]], + [[5, 2], [5, 9]] + ]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual([[[6, 2], [6, 9]], [[4, 2], [4, 9]], [[2, 2], [2, 9]]]) - expect(editor.lineTextForBufferRow(1)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(2)).toBe(' var sort = function(items) {') - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(4)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(5)).toBe(' current < pivot ? left.push(current) : right.push(current);') - expect(editor.lineTextForBufferRow(6)).toBe(' current = items.shift();') - }) - ) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[6, 2], [6, 9]], + [[4, 2], [4, 9]], + [[2, 2], [2, 9]] + ]) + expect(editor.lineTextForBufferRow(1)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(2)).toBe( + ' var sort = function(items) {' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' current < pivot ? left.push(current) : right.push(current);' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' current = items.shift();' + ) + })) describe('when there are many folds', () => { beforeEach(async () => { - editor = await atom.workspace.open('sample-with-many-folds.js', {autoIndent: false}) + editor = await atom.workspace.open('sample-with-many-folds.js', { + autoIndent: false + }) }) describe('and many selections intersects folded rows', () => @@ -3244,18 +3647,22 @@ describe('TextEditor', () => { editor.foldBufferRowRange(2, 4) editor.foldBufferRowRange(7, 9) - editor.setSelectedBufferRanges([ - [[2, 0], [2, 4]], - [[6, 0], [10, 4]] - ], {preserveFolds: true}) + editor.setSelectedBufferRanges( + [[[2, 0], [2, 4]], [[6, 0], [10, 4]]], + { preserveFolds: true } + ) editor.moveLineDown() expect(editor.lineTextForBufferRow(2)).toEqual('6;') - expect(editor.lineTextForBufferRow(3)).toEqual('function f3() {') + expect(editor.lineTextForBufferRow(3)).toEqual( + 'function f3() {' + ) expect(editor.lineTextForBufferRow(6)).toEqual('12;') expect(editor.lineTextForBufferRow(7)).toEqual('7;') - expect(editor.lineTextForBufferRow(8)).toEqual('function f8() {') + expect(editor.lineTextForBufferRow(8)).toEqual( + 'function f8() {' + ) expect(editor.lineTextForBufferRow(11)).toEqual('11;') expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() @@ -3268,8 +3675,7 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(9)).toBeTruthy() expect(editor.isFoldedAtBufferRow(10)).toBeTruthy() expect(editor.isFoldedAtBufferRow(11)).toBeFalsy() - }) - ) + })) }) describe('when there is a fold below one of the selected row', () => @@ -3282,20 +3688,33 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - editor.setSelectedBufferRanges([[[1, 2], [1, 6]], [[3, 0], [3, 4]], [[8, 0], [8, 3]]]) + editor.setSelectedBufferRanges([ + [[1, 2], [1, 6]], + [[3, 0], [3, 4]], + [[8, 0], [8, 3]] + ]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual([[[9, 0], [9, 3]], [[7, 0], [7, 4]], [[2, 2], [2, 6]]]) - expect(editor.lineTextForBufferRow(2)).toBe(' var sort = function(items) {') + expect(editor.getSelectedBufferRanges()).toEqual([ + [[9, 0], [9, 3]], + [[7, 0], [7, 4]], + [[2, 2], [2, 6]] + ]) + expect(editor.lineTextForBufferRow(2)).toBe( + ' var sort = function(items) {' + ) expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeTruthy() expect(editor.isFoldedAtBufferRow(7)).toBeFalsy() - expect(editor.lineTextForBufferRow(7)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(9)).toBe(' return sort(left).concat(pivot).concat(sort(right));') - }) - ) + expect(editor.lineTextForBufferRow(7)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(9)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) + })) describe('when there is a fold below a group of multiple selections without any lines with no selection in-between', () => it('moves all the lines below the fold, preserving the fold', () => { @@ -3307,31 +3726,44 @@ describe('TextEditor', () => { expect(editor.isFoldedAtBufferRow(7)).toBeTruthy() expect(editor.isFoldedAtBufferRow(8)).toBeFalsy() - editor.setSelectedBufferRanges([[[2, 2], [2, 6]], [[3, 0], [3, 4]]]) + editor.setSelectedBufferRanges([ + [[2, 2], [2, 6]], + [[3, 0], [3, 4]] + ]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual([[[7, 0], [7, 4]], [[6, 2], [6, 6]]]) - expect(editor.lineTextForBufferRow(2)).toBe(' while(items.length > 0) {') + expect(editor.getSelectedBufferRanges()).toEqual([ + [[7, 0], [7, 4]], + [[6, 2], [6, 6]] + ]) + expect(editor.lineTextForBufferRow(2)).toBe( + ' while(items.length > 0) {' + ) expect(editor.isFoldedAtBufferRow(2)).toBeTruthy() expect(editor.isFoldedAtBufferRow(3)).toBeTruthy() expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() expect(editor.isFoldedAtBufferRow(5)).toBeTruthy() expect(editor.isFoldedAtBufferRow(6)).toBeFalsy() - expect(editor.lineTextForBufferRow(6)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(7)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - }) - ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(7)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + })) }) describe('when one selection intersects a fold', () => { it('moves the lines to the previous row without breaking the fold', () => { - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) editor.foldBufferRowRange(4, 7) - editor.setSelectedBufferRanges([ - [[2, 2], [2, 9]], - [[4, 2], [4, 9]] - ], {preserveFolds: true}) + editor.setSelectedBufferRanges( + [[[2, 2], [2, 9]], [[4, 2], [4, 9]]], + { preserveFolds: true } + ) expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() expect(editor.isFoldedAtBufferRow(3)).toBeFalsy() @@ -3349,11 +3781,19 @@ describe('TextEditor', () => { [[3, 2], [3, 9]] ]) - expect(editor.lineTextForBufferRow(2)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(4)).toBe(' return sort(left).concat(pivot).concat(sort(right));') + expect(editor.lineTextForBufferRow(2)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) - expect(editor.lineTextForBufferRow(5)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(5)).toBe( + ' while(items.length > 0) {' + ) expect(editor.lineTextForBufferRow(9)).toBe(' };') expect(editor.isFoldedAtBufferRow(2)).toBeFalsy() @@ -3369,11 +3809,19 @@ describe('TextEditor', () => { describe('when some of the selections span the same lines', () => { it('moves lines that contain multiple selections correctly', () => { - editor.setSelectedBufferRanges([[[3, 2], [3, 9]], [[3, 12], [3, 13]]]) + editor.setSelectedBufferRanges([ + [[3, 2], [3, 9]], + [[3, 12], [3, 13]] + ]) editor.moveLineDown() - expect(editor.getSelectedBufferRanges()).toEqual([[[4, 12], [4, 13]], [[4, 2], [4, 9]]]) - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') + expect(editor.getSelectedBufferRanges()).toEqual([ + [[4, 12], [4, 13]], + [[4, 2], [4, 9]] + ]) + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) }) }) @@ -3381,7 +3829,7 @@ describe('TextEditor', () => { beforeEach(() => { editor.setSoftWrapped(true) editor.setEditorWidthInChars(80) - editor.setText(dedent ` + editor.setText(dedent` 1 2 Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. @@ -3418,7 +3866,7 @@ describe('TextEditor', () => { it('replaces the selection with the given text', () => { const range = editor.insertText('xxx') - expect(range).toEqual([ [[1, 0], [1, 3]] ]) + expect(range).toEqual([[[1, 0], [1, 3]]]) expect(buffer.lineForRow(1)).toBe('xxxvar sort = function(items) {') }) }) @@ -3431,7 +3879,9 @@ describe('TextEditor', () => { editor.insertText('xxx') - expect(buffer.lineForRow(1)).toBe(' xxxvarxxx sort = function(items) {') + expect(buffer.lineForRow(1)).toBe( + ' xxxvarxxx sort = function(items) {' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([1, 5]) @@ -3446,8 +3896,12 @@ describe('TextEditor', () => { editor.insertText('xxx') - expect(buffer.lineForRow(1)).toBe(' xxxvar sort = function(items) {') - expect(buffer.lineForRow(2)).toBe(' xxxif (items.length <= 1) return items;') + expect(buffer.lineForRow(1)).toBe( + ' xxxvar sort = function(items) {' + ) + expect(buffer.lineForRow(2)).toBe( + ' xxxif (items.length <= 1) return items;' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([1, 5]) @@ -3459,7 +3913,10 @@ describe('TextEditor', () => { describe('when there are multiple non-empty selections', () => { describe('when the selections are on the same line', () => { it('replaces each selection range with the inserted characters', () => { - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 22], [0, 24]]]) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[0, 22], [0, 24]] + ]) editor.insertText('x') const [cursor1, cursor2] = editor.getCursors() @@ -3481,7 +3938,9 @@ describe('TextEditor', () => { editor.insertText('xxx') expect(buffer.lineForRow(1)).toBe('xxxvar sort = function(items) {') - expect(buffer.lineForRow(2)).toBe('xxxif (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + 'xxxif (items.length <= 1) return items;' + ) const [selection1, selection2] = editor.getSelections() expect(selection1.isEmpty()).toBeTruthy() @@ -3505,9 +3964,21 @@ describe('TextEditor', () => { beforeEach(() => editor.setSelectedBufferRange([[1, 0], [1, 2]])) it('notifies the observers when inserting text', () => { - const willInsertSpy = jasmine.createSpy().andCallFake(() => expect(buffer.lineForRow(1)).toBe(' var sort = function(items) {')) + const willInsertSpy = jasmine + .createSpy() + .andCallFake(() => + expect(buffer.lineForRow(1)).toBe( + ' var sort = function(items) {' + ) + ) - const didInsertSpy = jasmine.createSpy().andCallFake(() => expect(buffer.lineForRow(1)).toBe('xxxvar sort = function(items) {')) + const didInsertSpy = jasmine + .createSpy() + .andCallFake(() => + expect(buffer.lineForRow(1)).toBe( + 'xxxvar sort = function(items) {' + ) + ) editor.onWillInsertText(willInsertSpy) editor.onDidInsertText(didInsertSpy) @@ -3527,7 +3998,9 @@ describe('TextEditor', () => { }) it('cancels text insertion when an ::onWillInsertText observer calls cancel on an event', () => { - const willInsertSpy = jasmine.createSpy().andCallFake(({cancel}) => cancel()) + const willInsertSpy = jasmine + .createSpy() + .andCallFake(({ cancel }) => cancel()) const didInsertSpy = jasmine.createSpy() @@ -3544,12 +4017,9 @@ describe('TextEditor', () => { describe("when the undo option is set to 'skip'", () => { it('groups the change with the previous change for purposes of undo and redo', () => { - editor.setSelectedBufferRanges([ - [[0, 0], [0, 0]], - [[1, 0], [1, 0]] - ]) + editor.setSelectedBufferRanges([[[0, 0], [0, 0]], [[1, 0], [1, 0]]]) editor.insertText('x') - editor.insertText('y', {undo: 'skip'}) + editor.insertText('y', { undo: 'skip' }) editor.undo() expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') expect(buffer.lineForRow(1)).toBe(' var sort = function(items) {') @@ -3561,18 +4031,21 @@ describe('TextEditor', () => { describe('when there is a single cursor', () => { describe('when the cursor is at the beginning of a line', () => { it('inserts an empty line before it', () => { - editor.setCursorScreenPosition({row: 1, column: 0}) + editor.setCursorScreenPosition({ row: 1, column: 0 }) editor.insertNewline() expect(buffer.lineForRow(1)).toBe('') - expect(editor.getCursorScreenPosition()).toEqual({row: 2, column: 0}) + expect(editor.getCursorScreenPosition()).toEqual({ + row: 2, + column: 0 + }) }) }) describe('when the cursor is in the middle of a line', () => { it('splits the current line to form a new line', () => { - editor.setCursorScreenPosition({row: 1, column: 6}) + editor.setCursorScreenPosition({ row: 1, column: 6 }) const originalLine = buffer.lineForRow(1) const lineBelowOriginalLine = buffer.lineForRow(2) @@ -3581,18 +4054,27 @@ describe('TextEditor', () => { expect(buffer.lineForRow(1)).toBe(originalLine.slice(0, 6)) expect(buffer.lineForRow(2)).toBe(originalLine.slice(6)) expect(buffer.lineForRow(3)).toBe(lineBelowOriginalLine) - expect(editor.getCursorScreenPosition()).toEqual({row: 2, column: 0}) + expect(editor.getCursorScreenPosition()).toEqual({ + row: 2, + column: 0 + }) }) }) describe('when the cursor is on the end of a line', () => { it('inserts an empty line after it', () => { - editor.setCursorScreenPosition({row: 1, column: buffer.lineForRow(1).length}) + editor.setCursorScreenPosition({ + row: 1, + column: buffer.lineForRow(1).length + }) editor.insertNewline() expect(buffer.lineForRow(2)).toBe('') - expect(editor.getCursorScreenPosition()).toEqual({row: 2, column: 0}) + expect(editor.getCursorScreenPosition()).toEqual({ + row: 2, + column: 0 + }) }) }) }) @@ -3606,9 +4088,15 @@ describe('TextEditor', () => { editor.insertNewline() expect(editor.lineTextForBufferRow(3)).toBe(' var pivot') - expect(editor.lineTextForBufferRow(4)).toBe(' = items.shift(), current') - expect(editor.lineTextForBufferRow(5)).toBe(', left = [], right = [];') - expect(editor.lineTextForBufferRow(6)).toBe(' while(items.length > 0) {') + expect(editor.lineTextForBufferRow(4)).toBe( + ' = items.shift(), current' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ', left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' while(items.length > 0) {' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([4, 0]) @@ -3623,11 +4111,19 @@ describe('TextEditor', () => { editor.insertText('\n') expect(editor.lineTextForBufferRow(3)).toBe('') - expect(editor.lineTextForBufferRow(4)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(5)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(6)).toBe(' current = items.shift();') + expect(editor.lineTextForBufferRow(4)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' current = items.shift();' + ) expect(editor.lineTextForBufferRow(7)).toBe('') - expect(editor.lineTextForBufferRow(8)).toBe(' current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(8)).toBe( + ' current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(9)).toBe(' }') const [cursor1, cursor2] = editor.getCursors() @@ -3650,7 +4146,7 @@ describe('TextEditor', () => { }) it("inserts a newline below the cursor's current line, autoindents it, and moves the cursor to the end of the line", () => { - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.insertNewlineBelow() expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') expect(buffer.lineForRow(1)).toBe(' ') @@ -3665,7 +4161,9 @@ describe('TextEditor', () => { editor.insertNewlineAbove() expect(editor.getCursorBufferPosition()).toEqual([0, 0]) expect(editor.lineTextForBufferRow(0)).toBe('') - expect(editor.lineTextForBufferRow(1)).toBe('var quicksort = function () {') + expect(editor.lineTextForBufferRow(1)).toBe( + 'var quicksort = function () {' + ) expect(editor.buffer.getLineCount()).toBe(14) }) }) @@ -3676,7 +4174,9 @@ describe('TextEditor', () => { editor.insertNewlineAbove() expect(editor.getCursorBufferPosition()).toEqual([3, 0]) expect(editor.lineTextForBufferRow(3)).toBe('') - expect(editor.lineTextForBufferRow(4)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(4)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.buffer.getLineCount()).toBe(14) editor.undo() @@ -3685,7 +4185,7 @@ describe('TextEditor', () => { }) it('indents the new line to the correct level when editor.autoIndent is true', () => { - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.setText(' var test') editor.setCursorBufferPosition([0, 2]) @@ -3718,7 +4218,7 @@ describe('TextEditor', () => { describe('.insertNewLine()', () => { describe('when a new line is appended before a closing tag (e.g. by pressing enter before a selection)', () => { it('moves the line down and keeps the indentation level the same when editor.autoIndent is true', () => { - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.setCursorBufferPosition([9, 2]) editor.insertNewline() expect(editor.lineTextForBufferRow(10)).toBe(' };') @@ -3727,7 +4227,7 @@ describe('TextEditor', () => { describe('when a newline is appended with a trailing closing tag behind the cursor (e.g. by pressing enter in the middel of a line)', () => { it('indents the new line to the correct level when editor.autoIndent is true and using a curly-bracket language', () => { - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) atom.grammars.assignLanguageMode(editor, 'source.js') editor.setText('var test = () => {\n return true;};') editor.setCursorBufferPosition([1, 14]) @@ -3738,7 +4238,7 @@ describe('TextEditor', () => { it('indents the new line to the current level when editor.autoIndent is true and no increaseIndentPattern is specified', () => { atom.grammars.assignLanguageMode(editor, null) - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.setText(' if true') editor.setCursorBufferPosition([0, 8]) editor.insertNewline() @@ -3749,7 +4249,7 @@ describe('TextEditor', () => { it('indents the new line to the correct level when editor.autoIndent is true and using an off-side rule language', async () => { await atom.packages.activatePackage('language-coffee-script') - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) atom.grammars.assignLanguageMode(editor, 'source.coffee') editor.setText('if true\n return trueelse\n return false') editor.setCursorBufferPosition([1, 13]) @@ -3763,9 +4263,9 @@ describe('TextEditor', () => { describe('when a newline is appended on a line that matches the decreaseNextIndentPattern', () => { it('indents the new line to the correct level when editor.autoIndent is true', async () => { await atom.packages.activatePackage('language-go') - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) atom.grammars.assignLanguageMode(editor, 'source.go') - editor.setText('fmt.Printf("some%s",\n "thing")') + editor.setText('fmt.Printf("some%s",\n "thing")') // eslint-disable-line no-tabs editor.setCursorBufferPosition([1, 10]) editor.insertNewline() expect(editor.indentationForBufferRow(1)).toBe(1) @@ -3780,20 +4280,25 @@ describe('TextEditor', () => { beforeEach(() => { const selection = editor.getLastSelection() - changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler') + changeScreenRangeHandler = jasmine.createSpy( + 'changeScreenRangeHandler' + ) selection.onDidChangeRange(changeScreenRangeHandler) }) describe('when the cursor is on the middle of the line', () => { it('removes the character before the cursor', () => { - editor.setCursorScreenPosition({row: 1, column: 7}) + editor.setCursorScreenPosition({ row: 1, column: 7 }) expect(buffer.lineForRow(1)).toBe(' var sort = function(items) {') editor.backspace() const line = buffer.lineForRow(1) expect(line).toBe(' var ort = function(items) {') - expect(editor.getCursorScreenPosition()).toEqual({row: 1, column: 6}) + expect(editor.getCursorScreenPosition()).toEqual({ + row: 1, + column: 6 + }) expect(changeScreenRangeHandler).toHaveBeenCalled() }) }) @@ -3804,14 +4309,19 @@ describe('TextEditor', () => { expect(originalLine0).toBe('var quicksort = function () {') expect(buffer.lineForRow(1)).toBe(' var sort = function(items) {') - editor.setCursorScreenPosition({row: 1, column: 0}) + editor.setCursorScreenPosition({ row: 1, column: 0 }) editor.backspace() const line0 = buffer.lineForRow(0) const line1 = buffer.lineForRow(1) - expect(line0).toBe('var quicksort = function () { var sort = function(items) {') + expect(line0).toBe( + 'var quicksort = function () { var sort = function(items) {' + ) expect(line1).toBe(' if (items.length <= 1) return items;') - expect(editor.getCursorScreenPosition()).toEqual([0, originalLine0.length]) + expect(editor.getCursorScreenPosition()).toEqual([ + 0, + originalLine0.length + ]) expect(changeScreenRangeHandler).toHaveBeenCalled() }) @@ -3819,7 +4329,7 @@ describe('TextEditor', () => { describe('when the cursor is at the first column of the first line', () => { it("does nothing, but doesn't raise an error", () => { - editor.setCursorScreenPosition({row: 0, column: 0}) + editor.setCursorScreenPosition({ row: 0, column: 0 }) editor.backspace() }) }) @@ -3843,7 +4353,9 @@ describe('TextEditor', () => { editor.backspace() expect(buffer.lineForRow(7)).toBe(' }') - expect(buffer.lineForRow(8)).toBe(' eturn sort(left).concat(pivot).concat(sort(right));') + expect(buffer.lineForRow(8)).toBe( + ' eturn sort(left).concat(pivot).concat(sort(right));' + ) }) }) @@ -3853,7 +4365,9 @@ describe('TextEditor', () => { editor.foldCurrentRow() editor.backspace() - expect(buffer.lineForRow(1)).toBe(' var sort = function(items) var pivot = items.shift(), current, left = [], right = [];') + expect(buffer.lineForRow(1)).toBe( + ' var sort = function(items) var pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.getCursorScreenPosition()).toEqual([1, 29]) }) }) @@ -3867,7 +4381,9 @@ describe('TextEditor', () => { editor.backspace() - expect(editor.lineTextForBufferRow(3)).toBe(' var pivo = items.shift(), curren, left = [], right = [];') + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivo = items.shift(), curren, left = [], right = [];' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([3, 12]) @@ -3887,8 +4403,12 @@ describe('TextEditor', () => { editor.backspace() - expect(editor.lineTextForBufferRow(3)).toBe(' var pivo = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(4)).toBe(' whileitems.length > 0) {') + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivo = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' whileitems.length > 0) {' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([3, 12]) @@ -3897,8 +4417,7 @@ describe('TextEditor', () => { const [selection1, selection2] = editor.getSelections() expect(selection1.isEmpty()).toBeTruthy() expect(selection2.isEmpty()).toBeTruthy() - }) - ) + })) describe('when the cursors are on the first column of their lines', () => it('removes the newlines preceding each cursor', () => { @@ -3906,16 +4425,21 @@ describe('TextEditor', () => { editor.addCursorAtScreenPosition([6, 0]) editor.backspace() - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(3)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(4)).toBe(' current = items.shift(); current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' current = items.shift(); current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(5)).toBe(' }') const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([2, 40]) expect(cursor2.getBufferPosition()).toEqual([4, 30]) - }) - ) + })) }) }) @@ -3940,7 +4464,10 @@ describe('TextEditor', () => { describe('when there are multiple selections', () => { it('removes all selected text', () => { - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]]) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[0, 16], [0, 24]] + ]) editor.backspace() expect(editor.lineTextForBufferRow(0)).toBe('var = () {') }) @@ -4022,19 +4549,25 @@ describe('TextEditor', () => { editor.deleteToBeginningOfWord() expect(buffer.lineForRow(1)).toBe(' var sort = function(ems) {') - expect(buffer.lineForRow(3)).toBe(' ar pivot = items.shift(), current, left = [], right = [];') + expect(buffer.lineForRow(3)).toBe( + ' ar pivot = items.shift(), current, left = [], right = [];' + ) expect(cursor1.getBufferPosition()).toEqual([1, 22]) expect(cursor2.getBufferPosition()).toEqual([3, 4]) editor.deleteToBeginningOfWord() expect(buffer.lineForRow(1)).toBe(' var sort = functionems) {') - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return itemsar pivot = items.shift(), current, left = [], right = [];') + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return itemsar pivot = items.shift(), current, left = [], right = [];' + ) expect(cursor1.getBufferPosition()).toEqual([1, 21]) expect(cursor2.getBufferPosition()).toEqual([2, 39]) editor.deleteToBeginningOfWord() expect(buffer.lineForRow(1)).toBe(' var sort = ems) {') - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return ar pivot = items.shift(), current, left = [], right = [];') + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return ar pivot = items.shift(), current, left = [], right = [];' + ) expect(cursor1.getBufferPosition()).toEqual([1, 13]) expect(cursor2.getBufferPosition()).toEqual([2, 34]) @@ -4050,7 +4583,9 @@ describe('TextEditor', () => { editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]]) editor.deleteToBeginningOfWord() expect(buffer.lineForRow(1)).toBe(' var sort = function(it) {') - expect(buffer.lineForRow(2)).toBe('if (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + 'if (items.length <= 1) return items;' + ) }) }) }) @@ -4073,7 +4608,9 @@ describe('TextEditor', () => { it('deletes the next newline', () => { editor.setCursorBufferPosition([1, 30]) editor.deleteToEndOfLine() - expect(buffer.lineForRow(1)).toBe(' var sort = function(items) { if (items.length <= 1) return items;') + expect(buffer.lineForRow(1)).toBe( + ' var sort = function(items) { if (items.length <= 1) return items;' + ) }) }) }) @@ -4083,7 +4620,9 @@ describe('TextEditor', () => { editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]]) editor.deleteToEndOfLine() expect(buffer.lineForRow(1)).toBe(' var sort = function(it) {') - expect(buffer.lineForRow(2)).toBe('if (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + 'if (items.length <= 1) return items;' + ) }) }) }) @@ -4097,7 +4636,9 @@ describe('TextEditor', () => { editor.deleteToBeginningOfLine() expect(buffer.lineForRow(1)).toBe('ems) {') - expect(buffer.lineForRow(2)).toBe('f (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + 'f (items.length <= 1) return items;' + ) expect(cursor1.getBufferPosition()).toEqual([1, 0]) expect(cursor2.getBufferPosition()).toEqual([2, 0]) }) @@ -4106,7 +4647,9 @@ describe('TextEditor', () => { it('deletes the newline', () => { editor.setCursorBufferPosition([2]) editor.deleteToBeginningOfLine() - expect(buffer.lineForRow(1)).toBe(' var sort = function(items) { if (items.length <= 1) return items;') + expect(buffer.lineForRow(1)).toBe( + ' var sort = function(items) { if (items.length <= 1) return items;' + ) }) }) }) @@ -4116,7 +4659,9 @@ describe('TextEditor', () => { editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]]) editor.deleteToBeginningOfLine() expect(buffer.lineForRow(1)).toBe('ems) {') - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) }) }) }) @@ -4135,7 +4680,9 @@ describe('TextEditor', () => { it('joins the line with the following line', () => { editor.setCursorScreenPosition([1, buffer.lineForRow(1).length]) editor.delete() - expect(buffer.lineForRow(1)).toBe(' var sort = function(items) { if (items.length <= 1) return items;') + expect(buffer.lineForRow(1)).toBe( + ' var sort = function(items) { if (items.length <= 1) return items;' + ) }) }) @@ -4157,7 +4704,9 @@ describe('TextEditor', () => { expect(buffer.lineForRow(3)).toBe(' vae(items.length > 0) {') expect(buffer.lineForRow(4)).toBe(' current = items.shift();') - expect(editor.getCursorScreenPosition()).toEqual(cursorPositionBefore) + expect(editor.getCursorScreenPosition()).toEqual( + cursorPositionBefore + ) }) }) @@ -4165,11 +4714,11 @@ describe('TextEditor', () => { it('deletes as normal', () => { editor.foldBufferRow(4) editor.setCursorScreenPosition([3, 4]) - const cursorPositionBefore = editor.getCursorScreenPosition() - editor.delete() - expect(buffer.lineForRow(3)).toBe(' ar pivot = items.shift(), current, left = [], right = [];') + expect(buffer.lineForRow(3)).toBe( + ' ar pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.isFoldedAtScreenRow(4)).toBe(true) expect(editor.getCursorScreenPosition()).toEqual([3, 4]) }) @@ -4182,9 +4731,15 @@ describe('TextEditor', () => { editor.delete() - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return items;') - expect(buffer.lineForRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(buffer.lineForRow(4)).toBe(' while ? left.push(current) : right.push(current);') + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(buffer.lineForRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(buffer.lineForRow(4)).toBe( + ' while ? left.push(current) : right.push(current);' + ) expect(buffer.lineForRow(5)).toBe(' }') expect(editor.getCursorBufferPosition()).toEqual([4, 9]) }) @@ -4199,7 +4754,9 @@ describe('TextEditor', () => { editor.delete() - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot= items.shift(), current left = [], right = [];') + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot= items.shift(), current left = [], right = [];' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([3, 13]) @@ -4219,8 +4776,12 @@ describe('TextEditor', () => { editor.delete() - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot= items.shift(), current, left = [], right = [];') - expect(editor.lineTextForBufferRow(4)).toBe(' while(tems.length > 0) {') + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot= items.shift(), current, left = [], right = [];' + ) + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(tems.length > 0) {' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([3, 13]) @@ -4229,8 +4790,7 @@ describe('TextEditor', () => { const [selection1, selection2] = editor.getSelections() expect(selection1.isEmpty()).toBeTruthy() expect(selection2.isEmpty()).toBeTruthy() - }) - ) + })) describe('when the cursors are at the end of their lines', () => it('removes the newlines following each cursor', () => { @@ -4239,13 +4799,14 @@ describe('TextEditor', () => { editor.delete() - expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () { var sort = function(items) { if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(0)).toBe( + 'var quicksort = function () { var sort = function(items) { if (items.length <= 1) return items;' + ) const [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual([0, 29]) expect(cursor2.getBufferPosition()).toEqual([0, 59]) - }) - ) + })) }) }) @@ -4254,7 +4815,9 @@ describe('TextEditor', () => { editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]]) editor.delete() expect(buffer.lineForRow(1)).toBe(' var sort = function(it) {') - expect(buffer.lineForRow(2)).toBe('if (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + 'if (items.length <= 1) return items;' + ) expect(editor.getLastSelection().isEmpty()).toBeTruthy() }) }) @@ -4262,12 +4825,14 @@ describe('TextEditor', () => { describe('when there are multiple selections', () => describe('when selections are on the same line', () => { it('removes all selected text', () => { - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[0, 16], [0, 24]]]) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[0, 16], [0, 24]] + ]) editor.delete() expect(editor.lineTextForBufferRow(0)).toBe('var = () {') }) - }) - ) + })) }) describe('.deleteToEndOfWord()', () => { @@ -4279,13 +4844,17 @@ describe('TextEditor', () => { editor.deleteToEndOfWord() expect(buffer.lineForRow(1)).toBe(' var sort = function(it) {') - expect(buffer.lineForRow(2)).toBe(' i (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + ' i (items.length <= 1) return items;' + ) expect(cursor1.getBufferPosition()).toEqual([1, 24]) expect(cursor2.getBufferPosition()).toEqual([2, 5]) editor.deleteToEndOfWord() expect(buffer.lineForRow(1)).toBe(' var sort = function(it {') - expect(buffer.lineForRow(2)).toBe(' iitems.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + ' iitems.length <= 1) return items;' + ) expect(cursor1.getBufferPosition()).toEqual([1, 24]) expect(cursor2.getBufferPosition()).toEqual([2, 5]) }) @@ -4333,8 +4902,7 @@ describe('TextEditor', () => { expect(buffer.lineForRow(0)).not.toMatch(/^\t/) editor.indent() expect(buffer.lineForRow(0)).toMatch(/^\t/) - }) - ) + })) }) describe('when autoIndent is enabled', () => { @@ -4343,7 +4911,7 @@ describe('TextEditor', () => { it('moves the cursor to the end of the leading whitespace and inserts enough whitespace to bring the line to the suggested level of indentation', () => { buffer.insert([5, 0], ' \n') editor.setCursorBufferPosition([5, 0]) - editor.indent({autoIndent: true}) + editor.indent({ autoIndent: true }) expect(buffer.lineForRow(5)).toMatch(/^\s+$/) expect(buffer.lineForRow(5).length).toBe(6) expect(editor.getCursorBufferPosition()).toEqual([5, 6]) @@ -4353,14 +4921,14 @@ describe('TextEditor', () => { editor.setTabLength(4) buffer.insert([12, 2], '\n ') editor.setCursorBufferPosition([13, 1]) - editor.indent({autoIndent: true}) + editor.indent({ autoIndent: true }) expect(buffer.lineForRow(13)).toMatch(/^\s+$/) expect(buffer.lineForRow(13).length).toBe(4) expect(editor.getCursorBufferPosition()).toEqual([13, 4]) buffer.insert([13, 0], ' ') editor.setCursorBufferPosition([13, 6]) - editor.indent({autoIndent: true}) + editor.indent({ autoIndent: true }) expect(buffer.lineForRow(13).length).toBe(8) }) }) @@ -4371,7 +4939,7 @@ describe('TextEditor', () => { editor.setSoftTabs(false) buffer.insert([5, 0], '\t\n') editor.setCursorBufferPosition([5, 0]) - editor.indent({autoIndent: true}) + editor.indent({ autoIndent: true }) expect(buffer.lineForRow(5)).toMatch(/^\t\t\t$/) expect(editor.getCursorBufferPosition()).toEqual([5, 3]) }) @@ -4382,11 +4950,10 @@ describe('TextEditor', () => { buffer.setText(' \ntest') editor.setCursorBufferPosition([1, 0]) - editor.indent({autoIndent: true}) + editor.indent({ autoIndent: true }) expect(buffer.lineForRow(1)).toBe('\ttest') expect(editor.getCursorBufferPosition()).toEqual([1, 1]) - }) - ) + })) }) }) @@ -4395,12 +4962,11 @@ describe('TextEditor', () => { it("moves the cursor to the end of the leading whitespace and inserts 'tabLength' spaces into the buffer", () => { buffer.insert([7, 0], ' \n') editor.setCursorBufferPosition([7, 2]) - editor.indent({autoIndent: true}) + editor.indent({ autoIndent: true }) expect(buffer.lineForRow(7)).toMatch(/^\s+$/) expect(buffer.lineForRow(7).length).toBe(8) expect(editor.getCursorBufferPosition()).toEqual([7, 8]) - }) - ) + })) describe("when 'softTabs' is false", () => it('moves the cursor to the end of the leading whitespace and inserts \t into the buffer', () => { @@ -4408,11 +4974,10 @@ describe('TextEditor', () => { editor.setSoftTabs(false) buffer.insert([7, 0], '\t\t\t\n') editor.setCursorBufferPosition([7, 1]) - editor.indent({autoIndent: true}) + editor.indent({ autoIndent: true }) expect(buffer.lineForRow(7)).toMatch(/^\t\t\t\t$/) expect(editor.getCursorBufferPosition()).toEqual([7, 4]) - }) - ) + })) }) }) }) @@ -4434,12 +4999,18 @@ describe('TextEditor', () => { editor.indent() expect(buffer.lineForRow(0)).toMatch(/^\t/) expect(editor.getCursorBufferPosition()).toEqual([0, 1]) - expect(editor.getCursorScreenPosition()).toEqual([0, editor.getTabLength()]) + expect(editor.getCursorScreenPosition()).toEqual([ + 0, + editor.getTabLength() + ]) editor.indent() expect(buffer.lineForRow(0)).toMatch(/^\t\t/) expect(editor.getCursorBufferPosition()).toEqual([0, 2]) - expect(editor.getCursorScreenPosition()).toEqual([0, editor.getTabLength() * 2]) + expect(editor.getCursorScreenPosition()).toEqual([ + 0, + editor.getTabLength() * 2 + ]) }) }) }) @@ -4456,23 +5027,26 @@ describe('TextEditor', () => { describe('when no text is selected', () => { beforeEach(() => - editor.setSelectedBufferRanges([ - [[0, 0], [0, 0]], - [[5, 0], [5, 0]] - ]) + editor.setSelectedBufferRanges([[[0, 0], [0, 0]], [[5, 0], [5, 0]]]) ) it('cuts the lines on which there are cursors', () => { editor.cutSelectedText() expect(buffer.getLineCount()).toBe(11) - expect(buffer.lineForRow(1)).toBe(' if (items.length <= 1) return items;') - expect(buffer.lineForRow(4)).toBe(' current < pivot ? left.push(current) : right.push(current);') - expect(atom.clipboard.read()).toEqual([ - 'var quicksort = function () {', - '', - ' current = items.shift();', - '' - ].join('\n')) + expect(buffer.lineForRow(1)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(buffer.lineForRow(4)).toBe( + ' current < pivot ? left.push(current) : right.push(current);' + ) + expect(atom.clipboard.read()).toEqual( + [ + 'var quicksort = function () {', + '', + ' current = items.shift();', + '' + ].join('\n') + ) }) }) @@ -4497,7 +5071,9 @@ describe('TextEditor', () => { editor.setEditorWidthInChars(25) editor.setCursorScreenPosition([2, 6]) editor.cutToEndOfLine() - expect(editor.lineTextForScreenRow(2)).toBe(' var function(items) {') + expect(editor.lineTextForScreenRow(2)).toBe( + ' var function(items) {' + ) }) }) @@ -4509,19 +5085,26 @@ describe('TextEditor', () => { editor.cutToEndOfLine() expect(buffer.lineForRow(2)).toBe(' if (items.length') expect(buffer.lineForRow(3)).toBe(' var pivot = item') - expect(atom.clipboard.read()).toBe(' <= 1) return items;\ns.shift(), current, left = [], right = [];') - }) - ) + expect(atom.clipboard.read()).toBe( + ' <= 1) return items;\ns.shift(), current, left = [], right = [];' + ) + })) describe('when text is selected', () => it('only cuts the selected text, not to the end of the line', () => { - editor.setSelectedBufferRanges([[[2, 20], [2, 30]], [[3, 20], [3, 20]]]) + editor.setSelectedBufferRanges([ + [[2, 20], [2, 30]], + [[3, 20], [3, 20]] + ]) editor.cutToEndOfLine() - expect(buffer.lineForRow(2)).toBe(' if (items.lengthurn items;') + expect(buffer.lineForRow(2)).toBe( + ' if (items.lengthurn items;' + ) expect(buffer.lineForRow(3)).toBe(' var pivot = item') - expect(atom.clipboard.read()).toBe(' <= 1) ret\ns.shift(), current, left = [], right = [];') - }) - ) + expect(atom.clipboard.read()).toBe( + ' <= 1) ret\ns.shift(), current, left = [], right = [];' + ) + })) }) }) @@ -4538,47 +5121,59 @@ describe('TextEditor', () => { editor.cutToEndOfBufferLine() expect(buffer.lineForRow(2)).toBe(' if (items.length') expect(buffer.lineForRow(3)).toBe(' var pivot = item') - expect(atom.clipboard.read()).toBe(' <= 1) return items;\ns.shift(), current, left = [], right = [];') + expect(atom.clipboard.read()).toBe( + ' <= 1) return items;\ns.shift(), current, left = [], right = [];' + ) }) }) describe('when text is selected', () => { it('only cuts the selected text, not to the end of the buffer line', () => { - editor.setSelectedBufferRanges([[[2, 20], [2, 30]], [[3, 20], [3, 20]]]) + editor.setSelectedBufferRanges([ + [[2, 20], [2, 30]], + [[3, 20], [3, 20]] + ]) editor.cutToEndOfBufferLine() expect(buffer.lineForRow(2)).toBe(' if (items.lengthurn items;') expect(buffer.lineForRow(3)).toBe(' var pivot = item') - expect(atom.clipboard.read()).toBe(' <= 1) ret\ns.shift(), current, left = [], right = [];') + expect(atom.clipboard.read()).toBe( + ' <= 1) ret\ns.shift(), current, left = [], right = [];' + ) }) }) }) describe('.copySelectedText()', () => { it('copies selected text onto the clipboard', () => { - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]], [[2, 8], [2, 13]]]) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[1, 6], [1, 10]], + [[2, 8], [2, 13]] + ]) editor.copySelectedText() - expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') + expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') expect(buffer.lineForRow(1)).toBe(' var sort = function(items) {') - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) expect(clipboard.readText()).toBe('quicksort\nsort\nitems') expect(atom.clipboard.read()).toEqual('quicksort\nsort\nitems') }) describe('when no text is selected', () => { beforeEach(() => { - editor.setSelectedBufferRanges([ - [[1, 5], [1, 5]], - [[5, 8], [5, 8]] - ]) + editor.setSelectedBufferRanges([[[1, 5], [1, 5]], [[5, 8], [5, 8]]]) }) it('copies the lines on which there are cursors', () => { editor.copySelectedText() - expect(atom.clipboard.read()).toEqual([ - ' var sort = function(items) {\n', - ' current = items.shift();\n' - ].join('\n')) + expect(atom.clipboard.read()).toEqual( + [ + ' var sort = function(items) {\n', + ' current = items.shift();\n' + ].join('\n') + ) expect(editor.getSelectedBufferRanges()).toEqual([ [[1, 5], [1, 5]], [[5, 8], [5, 8]] @@ -4602,12 +5197,18 @@ describe('TextEditor', () => { describe('.copyOnlySelectedText()', () => { describe('when thee are multiple selections', () => { it('copies selected text onto the clipboard', () => { - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]], [[2, 8], [2, 13]]]) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[1, 6], [1, 10]], + [[2, 8], [2, 13]] + ]) editor.copyOnlySelectedText() expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') expect(buffer.lineForRow(1)).toBe(' var sort = function(items) {') - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return items;') + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) expect(clipboard.readText()).toBe('quicksort\nsort\nitems') expect(atom.clipboard.read()).toEqual(`quicksort\nsort\nitems`) }) @@ -4623,28 +5224,21 @@ describe('TextEditor', () => { }) describe('.pasteText()', () => { - const copyText = function (text, {startColumn, textEditor} = {}) { - if (startColumn == null) startColumn = 0 - if (textEditor == null) textEditor = editor - textEditor.setCursorBufferPosition([0, 0]) - textEditor.insertText(text) - const numberOfNewlines = text.match(/\n/g).length - const endColumn = text.match(/[^\n]*$/)[0].length - textEditor.getLastSelection().setBufferRange([[0, startColumn], [numberOfNewlines, endColumn]]) - return textEditor.cutSelectedText() - } - it('pastes text into the buffer', () => { editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) atom.clipboard.write('first') editor.pasteText() - expect(editor.lineTextForBufferRow(0)).toBe('var first = function () {') - expect(editor.lineTextForBufferRow(1)).toBe(' var first = function(items) {') + expect(editor.lineTextForBufferRow(0)).toBe( + 'var first = function () {' + ) + expect(editor.lineTextForBufferRow(1)).toBe( + ' var first = function(items) {' + ) }) it('notifies ::onWillInsertText observers', () => { const insertedStrings = [] - editor.onWillInsertText(function ({text, cancel}) { + editor.onWillInsertText(function ({ text, cancel }) { insertedStrings.push(text) cancel() }) @@ -4657,7 +5251,9 @@ describe('TextEditor', () => { it('notifies ::onDidInsertText observers', () => { const insertedStrings = [] - editor.onDidInsertText(({text, range}) => insertedStrings.push(text)) + editor.onDidInsertText(({ text, range }) => + insertedStrings.push(text) + ) atom.clipboard.write('hello') editor.pasteText() @@ -4666,11 +5262,13 @@ describe('TextEditor', () => { }) describe('when `autoIndentOnPaste` is true', () => { - beforeEach(() => editor.update({autoIndentOnPaste: true})) + beforeEach(() => editor.update({ autoIndentOnPaste: true })) describe('when pasting multiple lines before any non-whitespace characters', () => { it('auto-indents the lines spanned by the pasted text, based on the first pasted line', () => { - atom.clipboard.write('a(x);\n b(x);\n c(x);\n', {indentBasis: 0}) + atom.clipboard.write('a(x);\n b(x);\n c(x);\n', { + indentBasis: 0 + }) editor.setCursorBufferPosition([5, 0]) editor.pasteText() @@ -4680,14 +5278,18 @@ describe('TextEditor', () => { expect(editor.lineTextForBufferRow(5)).toBe(' a(x);') expect(editor.lineTextForBufferRow(6)).toBe(' b(x);') expect(editor.lineTextForBufferRow(7)).toBe(' c(x);') - expect(editor.lineTextForBufferRow(8)).toBe(' current = items.shift();') + expect(editor.lineTextForBufferRow(8)).toBe( + ' current = items.shift();' + ) }) it('auto-indents lines with a mix of hard tabs and spaces without removing spaces', () => { editor.setSoftTabs(false) expect(editor.indentationForBufferRow(5)).toBe(3) - atom.clipboard.write('/**\n\t * testing\n\t * indent\n\t **/\n', {indentBasis: 1}) + atom.clipboard.write('/**\n\t * testing\n\t * indent\n\t **/\n', { + indentBasis: 1 + }) editor.setCursorBufferPosition([5, 0]) editor.pasteText() @@ -4701,7 +5303,9 @@ describe('TextEditor', () => { describe('when pasting line(s) above a line that matches the decreaseIndentPattern', () => it('auto-indents based on the pasted line(s) only', () => { - atom.clipboard.write('a(x);\n b(x);\n c(x);\n', {indentBasis: 0}) + atom.clipboard.write('a(x);\n b(x);\n c(x);\n', { + indentBasis: 0 + }) editor.setCursorBufferPosition([7, 0]) editor.pasteText() @@ -4709,19 +5313,21 @@ describe('TextEditor', () => { expect(editor.lineTextForBufferRow(8)).toBe(' b(x);') expect(editor.lineTextForBufferRow(9)).toBe(' c(x);') expect(editor.lineTextForBufferRow(10)).toBe(' }') - }) - ) + })) describe('when pasting a line of text without line ending', () => it('does not auto-indent the text', () => { - atom.clipboard.write('a(x);', {indentBasis: 0}) + atom.clipboard.write('a(x);', { indentBasis: 0 }) editor.setCursorBufferPosition([5, 0]) editor.pasteText() - expect(editor.lineTextForBufferRow(5)).toBe('a(x); current = items.shift();') - expect(editor.lineTextForBufferRow(6)).toBe(' current < pivot ? left.push(current) : right.push(current);') - }) - ) + expect(editor.lineTextForBufferRow(5)).toBe( + 'a(x); current = items.shift();' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' current < pivot ? left.push(current) : right.push(current);' + ) + })) describe('when pasting on a line after non-whitespace characters', () => it('does not auto-indent the affected line', () => { @@ -4739,12 +5345,11 @@ describe('TextEditor', () => { editor.pasteText() expect(editor.lineTextForBufferRow(1)).toBe(' y(); z();') expect(editor.lineTextForBufferRow(2)).toBe(' h();') - }) - ) + })) }) describe('when `autoIndentOnPaste` is false', () => { - beforeEach(() => editor.update({autoIndentOnPaste: false})) + beforeEach(() => editor.update({ autoIndentOnPaste: false })) describe('when the cursor is indented further than the original copied text', () => it('increases the indentation of the copied lines to match', () => { @@ -4754,10 +5359,13 @@ describe('TextEditor', () => { editor.setCursorBufferPosition([5, 6]) editor.pasteText() - expect(editor.lineTextForBufferRow(5)).toBe(' var sort = function(items) {') - expect(editor.lineTextForBufferRow(6)).toBe(' if (items.length <= 1) return items;') - }) - ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' var sort = function(items) {' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' if (items.length <= 1) return items;' + ) + })) describe('when the cursor is indented less far than the original copied text', () => it('decreases the indentation of the copied lines to match', () => { @@ -4767,10 +5375,11 @@ describe('TextEditor', () => { editor.setCursorBufferPosition([1, 2]) editor.pasteText() - expect(editor.lineTextForBufferRow(1)).toBe(' current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(1)).toBe( + ' current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(2)).toBe('}') - }) - ) + })) describe('when the first copied line has leading whitespace', () => it("preserves the line's leading whitespace", () => { @@ -4780,16 +5389,22 @@ describe('TextEditor', () => { editor.setCursorBufferPosition([0, 0]) editor.pasteText() - expect(editor.lineTextForBufferRow(0)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(1)).toBe(' current = items.shift();') - }) - ) + expect(editor.lineTextForBufferRow(0)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(1)).toBe( + ' current = items.shift();' + ) + })) }) describe('when the clipboard has many selections', () => { beforeEach(() => { - editor.update({autoIndentOnPaste: false}) - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + editor.update({ autoIndentOnPaste: false }) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[1, 6], [1, 10]] + ]) editor.copySelectedText() }) @@ -4802,17 +5417,25 @@ describe('TextEditor', () => { editor.moveRight() editor.insertText('_') editor.pasteText() - expect(editor.lineTextForBufferRow(0)).toBe('var quicksort_quicksort = function () {') - expect(editor.lineTextForBufferRow(1)).toBe(' var sort_sort = function(items) {') + expect(editor.lineTextForBufferRow(0)).toBe( + 'var quicksort_quicksort = function () {' + ) + expect(editor.lineTextForBufferRow(1)).toBe( + ' var sort_sort = function(items) {' + ) }) describe('and the selections count does not match', () => { - beforeEach(() => editor.setSelectedBufferRanges([[[0, 4], [0, 13]]])) + beforeEach(() => + editor.setSelectedBufferRanges([[[0, 4], [0, 13]]]) + ) it('pastes the whole text into the buffer', () => { editor.pasteText() expect(editor.lineTextForBufferRow(0)).toBe('var quicksort') - expect(editor.lineTextForBufferRow(1)).toBe('sort = function () {') + expect(editor.lineTextForBufferRow(1)).toBe( + 'sort = function () {' + ) }) }) }) @@ -4826,8 +5449,12 @@ describe('TextEditor', () => { it("pastes the line above the cursor and retains the cursor's column", () => { editor.pasteText() - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.getCursorBufferPosition()).toEqual([3, 13]) }) }) @@ -4842,35 +5469,47 @@ describe('TextEditor', () => { it('overwrites the selection as with any copied text', () => { editor.setSelectedBufferRange([[1, 2], [1, Infinity]]) editor.pasteText() - expect(editor.lineTextForBufferRow(1)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(1)).toBe( + ' if (items.length <= 1) return items;' + ) expect(editor.lineTextForBufferRow(2)).toBe('') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) expect(editor.getCursorBufferPosition()).toEqual([2, 0]) - }) - ) + })) describe('when there is no selection', () => it("pastes the line above the cursor and retains the cursor's column", () => { editor.pasteText() - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') - expect(editor.lineTextForBufferRow(3)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(editor.lineTextForBufferRow(3)).toBe( + ' if (items.length <= 1) return items;' + ) expect(editor.getCursorBufferPosition()).toEqual([3, 13]) - }) - ) + })) }) it('respects options that preserve the formatting of the pasted text', () => { - editor.update({autoIndentOnPaste: true}) - atom.clipboard.write('a(x);\n b(x);\r\nc(x);\n', {indentBasis: 0}) + editor.update({ autoIndentOnPaste: true }) + atom.clipboard.write('a(x);\n b(x);\r\nc(x);\n', { indentBasis: 0 }) editor.setCursorBufferPosition([5, 0]) editor.insertText(' ') - editor.pasteText({autoIndent: false, preserveTrailingLineIndentation: true, normalizeLineEndings: false}) + editor.pasteText({ + autoIndent: false, + preserveTrailingLineIndentation: true, + normalizeLineEndings: false + }) expect(editor.lineTextForBufferRow(5)).toBe(' a(x);') expect(editor.lineTextForBufferRow(6)).toBe(' b(x);') expect(editor.buffer.lineEndingForRow(6)).toBe('\r\n') expect(editor.lineTextForBufferRow(7)).toBe('c(x);') - expect(editor.lineTextForBufferRow(8)).toBe(' current = items.shift();') + expect(editor.lineTextForBufferRow(8)).toBe( + ' current = items.shift();' + ) }) }) }) @@ -4882,7 +5521,10 @@ describe('TextEditor', () => { editor.setSelectedBufferRange([[0, 3], [0, 3]]) editor.indentSelectedRows() expect(buffer.lineForRow(0)).toBe(' var quicksort = function () {') - expect(editor.getSelectedBufferRange()).toEqual([[0, 3 + editor.getTabLength()], [0, 3 + editor.getTabLength()]]) + expect(editor.getSelectedBufferRange()).toEqual([ + [0, 3 + editor.getTabLength()], + [0, 3 + editor.getTabLength()] + ]) }) }) @@ -4893,7 +5535,10 @@ describe('TextEditor', () => { editor.setSelectedBufferRange([[0, 3], [0, 3]]) editor.indentSelectedRows() expect(buffer.lineForRow(0)).toBe('\tvar quicksort = function () {') - expect(editor.getSelectedBufferRange()).toEqual([[0, 3 + 1], [0, 3 + 1]]) + expect(editor.getSelectedBufferRange()).toEqual([ + [0, 3 + 1], + [0, 3 + 1] + ]) }) }) }) @@ -4903,8 +5548,13 @@ describe('TextEditor', () => { it('indents line and retains selection', () => { editor.setSelectedBufferRange([[0, 4], [0, 14]]) editor.indentSelectedRows() - expect(buffer.lineForRow(0)).toBe(`${editor.getTabText()}var quicksort = function () {`) - expect(editor.getSelectedBufferRange()).toEqual([[0, 4 + editor.getTabLength()], [0, 14 + editor.getTabLength()]]) + expect(buffer.lineForRow(0)).toBe( + `${editor.getTabText()}var quicksort = function () {` + ) + expect(editor.getSelectedBufferRange()).toEqual([ + [0, 4 + editor.getTabLength()], + [0, 14 + editor.getTabLength()] + ]) }) }) @@ -4915,7 +5565,10 @@ describe('TextEditor', () => { editor.setSelectedBufferRange([[0, 4], [0, 14]]) editor.indentSelectedRows() expect(buffer.lineForRow(0)).toBe('\tvar quicksort = function () {') - expect(editor.getSelectedBufferRange()).toEqual([[0, 4 + 1], [0, 14 + 1]]) + expect(editor.getSelectedBufferRange()).toEqual([ + [0, 4 + 1], + [0, 14 + 1] + ]) }) }) }) @@ -4927,8 +5580,13 @@ describe('TextEditor', () => { editor.indentSelectedRows() expect(buffer.lineForRow(9)).toBe(' };') expect(buffer.lineForRow(10)).toBe('') - expect(buffer.lineForRow(11)).toBe(' return sort(Array.apply(this, arguments));') - expect(editor.getSelectedBufferRange()).toEqual([[9, 1 + editor.getTabLength()], [11, 15 + editor.getTabLength()]]) + expect(buffer.lineForRow(11)).toBe( + ' return sort(Array.apply(this, arguments));' + ) + expect(editor.getSelectedBufferRange()).toEqual([ + [9, 1 + editor.getTabLength()], + [11, 15 + editor.getTabLength()] + ]) }) it('does not indent the last row if the selection ends at column 0', () => { @@ -4936,8 +5594,13 @@ describe('TextEditor', () => { editor.indentSelectedRows() expect(buffer.lineForRow(9)).toBe(' };') expect(buffer.lineForRow(10)).toBe('') - expect(buffer.lineForRow(11)).toBe(' return sort(Array.apply(this, arguments));') - expect(editor.getSelectedBufferRange()).toEqual([[9, 1 + editor.getTabLength()], [11, 0]]) + expect(buffer.lineForRow(11)).toBe( + ' return sort(Array.apply(this, arguments));' + ) + expect(editor.getSelectedBufferRange()).toEqual([ + [9, 1 + editor.getTabLength()], + [11, 0] + ]) }) }) @@ -4949,8 +5612,13 @@ describe('TextEditor', () => { editor.indentSelectedRows() expect(buffer.lineForRow(9)).toBe('\t\t};') expect(buffer.lineForRow(10)).toBe('') - expect(buffer.lineForRow(11)).toBe('\t\treturn sort(Array.apply(this, arguments));') - expect(editor.getSelectedBufferRange()).toEqual([[9, 1 + 1], [11, 15 + 1]]) + expect(buffer.lineForRow(11)).toBe( + '\t\treturn sort(Array.apply(this, arguments));' + ) + expect(editor.getSelectedBufferRange()).toEqual([ + [9, 1 + 1], + [11, 15 + 1] + ]) }) }) }) @@ -4962,7 +5630,10 @@ describe('TextEditor', () => { editor.setSelectedBufferRange([[1, 3], [1, 3]]) editor.outdentSelectedRows() expect(buffer.lineForRow(1)).toBe('var sort = function(items) {') - expect(editor.getSelectedBufferRange()).toEqual([[1, 3 - editor.getTabLength()], [1, 3 - editor.getTabLength()]]) + expect(editor.getSelectedBufferRange()).toEqual([ + [1, 3 - editor.getTabLength()], + [1, 3 - editor.getTabLength()] + ]) }) it('outdents when indent is less than a tab length', () => { @@ -4992,11 +5663,17 @@ describe('TextEditor', () => { it('outdents only up to the first non-space non-tab character', () => { editor.insertText(' \tfoo\t ') editor.outdentSelectedRows() - expect(buffer.lineForRow(0)).toBe('\tfoo\t var quicksort = function () {') + expect(buffer.lineForRow(0)).toBe( + '\tfoo\t var quicksort = function () {' + ) editor.outdentSelectedRows() - expect(buffer.lineForRow(0)).toBe('foo\t var quicksort = function () {') + expect(buffer.lineForRow(0)).toBe( + 'foo\t var quicksort = function () {' + ) editor.outdentSelectedRows() - expect(buffer.lineForRow(0)).toBe('foo\t var quicksort = function () {') + expect(buffer.lineForRow(0)).toBe( + 'foo\t var quicksort = function () {' + ) }) }) @@ -5005,7 +5682,10 @@ describe('TextEditor', () => { editor.setSelectedBufferRange([[1, 4], [1, 14]]) editor.outdentSelectedRows() expect(buffer.lineForRow(1)).toBe('var sort = function(items) {') - expect(editor.getSelectedBufferRange()).toEqual([[1, 4 - editor.getTabLength()], [1, 14 - editor.getTabLength()]]) + expect(editor.getSelectedBufferRange()).toEqual([ + [1, 4 - editor.getTabLength()], + [1, 14 - editor.getTabLength()] + ]) }) }) @@ -5015,9 +5695,16 @@ describe('TextEditor', () => { editor.outdentSelectedRows() expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') expect(buffer.lineForRow(1)).toBe('var sort = function(items) {') - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return items;') - expect(buffer.lineForRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') - expect(editor.getSelectedBufferRange()).toEqual([[0, 1], [3, 15 - editor.getTabLength()]]) + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(buffer.lineForRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) + expect(editor.getSelectedBufferRange()).toEqual([ + [0, 1], + [3, 15 - editor.getTabLength()] + ]) }) it('does not outdent the last line of the selection if it ends at column 0', () => { @@ -5025,8 +5712,12 @@ describe('TextEditor', () => { editor.outdentSelectedRows() expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') expect(buffer.lineForRow(1)).toBe('var sort = function(items) {') - expect(buffer.lineForRow(2)).toBe(' if (items.length <= 1) return items;') - expect(buffer.lineForRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(buffer.lineForRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) + expect(buffer.lineForRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) expect(editor.getSelectedBufferRange()).toEqual([[0, 1], [3, 0]]) }) @@ -5079,10 +5770,7 @@ describe('TextEditor', () => { }) it('restores cursors and selections to their states before and after undone and redone changes', () => { - editor.setSelectedBufferRanges([ - [[0, 0], [0, 0]], - [[1, 0], [1, 3]] - ]) + editor.setSelectedBufferRanges([[[0, 0], [0, 0]], [[1, 0], [1, 3]]]) editor.insertText('abc') expect(editor.getSelectedBufferRanges()).toEqual([ @@ -5141,24 +5829,37 @@ describe('TextEditor', () => { editor.delete() editor.delete() - const selections = editor.getSelections() expect(buffer.lineForRow(1)).toBe(' var = function( {') - expect(editor.getSelectedBufferRanges()).toEqual([[[1, 6], [1, 6]], [[1, 17], [1, 17]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[1, 6], [1, 6]], + [[1, 17], [1, 17]] + ]) editor.undo() - expect(editor.getSelectedBufferRanges()).toEqual([[[1, 6], [1, 6]], [[1, 18], [1, 18]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[1, 6], [1, 6]], + [[1, 18], [1, 18]] + ]) editor.undo() - expect(editor.getSelectedBufferRanges()).toEqual([[[1, 6], [1, 10]], [[1, 22], [1, 27]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[1, 6], [1, 10]], + [[1, 22], [1, 27]] + ]) editor.redo() - expect(editor.getSelectedBufferRanges()).toEqual([[[1, 6], [1, 6]], [[1, 18], [1, 18]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[1, 6], [1, 6]], + [[1, 18], [1, 18]] + ]) }) xit('restores folds after undo and redo', () => { editor.foldBufferRow(1) - editor.setSelectedBufferRange([[1, 0], [10, Infinity]], {preserveFolds: true}) + editor.setSelectedBufferRange([[1, 0], [10, Infinity]], { + preserveFolds: true + }) expect(editor.isFoldedAtBufferRow(1)).toBeTruthy() editor.insertText(dedent`\ @@ -5208,9 +5909,9 @@ describe('TextEditor', () => { beforeEach(async () => { editor1 = editor - editor2 = new TextEditor({buffer: editor1.buffer}) + editor2 = new TextEditor({ buffer: editor1.buffer }) - editor1.setText(dedent ` + editor1.setText(dedent` aaaaaa bbbbbb cccccc @@ -5220,12 +5921,16 @@ describe('TextEditor', () => { }) it('[editor.transact] restore selection of change-initiated-editor', () => { - editor1.setCursorBufferPosition([0, 0]); editor1.transact(() => editor1.insertText('1')) - editor2.setCursorBufferPosition([1, 0]); editor2.transact(() => editor2.insertText('2')) - editor1.setCursorBufferPosition([2, 0]); editor1.transact(() => editor1.insertText('3')) - editor2.setCursorBufferPosition([3, 0]); editor2.transact(() => editor2.insertText('4')) + editor1.setCursorBufferPosition([0, 0]) + editor1.transact(() => editor1.insertText('1')) + editor2.setCursorBufferPosition([1, 0]) + editor2.transact(() => editor2.insertText('2')) + editor1.setCursorBufferPosition([2, 0]) + editor1.transact(() => editor1.insertText('3')) + editor2.setCursorBufferPosition([3, 0]) + editor2.transact(() => editor2.insertText('4')) - expect(editor1.getText()).toBe(dedent ` + expect(editor1.getText()).toBe(dedent` 1aaaaaa 2bbbbbb 3cccccc @@ -5234,29 +5939,45 @@ describe('TextEditor', () => { `) editor2.setCursorBufferPosition([4, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([3, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([2, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([1, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([0, 0]) expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 1]) - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 1]) - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 1]) - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([0, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([1, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([2, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([3, 1]) expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged editor1.setCursorBufferPosition([4, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([3, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([2, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([1, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([0, 0]) expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 1]) - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 1]) - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 1]) - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([0, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([1, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([2, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([3, 1]) expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged }) @@ -5267,12 +5988,16 @@ describe('TextEditor', () => { editor.groupChangesSinceCheckpoint(checkpoint) } - editor1.setCursorBufferPosition([0, 0]); transact(editor1, () => editor1.insertText('1')) - editor2.setCursorBufferPosition([1, 0]); transact(editor2, () => editor2.insertText('2')) - editor1.setCursorBufferPosition([2, 0]); transact(editor1, () => editor1.insertText('3')) - editor2.setCursorBufferPosition([3, 0]); transact(editor2, () => editor2.insertText('4')) + editor1.setCursorBufferPosition([0, 0]) + transact(editor1, () => editor1.insertText('1')) + editor2.setCursorBufferPosition([1, 0]) + transact(editor2, () => editor2.insertText('2')) + editor1.setCursorBufferPosition([2, 0]) + transact(editor1, () => editor1.insertText('3')) + editor2.setCursorBufferPosition([3, 0]) + transact(editor2, () => editor2.insertText('4')) - expect(editor1.getText()).toBe(dedent ` + expect(editor1.getText()).toBe(dedent` 1aaaaaa 2bbbbbb 3cccccc @@ -5281,29 +6006,45 @@ describe('TextEditor', () => { `) editor2.setCursorBufferPosition([4, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 0]) - editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([3, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([2, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([1, 0]) + editor1.undo() + expect(editor1.getCursorBufferPosition()).toEqual([0, 0]) expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 1]) - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 1]) - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 1]) - editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([0, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([1, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([2, 1]) + editor1.redo() + expect(editor1.getCursorBufferPosition()).toEqual([3, 1]) expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged editor1.setCursorBufferPosition([4, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 0]) - editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([3, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([2, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([1, 0]) + editor2.undo() + expect(editor2.getCursorBufferPosition()).toEqual([0, 0]) expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 1]) - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 1]) - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 1]) - editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([0, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([1, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([2, 1]) + editor2.redo() + expect(editor2.getCursorBufferPosition()).toEqual([3, 1]) expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged }) }) @@ -5341,7 +6082,7 @@ describe('TextEditor', () => { editor.addCursorAtScreenPosition([0, 2]) editor.addCursorAtScreenPosition([1, 2]) - const [cursor1, cursor2, cursor3] = editor.getCursors() + const [cursor1, , cursor3] = editor.getCursors() expect(editor.getCursors().length).toBe(3) buffer.delete([[0, 0], [0, 2]]) @@ -5375,7 +6116,10 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('quicksort') expect(selections[1].getText()).toBe('function') - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 3], [0, 12]], [[0, 15], [0, 23]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 3], [0, 12]], + [[0, 15], [0, 23]] + ]) }) it('moves multiple active selections on multiple lines one column to the left', () => { @@ -5389,7 +6133,10 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('quicksort') expect(selections[1].getText()).toBe('sort') - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 3], [0, 12]], [[1, 5], [1, 9]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 3], [0, 12]], + [[1, 5], [1, 9]] + ]) }) describe('when a selection is at the first column of a line', () => { @@ -5405,12 +6152,18 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('var') expect(selections[1].getText()).toBe(' v') - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 3]], [[1, 0], [1, 3]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 0], [0, 3]], + [[1, 0], [1, 3]] + ]) }) describe('when multiple selections are active on one line', () => { it('does not change the selection', () => { - editor.setSelectedBufferRanges([[[0, 0], [0, 3]], [[0, 4], [0, 13]]]) + editor.setSelectedBufferRanges([ + [[0, 0], [0, 3]], + [[0, 4], [0, 13]] + ]) const selections = editor.getSelections() expect(selections[0].getText()).toBe('var') @@ -5420,7 +6173,10 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('var') expect(selections[1].getText()).toBe('quicksort') - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 0], [0, 3]], [[0, 4], [0, 13]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 0], [0, 3]], + [[0, 4], [0, 13]] + ]) }) }) }) @@ -5448,7 +6204,10 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('quicksort') expect(selections[1].getText()).toBe('function') - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 5], [0, 14]], [[0, 17], [0, 25]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 5], [0, 14]], + [[0, 17], [0, 25]] + ]) }) it('moves multiple active selections on multiple lines one column to the right', () => { @@ -5462,12 +6221,18 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('quicksort') expect(selections[1].getText()).toBe('sort') - expect(editor.getSelectedBufferRanges()).toEqual([[[0, 5], [0, 14]], [[1, 7], [1, 11]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[0, 5], [0, 14]], + [[1, 7], [1, 11]] + ]) }) describe('when a selection is at the last column of a line', () => { it('does not change the selection', () => { - editor.setSelectedBufferRanges([[[2, 34], [2, 40]], [[5, 22], [5, 30]]]) + editor.setSelectedBufferRanges([ + [[2, 34], [2, 40]], + [[5, 22], [5, 30]] + ]) const selections = editor.getSelections() expect(selections[0].getText()).toBe('items;') @@ -5478,12 +6243,18 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('items;') expect(selections[1].getText()).toBe('shift();') - expect(editor.getSelectedBufferRanges()).toEqual([[[2, 34], [2, 40]], [[5, 22], [5, 30]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[2, 34], [2, 40]], + [[5, 22], [5, 30]] + ]) }) describe('when multiple selections are active on one line', () => { it('does not change the selection', () => { - editor.setSelectedBufferRanges([[[2, 27], [2, 33]], [[2, 34], [2, 40]]]) + editor.setSelectedBufferRanges([ + [[2, 27], [2, 33]], + [[2, 34], [2, 40]] + ]) const selections = editor.getSelections() expect(selections[0].getText()).toBe('return') @@ -5493,7 +6264,10 @@ describe('TextEditor', () => { expect(selections[0].getText()).toBe('return') expect(selections[1].getText()).toBe('items;') - expect(editor.getSelectedBufferRanges()).toEqual([[[2, 27], [2, 33]], [[2, 34], [2, 40]]]) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[2, 27], [2, 33]], + [[2, 34], [2, 40]] + ]) }) }) }) @@ -5529,7 +6303,7 @@ describe('TextEditor', () => { { name: 'insertNewline', op: (opts = {}) => { - editor.setCursorScreenPosition({row: 1, column: 0}) + editor.setCursorScreenPosition({ row: 1, column: 0 }) editor.insertNewline(opts) } }, @@ -5550,7 +6324,7 @@ describe('TextEditor', () => { { name: 'backspace', op: (opts = {}) => { - editor.setCursorScreenPosition({row: 1, column: 7}) + editor.setCursorScreenPosition({ row: 1, column: 7 }) editor.backspace(opts) } }, @@ -5612,7 +6386,10 @@ describe('TextEditor', () => { { name: 'cutSelectedText', op: (opts = {}) => { - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[1, 6], [1, 10]] + ]) editor.cutSelectedText(opts) } }, @@ -5633,7 +6410,10 @@ describe('TextEditor', () => { { name: 'pasteText', op: (opts = {}) => { - editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + editor.setSelectedBufferRanges([ + [[0, 4], [0, 13]], + [[1, 6], [1, 10]] + ]) atom.clipboard.write('first') editor.pasteText(opts) } @@ -5672,7 +6452,7 @@ describe('TextEditor', () => { ] describe('without bypassReadOnly', () => { - for (const {name, op} of modifications) { + for (const { name, op } of modifications) { it(`throws an error on ${name}`, () => { expect(op).toThrow() }) @@ -5680,9 +6460,9 @@ describe('TextEditor', () => { }) describe('with bypassReadOnly', () => { - for (const {name, op} of modifications) { + for (const { name, op } of modifications) { it(`permits ${name}`, () => { - op({bypassReadOnly: true}) + op({ bypassReadOnly: true }) }) } }) @@ -5692,7 +6472,9 @@ describe('TextEditor', () => { describe('reading text', () => { it('.lineTextForScreenRow(row)', () => { editor.foldBufferRow(4) - expect(editor.lineTextForScreenRow(5)).toEqual(' return sort(left).concat(pivot).concat(sort(right));') + expect(editor.lineTextForScreenRow(5)).toEqual( + ' return sort(left).concat(pivot).concat(sort(right));' + ) expect(editor.lineTextForScreenRow(9)).toEqual('};') expect(editor.lineTextForScreenRow(10)).toBeUndefined() }) @@ -5731,7 +6513,7 @@ describe('TextEditor', () => { expect(buffer.getLineCount()).toBe(count - 2) }) - it("restores cursor position for multiple cursors", () => { + it('restores cursor position for multiple cursors', () => { const line = '0123456789'.repeat(8) editor.setText((line + '\n').repeat(5)) editor.setCursorScreenPosition([0, 5]) @@ -5744,13 +6526,10 @@ describe('TextEditor', () => { expect(cursors[1].getScreenPosition()).toEqual([1, 8]) }) - it("restores cursor position for multiple selections", () => { + it('restores cursor position for multiple selections', () => { const line = '0123456789'.repeat(8) editor.setText((line + '\n').repeat(5)) - editor.setSelectedBufferRanges([ - [[0, 5], [0, 8]], - [[2, 4], [2, 15]] - ]) + editor.setSelectedBufferRanges([[[0, 5], [0, 8]], [[2, 4], [2, 15]]]) editor.deleteLine() const cursors = editor.getCursors() @@ -5762,10 +6541,7 @@ describe('TextEditor', () => { it('deletes a line only once when multiple selections are on the same line', () => { const line1 = buffer.lineForRow(1) const count = buffer.getLineCount() - editor.setSelectedBufferRanges([ - [[0, 1], [0, 2]], - [[0, 4], [0, 5]] - ]) + editor.setSelectedBufferRanges([[[0, 1], [0, 2]], [[0, 4], [0, 5]]]) expect(buffer.lineForRow(0)).not.toBe(line1) editor.deleteLine() @@ -5841,7 +6617,9 @@ describe('TextEditor', () => { expect(buffer.lineForRow(3)).toBe(' while(items.length > 0) {') editor.undo() expect(editor.isFoldedAtScreenRow(4)).toBeTruthy() - expect(buffer.lineForRow(3)).toBe(' var pivot = items.shift(), current, left = [], right = [];') + expect(buffer.lineForRow(3)).toBe( + ' var pivot = items.shift(), current, left = [], right = [];' + ) }) }) }) @@ -5853,7 +6631,7 @@ describe('TextEditor', () => { expect(buffer.lineForRow(0)).toBe('123var quicksort = function () {') editor.setCursorBufferPosition([0]) - editor.replaceSelectedText({selectWordIfEmpty: true}, () => 'var') + editor.replaceSelectedText({ selectWordIfEmpty: true }, () => 'var') expect(buffer.lineForRow(0)).toBe('var quicksort = function () {') editor.setCursorBufferPosition([10]) @@ -5941,11 +6719,15 @@ describe('TextEditor', () => { describe('.setTabLength(tabLength)', () => { it('clips atomic soft tabs to the given tab length', () => { expect(editor.getTabLength()).toBe(2) - expect(editor.clipScreenPosition([5, 1], {clipDirection: 'forward'})).toEqual([5, 2]) + expect( + editor.clipScreenPosition([5, 1], { clipDirection: 'forward' }) + ).toEqual([5, 2]) editor.setTabLength(6) expect(editor.getTabLength()).toBe(6) - expect(editor.clipScreenPosition([5, 1], {clipDirection: 'forward'})).toEqual([5, 6]) + expect( + editor.clipScreenPosition([5, 1], { clipDirection: 'forward' }) + ).toEqual([5, 6]) const changeHandler = jasmine.createSpy('changeHandler') editor.onDidChange(changeHandler) @@ -5966,7 +6748,8 @@ describe('TextEditor', () => { expect(editor.indentLevelForLine(' hello')).toBe(1.5) }) - it('returns the indent level when the line has only leading tabs', () => expect(editor.indentLevelForLine('\t\thello')).toBe(2)) + it('returns the indent level when the line has only leading tabs', () => + expect(editor.indentLevelForLine('\t\thello')).toBe(2)) it('returns the indent level based on the character starting the line when the leading whitespace contains both spaces and tabs', () => { expect(editor.indentLevelForLine('\t hello')).toBe(2) @@ -5978,7 +6761,7 @@ describe('TextEditor', () => { }) }) - describe('when the buffer\'s language mode changes', () => { + describe("when the buffer's language mode changes", () => { beforeEach(() => { atom.config.set('core.useTreeSitterParsers', false) }) @@ -5993,7 +6776,9 @@ describe('TextEditor', () => { editor.onDidTokenize(event => events.push(event)) await atom.packages.activatePackage('language-c') - expect(atom.grammars.assignLanguageMode(editor.getBuffer(), 'source.c')).toBe(true) + expect( + atom.grammars.assignLanguageMode(editor.getBuffer(), 'source.c') + ).toBe(true) advanceClock(1) expect(events.length).toBe(1) }) @@ -6003,7 +6788,9 @@ describe('TextEditor', () => { editor.onDidChangeGrammar(grammar => events.push(grammar)) await atom.packages.activatePackage('language-c') - expect(atom.grammars.assignLanguageMode(editor.getBuffer(), 'source.c')).toBe(true) + expect( + atom.grammars.assignLanguageMode(editor.getBuffer(), 'source.c') + ).toBe(true) expect(events.length).toBe(1) expect(events[0].name).toBe('C') }) @@ -6017,7 +6804,7 @@ describe('TextEditor', () => { editor.insertText('\n ') expect(editor.lineTextForBufferRow(2)).toBe(' ') - editor.update({autoIndent: false}) + editor.update({ autoIndent: false }) editor.indent() expect(editor.lineTextForBufferRow(2)).toBe(' ') }) @@ -6025,7 +6812,7 @@ describe('TextEditor', () => { }) describe('when editor.autoIndent is true', () => { - beforeEach(() => editor.update({autoIndent: true})) + beforeEach(() => editor.update({ autoIndent: true })) describe('when `indent` is triggered', () => { it('auto-indents the line', () => { @@ -6033,7 +6820,7 @@ describe('TextEditor', () => { editor.insertText('\n ') expect(editor.lineTextForBufferRow(2)).toBe(' ') - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.indent() expect(editor.lineTextForBufferRow(2)).toBe(' ') }) @@ -6044,7 +6831,9 @@ describe('TextEditor', () => { it('indents the newline to one additional level of indentation beyond the preceding line', () => { editor.setCursorBufferPosition([1, Infinity]) editor.insertText('\n') - expect(editor.indentationForBufferRow(2)).toBe(editor.indentationForBufferRow(1) + 1) + expect(editor.indentationForBufferRow(2)).toBe( + editor.indentationForBufferRow(1) + 1 + ) }) }) @@ -6052,7 +6841,9 @@ describe('TextEditor', () => { it('indents the new line to the same level as the preceding line', () => { editor.setCursorBufferPosition([5, 14]) editor.insertText('\n') - expect(editor.indentationForBufferRow(6)).toBe(editor.indentationForBufferRow(5)) + expect(editor.indentationForBufferRow(6)).toBe( + editor.indentationForBufferRow(5) + ) }) }) @@ -6082,7 +6873,7 @@ describe('TextEditor', () => { editor.insertText(' var this-line-should-be-indented-more\n') expect(editor.indentationForBufferRow(1)).toBe(1) - editor.update({autoIndent: true}) + editor.update({ autoIndent: true }) editor.setCursorBufferPosition([2, Infinity]) editor.insertText('\n') expect(editor.indentationForBufferRow(1)).toBe(1) @@ -6107,9 +6898,13 @@ describe('TextEditor', () => { it('decreases the indentation to match that of the preceding line', () => { editor.setCursorBufferPosition([1, Infinity]) editor.insertText('\n') - expect(editor.indentationForBufferRow(2)).toBe(editor.indentationForBufferRow(1) + 1) + expect(editor.indentationForBufferRow(2)).toBe( + editor.indentationForBufferRow(1) + 1 + ) editor.insertText('}') - expect(editor.indentationForBufferRow(2)).toBe(editor.indentationForBufferRow(1)) + expect(editor.indentationForBufferRow(2)).toBe( + editor.indentationForBufferRow(1) + ) }) }) @@ -6117,15 +6912,21 @@ describe('TextEditor', () => { it('decreases the indentation to be one level below that of the preceding line', () => { editor.setCursorBufferPosition([3, Infinity]) editor.insertText('\n ') - expect(editor.indentationForBufferRow(4)).toBe(editor.indentationForBufferRow(3)) + expect(editor.indentationForBufferRow(4)).toBe( + editor.indentationForBufferRow(3) + ) editor.insertText('}') - expect(editor.indentationForBufferRow(4)).toBe(editor.indentationForBufferRow(3) - 1) + expect(editor.indentationForBufferRow(4)).toBe( + editor.indentationForBufferRow(3) - 1 + ) }) it("doesn't break when decreasing the indentation on a row that has no indentation", () => { editor.setCursorBufferPosition([12, Infinity]) editor.insertText('\n}; # too many closing brackets!') - expect(editor.lineTextForBufferRow(13)).toBe('}; # too many closing brackets!') + expect(editor.lineTextForBufferRow(13)).toBe( + '}; # too many closing brackets!' + ) }) }) }) @@ -6143,9 +6944,13 @@ describe('TextEditor', () => { describe('when the current line does not match a decrease indent pattern', () => { it('leaves the line unchanged', () => { editor.setCursorBufferPosition([2, 4]) - expect(editor.indentationForBufferRow(2)).toBe(editor.indentationForBufferRow(1) + 1) + expect(editor.indentationForBufferRow(2)).toBe( + editor.indentationForBufferRow(1) + 1 + ) editor.insertText('foo') - expect(editor.indentationForBufferRow(2)).toBe(editor.indentationForBufferRow(1) + 1) + expect(editor.indentationForBufferRow(2)).toBe( + editor.indentationForBufferRow(1) + 1 + ) }) }) }) @@ -6153,16 +6958,16 @@ describe('TextEditor', () => { describe('atomic soft tabs', () => { it('skips tab-length runs of leading whitespace when moving the cursor', () => { - editor.update({tabLength: 4, atomicSoftTabs: true}) + editor.update({ tabLength: 4, atomicSoftTabs: true }) editor.setCursorScreenPosition([2, 3]) expect(editor.getCursorScreenPosition()).toEqual([2, 4]) - editor.update({atomicSoftTabs: false}) + editor.update({ atomicSoftTabs: false }) editor.setCursorScreenPosition([2, 3]) expect(editor.getCursorScreenPosition()).toEqual([2, 3]) - editor.update({atomicSoftTabs: true}) + editor.update({ atomicSoftTabs: true }) editor.setCursorScreenPosition([2, 3]) expect(editor.getCursorScreenPosition()).toEqual([2, 4]) }) @@ -6181,7 +6986,7 @@ describe('TextEditor', () => { it('notifies ::onDidDestroy observers when the editor is destroyed', () => { let destroyObserverCalled = false - editor.onDidDestroy(() => destroyObserverCalled = true) + editor.onDidDestroy(() => (destroyObserverCalled = true)) editor.destroy() expect(destroyObserverCalled).toBe(true) @@ -6217,7 +7022,9 @@ describe('TextEditor', () => { editor.insertText(' ') editor.setCursorBufferPosition([0]) editor.joinLines() - expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () { var sort = function(items) {') + expect(editor.lineTextForBufferRow(0)).toBe( + 'var quicksort = function () { var sort = function(items) {' + ) expect(editor.getCursorBufferPosition()).toEqual([0, 29]) }) }) @@ -6227,7 +7034,9 @@ describe('TextEditor', () => { editor.setCursorBufferPosition([9]) editor.joinLines() expect(editor.lineTextForBufferRow(9)).toBe(' };') - expect(editor.lineTextForBufferRow(10)).toBe(' return sort(Array.apply(this, arguments));') + expect(editor.lineTextForBufferRow(10)).toBe( + ' return sort(Array.apply(this, arguments));' + ) expect(editor.getCursorBufferPosition()).toEqual([9, 4]) }) }) @@ -6244,7 +7053,9 @@ describe('TextEditor', () => { it('joins the line below with the current line with no added space', () => { editor.setCursorBufferPosition([10]) editor.joinLines() - expect(editor.lineTextForBufferRow(10)).toBe('return sort(Array.apply(this, arguments));') + expect(editor.lineTextForBufferRow(10)).toBe( + 'return sort(Array.apply(this, arguments));' + ) expect(editor.getCursorBufferPosition()).toEqual([10, 0]) }) }) @@ -6255,7 +7066,9 @@ describe('TextEditor', () => { it('joins the line below with the current line separated by a space and retains the selected text', () => { editor.setSelectedBufferRange([[0, 1], [0, 3]]) editor.joinLines() - expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () { var sort = function(items) {') + expect(editor.lineTextForBufferRow(0)).toBe( + 'var quicksort = function () { var sort = function(items) {' + ) expect(editor.getSelectedBufferRange()).toEqual([[0, 1], [0, 3]]) }) }) @@ -6264,7 +7077,9 @@ describe('TextEditor', () => { it('joins all selected lines separated by a space and retains the selected text', () => { editor.setSelectedBufferRange([[9, 3], [12, 1]]) editor.joinLines() - expect(editor.lineTextForBufferRow(9)).toBe(' }; return sort(Array.apply(this, arguments)); };') + expect(editor.lineTextForBufferRow(9)).toBe( + ' }; return sort(Array.apply(this, arguments)); };' + ) expect(editor.getSelectedBufferRange()).toEqual([[9, 3], [9, 49]]) }) }) @@ -6275,11 +7090,14 @@ describe('TextEditor', () => { it('for each selection, duplicates all buffer lines intersected by the selection', () => { editor.foldBufferRow(4) editor.setCursorBufferPosition([2, 5]) - editor.addSelectionForBufferRange([[3, 0], [8, 0]], {preserveFolds: true}) + editor.addSelectionForBufferRange([[3, 0], [8, 0]], { + preserveFolds: true + }) editor.duplicateLines() - expect(editor.getTextInBufferRange([[2, 0], [13, 5]])).toBe(dedent ` + expect(editor.getTextInBufferRange([[2, 0], [13, 5]])).toBe( + dedent` if (items.length <= 1) return items; if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = []; @@ -6292,14 +7110,25 @@ describe('TextEditor', () => { current = items.shift(); current < pivot ? left.push(current) : right.push(current); }\ - `.split('\n').map(l => ` ${l}`).join('\n')) - expect(editor.getSelectedBufferRanges()).toEqual([[[3, 5], [3, 5]], [[9, 0], [14, 0]]]) + ` + .split('\n') + .map(l => ` ${l}`) + .join('\n') + ) + expect(editor.getSelectedBufferRanges()).toEqual([ + [[3, 5], [3, 5]], + [[9, 0], [14, 0]] + ]) // folds are also duplicated expect(editor.isFoldedAtScreenRow(5)).toBe(true) expect(editor.isFoldedAtScreenRow(7)).toBe(true) - expect(editor.lineTextForScreenRow(7)).toBe(` while(items.length > 0) {${editor.displayLayer.foldCharacter}}`) - expect(editor.lineTextForScreenRow(8)).toBe(' return sort(left).concat(pivot).concat(sort(right));') + expect(editor.lineTextForScreenRow(7)).toBe( + ` while(items.length > 0) {${editor.displayLayer.foldCharacter}}` + ) + expect(editor.lineTextForScreenRow(8)).toBe( + ' return sort(left).concat(pivot).concat(sort(right));' + ) }) it('duplicates all folded lines for empty selections on lines containing folds', () => { @@ -6308,7 +7137,8 @@ describe('TextEditor', () => { editor.duplicateLines() - expect(editor.getTextInBufferRange([[2, 0], [11, 5]])).toBe(dedent` + expect(editor.getTextInBufferRange([[2, 0], [11, 5]])).toBe( + dedent` if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = []; while(items.length > 0) { @@ -6319,24 +7149,31 @@ describe('TextEditor', () => { current = items.shift(); current < pivot ? left.push(current) : right.push(current); } - `.split('\n').map(l => ` ${l}`).join('\n')) + ` + .split('\n') + .map(l => ` ${l}`) + .join('\n') + ) expect(editor.getSelectedBufferRange()).toEqual([[8, 0], [8, 0]]) }) it('can duplicate the last line of the buffer', () => { editor.setSelectedBufferRange([[11, 0], [12, 2]]) editor.duplicateLines() - expect(editor.getTextInBufferRange([[11, 0], [14, 2]])).toBe(' ' + dedent ` + expect(editor.getTextInBufferRange([[11, 0], [14, 2]])).toBe( + ' ' + + dedent` return sort(Array.apply(this, arguments)); }; return sort(Array.apply(this, arguments)); }; - `.trim()) + `.trim() + ) expect(editor.getSelectedBufferRange()).toEqual([[13, 0], [14, 2]]) }) it('only duplicates lines containing multiple selections once', () => { - editor.setText(dedent ` + editor.setText(dedent` aaaaaa bbbbbb cccccc @@ -6350,7 +7187,7 @@ describe('TextEditor', () => { [[3, 3], [3, 4]] ]) editor.duplicateLines() - expect(editor.getText()).toBe(dedent ` + expect(editor.getText()).toBe(dedent` aaaaaa aaaaaa bbbbbb @@ -6492,13 +7329,17 @@ describe('TextEditor', () => { await atom.packages.activatePackage('language-hyperlink') const grammar = atom.grammars.selectGrammar('text.js') - const {line, tags} = grammar.tokenizeLine('var i; // http://github.com') + const { line, tags } = grammar.tokenizeLine('var i; // http://github.com') const tokens = atom.grammars.decodeTokens(line, tags) expect(tokens[0].value).toBe('var') expect(tokens[0].scopes).toEqual(['source.js', 'storage.type.var.js']) expect(tokens[6].value).toBe('http://github.com') - expect(tokens[6].scopes).toEqual(['source.js', 'comment.line.double-slash.js', 'markup.underline.link.http.hyperlink']) + expect(tokens[6].scopes).toEqual([ + 'source.js', + 'comment.line.double-slash.js', + 'markup.underline.link.http.hyperlink' + ]) }) describe('when the grammar is added', () => { @@ -6507,16 +7348,49 @@ describe('TextEditor', () => { editor.setText('// http://github.com') let tokens = editor.tokensForScreenRow(0) expect(tokens).toEqual([ - {text: '//', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--punctuation syntax--definition syntax--comment syntax--js']}, - {text: ' http://github.com', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']} + { + text: '//', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--punctuation syntax--definition syntax--comment syntax--js' + ] + }, + { + text: ' http://github.com', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + } ]) await atom.packages.activatePackage('language-hyperlink') tokens = editor.tokensForScreenRow(0) expect(tokens).toEqual([ - {text: '//', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--punctuation syntax--definition syntax--comment syntax--js']}, - {text: ' ', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']}, - {text: 'http://github.com', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--markup syntax--underline syntax--link syntax--http syntax--hyperlink']} + { + text: '//', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--punctuation syntax--definition syntax--comment syntax--js' + ] + }, + { + text: ' ', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + }, + { + text: 'http://github.com', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--markup syntax--underline syntax--link syntax--http syntax--hyperlink' + ] + } ]) }) @@ -6526,28 +7400,106 @@ describe('TextEditor', () => { editor.setText('// SELECT * FROM OCTOCATS') let tokens = editor.tokensForScreenRow(0) expect(tokens).toEqual([ - {text: '//', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--punctuation syntax--definition syntax--comment syntax--js']}, - {text: ' SELECT * FROM OCTOCATS', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']} + { + text: '//', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--punctuation syntax--definition syntax--comment syntax--js' + ] + }, + { + text: ' SELECT * FROM OCTOCATS', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + } ]) await atom.packages.activatePackage('package-with-injection-selector') tokens = editor.tokensForScreenRow(0) expect(tokens).toEqual([ - {text: '//', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--punctuation syntax--definition syntax--comment syntax--js']}, - {text: ' SELECT * FROM OCTOCATS', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']} + { + text: '//', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--punctuation syntax--definition syntax--comment syntax--js' + ] + }, + { + text: ' SELECT * FROM OCTOCATS', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + } ]) await atom.packages.activatePackage('language-sql') tokens = editor.tokensForScreenRow(0) expect(tokens).toEqual([ - {text: '//', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--punctuation syntax--definition syntax--comment syntax--js']}, - {text: ' ', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']}, - {text: 'SELECT', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--keyword syntax--other syntax--DML syntax--sql']}, - {text: ' ', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']}, - {text: '*', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--keyword syntax--operator syntax--star syntax--sql']}, - {text: ' ', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']}, - {text: 'FROM', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js', 'syntax--keyword syntax--other syntax--DML syntax--sql']}, - {text: ' OCTOCATS', scopes: ['syntax--source syntax--js', 'syntax--comment syntax--line syntax--double-slash syntax--js']} + { + text: '//', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--punctuation syntax--definition syntax--comment syntax--js' + ] + }, + { + text: ' ', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + }, + { + text: 'SELECT', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--keyword syntax--other syntax--DML syntax--sql' + ] + }, + { + text: ' ', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + }, + { + text: '*', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--keyword syntax--operator syntax--star syntax--sql' + ] + }, + { + text: ' ', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + }, + { + text: 'FROM', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js', + 'syntax--keyword syntax--other syntax--DML syntax--sql' + ] + }, + { + text: ' OCTOCATS', + scopes: [ + 'syntax--source syntax--js', + 'syntax--comment syntax--line syntax--double-slash syntax--js' + ] + } ]) }) }) @@ -6574,10 +7526,10 @@ describe('TextEditor', () => { describe('.pageUp/Down()', () => { it('moves the cursor down one page length', () => { - editor.update({autoHeight: false}) + editor.update({ autoHeight: false }) const element = editor.getElement() jasmine.attachToDOM(element) - element.style.height = (element.component.getLineHeight() * 5) + 'px' + element.style.height = element.component.getLineHeight() * 5 + 'px' element.measureDimensions() expect(editor.getCursorBufferPosition().row).toBe(0) @@ -6598,10 +7550,10 @@ describe('TextEditor', () => { describe('.selectPageUp/Down()', () => { it('selects one screen height of text up or down', () => { - editor.update({autoHeight: false}) + editor.update({ autoHeight: false }) const element = editor.getElement() jasmine.attachToDOM(element) - element.style.height = (element.component.getLineHeight() * 5) + 'px' + element.style.height = element.component.getLineHeight() * 5 + 'px' element.measureDimensions() expect(editor.getCursorBufferPosition().row).toBe(0) @@ -6633,28 +7585,37 @@ describe('TextEditor', () => { editor.onDidRequestAutoscroll(scrollSpy) editor.scrollToScreenPosition([8, 20]) - editor.scrollToScreenPosition([8, 20], {center: true}) - editor.scrollToScreenPosition([8, 20], {center: false, reversed: true}) + editor.scrollToScreenPosition([8, 20], { center: true }) + editor.scrollToScreenPosition([8, 20], { center: false, reversed: true }) - expect(scrollSpy).toHaveBeenCalledWith({screenRange: [[8, 20], [8, 20]], options: {}}) - expect(scrollSpy).toHaveBeenCalledWith({screenRange: [[8, 20], [8, 20]], options: {center: true}}) - expect(scrollSpy).toHaveBeenCalledWith({screenRange: [[8, 20], [8, 20]], options: {center: false, reversed: true}}) + expect(scrollSpy).toHaveBeenCalledWith({ + screenRange: [[8, 20], [8, 20]], + options: {} + }) + expect(scrollSpy).toHaveBeenCalledWith({ + screenRange: [[8, 20], [8, 20]], + options: { center: true } + }) + expect(scrollSpy).toHaveBeenCalledWith({ + screenRange: [[8, 20], [8, 20]], + options: { center: false, reversed: true } + }) }) }) describe('scroll past end', () => { it('returns false by default but can be customized', () => { expect(editor.getScrollPastEnd()).toBe(false) - editor.update({scrollPastEnd: true}) + editor.update({ scrollPastEnd: true }) expect(editor.getScrollPastEnd()).toBe(true) - editor.update({scrollPastEnd: false}) + editor.update({ scrollPastEnd: false }) expect(editor.getScrollPastEnd()).toBe(false) }) it('always returns false when autoHeight is on', () => { - editor.update({autoHeight: true, scrollPastEnd: true}) + editor.update({ autoHeight: true, scrollPastEnd: true }) expect(editor.getScrollPastEnd()).toBe(false) - editor.update({autoHeight: false}) + editor.update({ autoHeight: false }) expect(editor.getScrollPastEnd()).toBe(true) }) }) @@ -6663,9 +7624,9 @@ describe('TextEditor', () => { it('returns true by default but can be customized', () => { editor = new TextEditor() expect(editor.getAutoHeight()).toBe(true) - editor.update({autoHeight: false}) + editor.update({ autoHeight: false }) expect(editor.getAutoHeight()).toBe(false) - editor.update({autoHeight: true}) + editor.update({ autoHeight: true }) expect(editor.getAutoHeight()).toBe(true) editor.destroy() }) @@ -6674,9 +7635,9 @@ describe('TextEditor', () => { describe('auto width', () => { it('returns false by default but can be customized', () => { expect(editor.getAutoWidth()).toBe(false) - editor.update({autoWidth: true}) + editor.update({ autoWidth: true }) expect(editor.getAutoWidth()).toBe(true) - editor.update({autoWidth: false}) + editor.update({ autoWidth: false }) expect(editor.getAutoWidth()).toBe(false) }) }) @@ -6692,7 +7653,7 @@ describe('TextEditor', () => { it('models placeholderText and emits an event when changed', () => { let handler - editor.onDidChangePlaceholderText(handler = jasmine.createSpy()) + editor.onDidChangePlaceholderText((handler = jasmine.createSpy())) expect(editor.getPlaceholderText()).toBeUndefined() @@ -6738,17 +7699,22 @@ describe('TextEditor', () => { expect(gutter.type).toBe('line-number') }) - it("does not allow a custom gutter with the 'line-number' name.", () => expect(editor.addGutter.bind(editor, {name: 'line-number'})).toThrow()) + it("does not allow a custom gutter with the 'line-number' name.", () => + expect( + editor.addGutter.bind(editor, { name: 'line-number' }) + ).toThrow()) }) describe('::decorateMarker', () => { let marker - beforeEach(() => marker = editor.markBufferRange([[1, 0], [1, 0]])) + beforeEach(() => (marker = editor.markBufferRange([[1, 0], [1, 0]]))) it('reflects an added decoration when one of its custom gutters is decorated.', () => { - const gutter = editor.addGutter({'name': 'custom-gutter'}) - const decoration = gutter.decorateMarker(marker, {class: 'custom-class'}) + const gutter = editor.addGutter({ name: 'custom-gutter' }) + const decoration = gutter.decorateMarker(marker, { + class: 'custom-class' + }) const gutterDecorations = editor.getDecorations({ type: 'gutter', gutterName: 'custom-gutter', @@ -6759,7 +7725,9 @@ describe('TextEditor', () => { }) it('reflects an added decoration when its line-number gutter is decorated.', () => { - const decoration = editor.gutterWithName('line-number').decorateMarker(marker, {class: 'test-class'}) + const decoration = editor + .gutterWithName('line-number') + .decorateMarker(marker, { class: 'test-class' }) const gutterDecorations = editor.getDecorations({ type: 'line-number', gutterName: 'line-number', @@ -6782,14 +7750,14 @@ describe('TextEditor', () => { const lineNumberGutter = editor.gutterWithName('line-number') editor.observeGutters(callback) expect(payloads).toEqual([lineNumberGutter]) - const gutter1 = editor.addGutter({name: 'test-gutter-1'}) + const gutter1 = editor.addGutter({ name: 'test-gutter-1' }) expect(payloads).toEqual([lineNumberGutter, gutter1]) - const gutter2 = editor.addGutter({name: 'test-gutter-2'}) + const gutter2 = editor.addGutter({ name: 'test-gutter-2' }) expect(payloads).toEqual([lineNumberGutter, gutter1, gutter2]) }) it('does not call the callback when a gutter is removed.', () => { - const gutter = editor.addGutter({name: 'test-gutter'}) + const gutter = editor.addGutter({ name: 'test-gutter' }) editor.observeGutters(callback) payloads = [] gutter.destroy() @@ -6800,7 +7768,7 @@ describe('TextEditor', () => { const subscription = editor.observeGutters(callback) payloads = [] subscription.dispose() - editor.addGutter({name: 'test-gutter'}) + editor.addGutter({ name: 'test-gutter' }) expect(payloads).toEqual([]) }) }) @@ -6816,7 +7784,7 @@ describe('TextEditor', () => { it('calls the callback with each newly-added gutter, but not with existing gutters.', () => { editor.onDidAddGutter(callback) expect(payloads).toEqual([]) - const gutter = editor.addGutter({name: 'test-gutter'}) + const gutter = editor.addGutter({ name: 'test-gutter' }) expect(payloads).toEqual([gutter]) }) @@ -6824,7 +7792,7 @@ describe('TextEditor', () => { const subscription = editor.onDidAddGutter(callback) payloads = [] subscription.dispose() - editor.addGutter({name: 'test-gutter'}) + editor.addGutter({ name: 'test-gutter' }) expect(payloads).toEqual([]) }) }) @@ -6838,7 +7806,7 @@ describe('TextEditor', () => { }) it('calls the callback when a gutter is removed.', () => { - const gutter = editor.addGutter({name: 'test-gutter'}) + const gutter = editor.addGutter({ name: 'test-gutter' }) editor.onDidRemoveGutter(callback) expect(payloads).toEqual([]) gutter.destroy() @@ -6846,7 +7814,7 @@ describe('TextEditor', () => { }) it('does not call the callback after the subscription has been disposed.', () => { - const gutter = editor.addGutter({name: 'test-gutter'}) + const gutter = editor.addGutter({ name: 'test-gutter' }) const subscription = editor.onDidRemoveGutter(callback) subscription.dispose() gutter.destroy() @@ -6859,9 +7827,19 @@ describe('TextEditor', () => { describe('::decorateMarker', () => { it('includes the decoration in the object returned from ::decorationsStateForScreenRowRange', () => { const marker = editor.markBufferRange([[2, 4], [6, 8]]) - const decoration = editor.decorateMarker(marker, {type: 'highlight', class: 'foo'}) - expect(editor.decorationsStateForScreenRowRange(0, 5)[decoration.id]).toEqual({ - properties: {id: decoration.id, order: Infinity, type: 'highlight', class: 'foo'}, + const decoration = editor.decorateMarker(marker, { + type: 'highlight', + class: 'foo' + }) + expect( + editor.decorationsStateForScreenRowRange(0, 5)[decoration.id] + ).toEqual({ + properties: { + id: decoration.id, + order: Infinity, + type: 'highlight', + class: 'foo' + }, screenRange: marker.getScreenRange(), bufferRange: marker.getBufferRange(), rangeIsReversed: false @@ -6870,8 +7848,8 @@ describe('TextEditor', () => { it("does not throw errors after the marker's containing layer is destroyed", () => { const layer = editor.addMarkerLayer() - const marker = layer.markBufferRange([[2, 4], [6, 8]]) - const decoration = editor.decorateMarker(marker, {type: 'highlight', class: 'foo'}) + layer.markBufferRange([[2, 4], [6, 8]]) + layer.destroy() editor.decorationsStateForScreenRowRange(0, 5) }) @@ -6885,71 +7863,105 @@ describe('TextEditor', () => { const layer2 = editor.getBuffer().addMarkerLayer() const marker3 = layer2.markRange([[8, 0], [9, 0]]) - const layer1Decoration1 = editor.decorateMarkerLayer(layer1, {type: 'highlight', class: 'foo'}) - const layer1Decoration2 = editor.decorateMarkerLayer(layer1, {type: 'highlight', class: 'bar'}) - const layer2Decoration = editor.decorateMarkerLayer(layer2, {type: 'highlight', class: 'baz'}) + const layer1Decoration1 = editor.decorateMarkerLayer(layer1, { + type: 'highlight', + class: 'foo' + }) + const layer1Decoration2 = editor.decorateMarkerLayer(layer1, { + type: 'highlight', + class: 'bar' + }) + const layer2Decoration = editor.decorateMarkerLayer(layer2, { + type: 'highlight', + class: 'baz' + }) let decorationState = editor.decorationsStateForScreenRowRange(0, 13) - expect(decorationState[`${layer1Decoration1.id}-${marker1.id}`]).toEqual({ - properties: {type: 'highlight', class: 'foo'}, + expect( + decorationState[`${layer1Decoration1.id}-${marker1.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'foo' }, screenRange: marker1.getRange(), bufferRange: marker1.getRange(), rangeIsReversed: false }) - expect(decorationState[`${layer1Decoration1.id}-${marker2.id}`]).toEqual({ - properties: {type: 'highlight', class: 'foo'}, + expect( + decorationState[`${layer1Decoration1.id}-${marker2.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'foo' }, screenRange: marker2.getRange(), bufferRange: marker2.getRange(), rangeIsReversed: false }) - expect(decorationState[`${layer1Decoration2.id}-${marker1.id}`]).toEqual({ - properties: {type: 'highlight', class: 'bar'}, + expect( + decorationState[`${layer1Decoration2.id}-${marker1.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'bar' }, screenRange: marker1.getRange(), bufferRange: marker1.getRange(), rangeIsReversed: false }) - expect(decorationState[`${layer1Decoration2.id}-${marker2.id}`]).toEqual({ - properties: {type: 'highlight', class: 'bar'}, + expect( + decorationState[`${layer1Decoration2.id}-${marker2.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'bar' }, screenRange: marker2.getRange(), bufferRange: marker2.getRange(), rangeIsReversed: false }) - expect(decorationState[`${layer2Decoration.id}-${marker3.id}`]).toEqual({ - properties: {type: 'highlight', class: 'baz'}, - screenRange: marker3.getRange(), - bufferRange: marker3.getRange(), - rangeIsReversed: false - }) + expect(decorationState[`${layer2Decoration.id}-${marker3.id}`]).toEqual( + { + properties: { type: 'highlight', class: 'baz' }, + screenRange: marker3.getRange(), + bufferRange: marker3.getRange(), + rangeIsReversed: false + } + ) layer1Decoration1.destroy() decorationState = editor.decorationsStateForScreenRowRange(0, 12) - expect(decorationState[`${layer1Decoration1.id}-${marker1.id}`]).toBeUndefined() - expect(decorationState[`${layer1Decoration1.id}-${marker2.id}`]).toBeUndefined() - expect(decorationState[`${layer1Decoration2.id}-${marker1.id}`]).toEqual({ - properties: {type: 'highlight', class: 'bar'}, + expect( + decorationState[`${layer1Decoration1.id}-${marker1.id}`] + ).toBeUndefined() + expect( + decorationState[`${layer1Decoration1.id}-${marker2.id}`] + ).toBeUndefined() + expect( + decorationState[`${layer1Decoration2.id}-${marker1.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'bar' }, screenRange: marker1.getRange(), bufferRange: marker1.getRange(), rangeIsReversed: false }) - expect(decorationState[`${layer1Decoration2.id}-${marker2.id}`]).toEqual({ - properties: {type: 'highlight', class: 'bar'}, + expect( + decorationState[`${layer1Decoration2.id}-${marker2.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'bar' }, screenRange: marker2.getRange(), bufferRange: marker2.getRange(), rangeIsReversed: false }) - expect(decorationState[`${layer2Decoration.id}-${marker3.id}`]).toEqual({ - properties: {type: 'highlight', class: 'baz'}, - screenRange: marker3.getRange(), - bufferRange: marker3.getRange(), - rangeIsReversed: false - }) + expect(decorationState[`${layer2Decoration.id}-${marker3.id}`]).toEqual( + { + properties: { type: 'highlight', class: 'baz' }, + screenRange: marker3.getRange(), + bufferRange: marker3.getRange(), + rangeIsReversed: false + } + ) - layer1Decoration2.setPropertiesForMarker(marker1, {type: 'highlight', class: 'quux'}) + layer1Decoration2.setPropertiesForMarker(marker1, { + type: 'highlight', + class: 'quux' + }) decorationState = editor.decorationsStateForScreenRowRange(0, 12) - expect(decorationState[`${layer1Decoration2.id}-${marker1.id}`]).toEqual({ - properties: {type: 'highlight', class: 'quux'}, + expect( + decorationState[`${layer1Decoration2.id}-${marker1.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'quux' }, screenRange: marker1.getRange(), bufferRange: marker1.getRange(), rangeIsReversed: false @@ -6957,8 +7969,10 @@ describe('TextEditor', () => { layer1Decoration2.setPropertiesForMarker(marker1, null) decorationState = editor.decorationsStateForScreenRowRange(0, 12) - expect(decorationState[`${layer1Decoration2.id}-${marker1.id}`]).toEqual({ - properties: {type: 'highlight', class: 'bar'}, + expect( + decorationState[`${layer1Decoration2.id}-${marker1.id}`] + ).toEqual({ + properties: { type: 'highlight', class: 'bar' }, screenRange: marker1.getRange(), bufferRange: marker1.getRange(), rangeIsReversed: false @@ -6969,47 +7983,68 @@ describe('TextEditor', () => { describe('invisibles', () => { beforeEach(() => { - editor.update({showInvisibles: true}) + editor.update({ showInvisibles: true }) }) it('substitutes invisible characters according to the given rules', () => { const previousLineText = editor.lineTextForScreenRow(0) - editor.update({invisibles: {eol: '?'}}) + editor.update({ invisibles: { eol: '?' } }) expect(editor.lineTextForScreenRow(0)).not.toBe(previousLineText) expect(editor.lineTextForScreenRow(0).endsWith('?')).toBe(true) - expect(editor.getInvisibles()).toEqual({eol: '?'}) + expect(editor.getInvisibles()).toEqual({ eol: '?' }) }) it('does not use invisibles if showInvisibles is set to false', () => { - editor.update({invisibles: {eol: '?'}}) + editor.update({ invisibles: { eol: '?' } }) expect(editor.lineTextForScreenRow(0).endsWith('?')).toBe(true) - editor.update({showInvisibles: false}) + editor.update({ showInvisibles: false }) expect(editor.lineTextForScreenRow(0).endsWith('?')).toBe(false) }) }) describe('indent guides', () => { it('shows indent guides when `editor.showIndentGuide` is set to true and the editor is not mini', () => { - editor.update({showIndentGuide: false}) + editor.update({ showIndentGuide: false }) expect(editor.tokensForScreenRow(1).slice(0, 3)).toEqual([ - {text: ' ', scopes: ['syntax--source syntax--js', 'leading-whitespace']}, - {text: 'var', scopes: ['syntax--source syntax--js', 'syntax--storage syntax--type']}, - {text: ' sort ', scopes: ['syntax--source syntax--js']} + { + text: ' ', + scopes: ['syntax--source syntax--js', 'leading-whitespace'] + }, + { + text: 'var', + scopes: ['syntax--source syntax--js', 'syntax--storage syntax--type'] + }, + { text: ' sort ', scopes: ['syntax--source syntax--js'] } ]) - editor.update({showIndentGuide: true}) + editor.update({ showIndentGuide: true }) expect(editor.tokensForScreenRow(1).slice(0, 3)).toEqual([ - {text: ' ', scopes: ['syntax--source syntax--js', 'leading-whitespace indent-guide']}, - {text: 'var', scopes: ['syntax--source syntax--js', 'syntax--storage syntax--type']}, - {text: ' sort ', scopes: ['syntax--source syntax--js']} + { + text: ' ', + scopes: [ + 'syntax--source syntax--js', + 'leading-whitespace indent-guide' + ] + }, + { + text: 'var', + scopes: ['syntax--source syntax--js', 'syntax--storage syntax--type'] + }, + { text: ' sort ', scopes: ['syntax--source syntax--js'] } ]) editor.setMini(true) expect(editor.tokensForScreenRow(1).slice(0, 3)).toEqual([ - {text: ' ', scopes: ['syntax--source syntax--js', 'leading-whitespace']}, - {text: 'var', scopes: ['syntax--source syntax--js', 'syntax--storage syntax--type']}, - {text: ' sort ', scopes: ['syntax--source syntax--js']} + { + text: ' ', + scopes: ['syntax--source syntax--js', 'leading-whitespace'] + }, + { + text: 'var', + scopes: ['syntax--source syntax--js', 'syntax--storage syntax--type'] + }, + { text: ' sort ', scopes: ['syntax--source syntax--js'] } ]) }) }) @@ -7025,11 +8060,13 @@ describe('TextEditor', () => { expect(editor.lineTextForScreenRow(0)).toBe('var quicksort = ') - editor.update({editorWidthInChars: 10}) + editor.update({ editorWidthInChars: 10 }) expect(editor.lineTextForScreenRow(0)).toBe('var ') - editor.update({mini: true}) - expect(editor.lineTextForScreenRow(0)).toBe('var quicksort = function () {') + editor.update({ mini: true }) + expect(editor.lineTextForScreenRow(0)).toBe( + 'var quicksort = function () {' + ) }) }) @@ -7043,13 +8080,14 @@ describe('TextEditor', () => { }) expect(editor.lineTextForScreenRow(1)).toEqual(' 9') - editor.update({softWrapHangingIndentLength: 4}) + editor.update({ softWrapHangingIndentLength: 4 }) expect(editor.lineTextForScreenRow(1)).toEqual(' 9') }) }) describe('::getElement', () => { - it('returns an element', () => expect(editor.getElement() instanceof HTMLElement).toBe(true)) + it('returns an element', () => + expect(editor.getElement() instanceof HTMLElement).toBe(true)) }) describe('setMaxScreenLineLength', () => { @@ -7072,7 +8110,7 @@ describe('TextEditor', () => { describe('.scopeDescriptorForBufferPosition(position)', () => { it('returns a default scope descriptor when no language mode is assigned', () => { - editor = new TextEditor({buffer: new TextBuffer()}) + editor = new TextEditor({ buffer: new TextBuffer() }) const scopeDescriptor = editor.scopeDescriptorForBufferPosition([0, 0]) expect(scopeDescriptor.getScopesArray()).toEqual(['text']) }) @@ -7081,7 +8119,7 @@ describe('TextEditor', () => { describe('.syntaxTreeScopeDescriptorForBufferPosition(position)', () => { it('returns the result of scopeDescriptorForBufferPosition() when textmate language mode is used', async () => { atom.config.set('core.useTreeSitterParsers', false) - editor = await atom.workspace.open('sample.js', {autoIndent: false}) + editor = await atom.workspace.open('sample.js', { autoIndent: false }) await atom.packages.activatePackage('language-javascript') let buffer = editor.getBuffer() @@ -7098,7 +8136,9 @@ describe('TextEditor', () => { advanceClock() } - const syntaxTreeeScopeDescriptor = editor.syntaxTreeScopeDescriptorForBufferPosition([4, 17]) + const syntaxTreeeScopeDescriptor = editor.syntaxTreeScopeDescriptorForBufferPosition( + [4, 17] + ) expect(syntaxTreeeScopeDescriptor.getScopesArray()).toEqual([ 'source.js', 'support.variable.property.js' @@ -7106,17 +8146,21 @@ describe('TextEditor', () => { }) it('returns the result of syntaxTreeScopeDescriptorForBufferPosition() when tree-sitter language mode is used', async () => { - editor = await atom.workspace.open('sample.js', {autoIndent: false}) + editor = await atom.workspace.open('sample.js', { autoIndent: false }) await atom.packages.activatePackage('language-javascript') let buffer = editor.getBuffer() - buffer.setLanguageMode(new TreeSitterLanguageMode({ - buffer, - grammar: atom.grammars.grammarForScopeName('source.js') - })) + buffer.setLanguageMode( + new TreeSitterLanguageMode({ + buffer, + grammar: atom.grammars.grammarForScopeName('source.js') + }) + ) - const syntaxTreeeScopeDescriptor = editor.syntaxTreeScopeDescriptorForBufferPosition([4, 17]) + const syntaxTreeeScopeDescriptor = editor.syntaxTreeScopeDescriptorForBufferPosition( + [4, 17] + ) expect(syntaxTreeeScopeDescriptor.getScopesArray()).toEqual([ 'source.js', 'program', @@ -7154,7 +8198,9 @@ describe('TextEditor', () => { editor.setText('changed') atom.workspace.getActivePane().splitRight() - const editor2 = await atom.workspace.open('sample.js', {autoIndent: false}) + const editor2 = await atom.workspace.open('sample.js', { + autoIndent: false + }) expect(editor.shouldPromptToSave()).toBeFalsy() editor2.destroy() @@ -7169,20 +8215,40 @@ describe('TextEditor', () => { editor.setText('other stuff') fs.writeFileSync(editor.getPath(), 'new stuff') - expect(editor.shouldPromptToSave({windowCloseRequested: true, projectHasPaths: true})).toBeFalsy() + expect( + editor.shouldPromptToSave({ + windowCloseRequested: true, + projectHasPaths: true + }) + ).toBeFalsy() await new Promise(resolve => editor.onDidConflict(resolve)) - expect(editor.shouldPromptToSave({windowCloseRequested: true, projectHasPaths: true})).toBeTruthy() + expect( + editor.shouldPromptToSave({ + windowCloseRequested: true, + projectHasPaths: true + }) + ).toBeTruthy() }) it('returns false when the window is closing and the project has one or more directory paths', () => { editor.setText('changed') - expect(editor.shouldPromptToSave({windowCloseRequested: true, projectHasPaths: true})).toBeFalsy() + expect( + editor.shouldPromptToSave({ + windowCloseRequested: true, + projectHasPaths: true + }) + ).toBeFalsy() }) it('returns false when the window is closing and the project has no directory paths', () => { editor.setText('changed') - expect(editor.shouldPromptToSave({windowCloseRequested: true, projectHasPaths: false})).toBeTruthy() + expect( + editor.shouldPromptToSave({ + windowCloseRequested: true, + projectHasPaths: false + }) + ).toBeTruthy() }) }) @@ -7196,63 +8262,105 @@ describe('TextEditor', () => { editor.setSelectedBufferRange([[4, 5], [7, 5]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {') - expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();') - expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(4)).toBe( + ' // while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' // current = items.shift();' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' // current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(7)).toBe(' // }') expect(editor.getSelectedBufferRange()).toEqual([[4, 8], [7, 8]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(5)).toBe(' current = items.shift();') - expect(editor.lineTextForBufferRow(6)).toBe(' current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' current = items.shift();' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(7)).toBe(' }') }) it('does not comment the last line of a non-empty selection if it ends at column 0', () => { editor.setSelectedBufferRange([[4, 5], [7, 0]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {') - expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();') - expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(4)).toBe( + ' // while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' // current = items.shift();' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' // current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(7)).toBe(' }') }) it('uncomments lines if all lines match the comment regex', () => { editor.setSelectedBufferRange([[0, 0], [0, 1]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {') + expect(editor.lineTextForBufferRow(0)).toBe( + '// var quicksort = function () {' + ) editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(0)).toBe('// // var quicksort = function () {') - expect(editor.lineTextForBufferRow(1)).toBe('// var sort = function(items) {') - expect(editor.lineTextForBufferRow(2)).toBe('// if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(0)).toBe( + '// // var quicksort = function () {' + ) + expect(editor.lineTextForBufferRow(1)).toBe( + '// var sort = function(items) {' + ) + expect(editor.lineTextForBufferRow(2)).toBe( + '// if (items.length <= 1) return items;' + ) editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {') - expect(editor.lineTextForBufferRow(1)).toBe(' var sort = function(items) {') - expect(editor.lineTextForBufferRow(2)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForBufferRow(0)).toBe( + '// var quicksort = function () {' + ) + expect(editor.lineTextForBufferRow(1)).toBe( + ' var sort = function(items) {' + ) + expect(editor.lineTextForBufferRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) editor.setSelectedBufferRange([[0, 0], [0, Infinity]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () {') + expect(editor.lineTextForBufferRow(0)).toBe( + 'var quicksort = function () {' + ) }) it('uncomments commented lines separated by an empty line', () => { editor.setSelectedBufferRange([[0, 0], [1, Infinity]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(0)).toBe('// var quicksort = function () {') - expect(editor.lineTextForBufferRow(1)).toBe('// var sort = function(items) {') + expect(editor.lineTextForBufferRow(0)).toBe( + '// var quicksort = function () {' + ) + expect(editor.lineTextForBufferRow(1)).toBe( + '// var sort = function(items) {' + ) editor.getBuffer().insert([0, Infinity], '\n') editor.setSelectedBufferRange([[0, 0], [2, Infinity]]) editor.toggleLineCommentsInSelection() - expect(editor.lineTextForBufferRow(0)).toBe('var quicksort = function () {') + expect(editor.lineTextForBufferRow(0)).toBe( + 'var quicksort = function () {' + ) expect(editor.lineTextForBufferRow(1)).toBe('') - expect(editor.lineTextForBufferRow(2)).toBe(' var sort = function(items) {') + expect(editor.lineTextForBufferRow(2)).toBe( + ' var sort = function(items) {' + ) }) it('preserves selection emptiness', () => { @@ -7262,7 +8370,9 @@ describe('TextEditor', () => { }) it('does not explode if the current language mode has no comment regex', () => { - const editor = new TextEditor({buffer: new TextBuffer({text: 'hello'})}) + const editor = new TextEditor({ + buffer: new TextBuffer({ text: 'hello' }) + }) editor.setSelectedBufferRange([[0, 0], [0, 5]]) editor.toggleLineCommentsInSelection() expect(editor.lineTextForBufferRow(0)).toBe('hello') @@ -7340,35 +8450,50 @@ describe('TextEditor', () => { expect(editor.lineTextForBufferRow(0)).toBe('/* body {') expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px; */') expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%;') - expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;') + expect(editor.lineTextForBufferRow(3)).toBe( + ' font-weight: bold !important;' + ) editor.toggleLineCommentsForBufferRows(2, 2) expect(editor.lineTextForBufferRow(0)).toBe('/* body {') expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px; */') expect(editor.lineTextForBufferRow(2)).toBe(' /* width: 110%; */') - expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;') + expect(editor.lineTextForBufferRow(3)).toBe( + ' font-weight: bold !important;' + ) editor.toggleLineCommentsForBufferRows(0, 1) expect(editor.lineTextForBufferRow(0)).toBe('body {') expect(editor.lineTextForBufferRow(1)).toBe(' font-size: 1234px;') expect(editor.lineTextForBufferRow(2)).toBe(' /* width: 110%; */') - expect(editor.lineTextForBufferRow(3)).toBe(' font-weight: bold !important;') + expect(editor.lineTextForBufferRow(3)).toBe( + ' font-weight: bold !important;' + ) }) it('uncomments lines with leading whitespace', () => { - editor.setTextInBufferRange([[2, 0], [2, Infinity]], ' /* width: 110%; */') + editor.setTextInBufferRange( + [[2, 0], [2, Infinity]], + ' /* width: 110%; */' + ) editor.toggleLineCommentsForBufferRows(2, 2) expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%;') }) it('uncomments lines with trailing whitespace', () => { - editor.setTextInBufferRange([[2, 0], [2, Infinity]], '/* width: 110%; */ ') + editor.setTextInBufferRange( + [[2, 0], [2, Infinity]], + '/* width: 110%; */ ' + ) editor.toggleLineCommentsForBufferRows(2, 2) expect(editor.lineTextForBufferRow(2)).toBe('width: 110%; ') }) it('uncomments lines with leading and trailing whitespace', () => { - editor.setTextInBufferRange([[2, 0], [2, Infinity]], ' /* width: 110%; */ ') + editor.setTextInBufferRange( + [[2, 0], [2, Infinity]], + ' /* width: 110%; */ ' + ) editor.toggleLineCommentsForBufferRows(2, 2) expect(editor.lineTextForBufferRow(2)).toBe(' width: 110%; ') }) @@ -7382,7 +8507,9 @@ describe('TextEditor', () => { it('comments/uncomments lines in the given range', () => { editor.toggleLineCommentsForBufferRows(4, 6) - expect(editor.lineTextForBufferRow(4)).toBe(' # pivot = items.shift()') + expect(editor.lineTextForBufferRow(4)).toBe( + ' # pivot = items.shift()' + ) expect(editor.lineTextForBufferRow(5)).toBe(' # left = []') expect(editor.lineTextForBufferRow(6)).toBe(' # right = []') @@ -7394,7 +8521,9 @@ describe('TextEditor', () => { it('comments/uncomments empty lines', () => { editor.toggleLineCommentsForBufferRows(4, 7) - expect(editor.lineTextForBufferRow(4)).toBe(' # pivot = items.shift()') + expect(editor.lineTextForBufferRow(4)).toBe( + ' # pivot = items.shift()' + ) expect(editor.lineTextForBufferRow(5)).toBe(' # left = []') expect(editor.lineTextForBufferRow(6)).toBe(' # right = []') expect(editor.lineTextForBufferRow(7)).toBe(' # ') @@ -7415,15 +8544,27 @@ describe('TextEditor', () => { it('comments/uncomments lines in the given range', () => { editor.toggleLineCommentsForBufferRows(4, 7) - expect(editor.lineTextForBufferRow(4)).toBe(' // while(items.length > 0) {') - expect(editor.lineTextForBufferRow(5)).toBe(' // current = items.shift();') - expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(4)).toBe( + ' // while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' // current = items.shift();' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' // current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(7)).toBe(' // }') editor.toggleLineCommentsForBufferRows(4, 5) - expect(editor.lineTextForBufferRow(4)).toBe(' while(items.length > 0) {') - expect(editor.lineTextForBufferRow(5)).toBe(' current = items.shift();') - expect(editor.lineTextForBufferRow(6)).toBe(' // current < pivot ? left.push(current) : right.push(current);') + expect(editor.lineTextForBufferRow(4)).toBe( + ' while(items.length > 0) {' + ) + expect(editor.lineTextForBufferRow(5)).toBe( + ' current = items.shift();' + ) + expect(editor.lineTextForBufferRow(6)).toBe( + ' // current < pivot ? left.push(current) : right.push(current);' + ) expect(editor.lineTextForBufferRow(7)).toBe(' // }') editor.setText('\tvar i;') @@ -7462,7 +8603,7 @@ describe('TextEditor', () => { }) it('maintains cursor buffer position when a folding/unfolding', async () => { - editor = await atom.workspace.open('sample.js', {autoIndent: false}) + editor = await atom.workspace.open('sample.js', { autoIndent: false }) editor.setCursorBufferPosition([5, 5]) editor.foldAll() expect(editor.getCursorBufferPosition()).toEqual([5, 5]) @@ -7470,7 +8611,7 @@ describe('TextEditor', () => { describe('.unfoldAll()', () => { it('unfolds every folded line', async () => { - editor = await atom.workspace.open('sample.js', {autoIndent: false}) + editor = await atom.workspace.open('sample.js', { autoIndent: false }) const initialScreenLineCount = editor.getScreenLineCount() editor.foldBufferRow(0) @@ -7481,7 +8622,9 @@ describe('TextEditor', () => { }) it('unfolds every folded line with comments', async () => { - editor = await atom.workspace.open('sample-with-comments.js', {autoIndent: false}) + editor = await atom.workspace.open('sample-with-comments.js', { + autoIndent: false + }) const initialScreenLineCount = editor.getScreenLineCount() editor.foldBufferRow(0) @@ -7494,7 +8637,7 @@ describe('TextEditor', () => { describe('.foldAll()', () => { it('folds every foldable line', async () => { - editor = await atom.workspace.open('sample.js', {autoIndent: false}) + editor = await atom.workspace.open('sample.js', { autoIndent: false }) editor.foldAll() const [fold1, fold2, fold3] = editor.unfoldAll() @@ -7568,26 +8711,40 @@ describe('TextEditor', () => { describe('.foldAllAtIndentLevel(indentLevel)', () => { it('folds blocks of text at the given indentation level', async () => { - editor = await atom.workspace.open('sample.js', {autoIndent: false}) + editor = await atom.workspace.open('sample.js', { autoIndent: false }) editor.foldAllAtIndentLevel(0) - expect(editor.lineTextForScreenRow(0)).toBe(`var quicksort = function () {${editor.displayLayer.foldCharacter}};`) + expect(editor.lineTextForScreenRow(0)).toBe( + `var quicksort = function () {${editor.displayLayer.foldCharacter}};` + ) expect(editor.getLastScreenRow()).toBe(0) editor.foldAllAtIndentLevel(1) - expect(editor.lineTextForScreenRow(0)).toBe('var quicksort = function () {') - expect(editor.lineTextForScreenRow(1)).toBe(` var sort = function(items) {${editor.displayLayer.foldCharacter}};`) + expect(editor.lineTextForScreenRow(0)).toBe( + 'var quicksort = function () {' + ) + expect(editor.lineTextForScreenRow(1)).toBe( + ` var sort = function(items) {${editor.displayLayer.foldCharacter}};` + ) expect(editor.getLastScreenRow()).toBe(4) editor.foldAllAtIndentLevel(2) - expect(editor.lineTextForScreenRow(0)).toBe('var quicksort = function () {') - expect(editor.lineTextForScreenRow(1)).toBe(' var sort = function(items) {') - expect(editor.lineTextForScreenRow(2)).toBe(' if (items.length <= 1) return items;') + expect(editor.lineTextForScreenRow(0)).toBe( + 'var quicksort = function () {' + ) + expect(editor.lineTextForScreenRow(1)).toBe( + ' var sort = function(items) {' + ) + expect(editor.lineTextForScreenRow(2)).toBe( + ' if (items.length <= 1) return items;' + ) expect(editor.getLastScreenRow()).toBe(9) }) it('does not fold anything but the indentLevel', async () => { - editor = await atom.workspace.open('sample-with-comments.js', {autoIndent: false}) + editor = await atom.workspace.open('sample-with-comments.js', { + autoIndent: false + }) editor.foldAllAtIndentLevel(0) const folds = editor.unfoldAll() diff --git a/spec/text-mate-language-mode-spec.js b/spec/text-mate-language-mode-spec.js index 226a828e7..2fd155e6a 100644 --- a/spec/text-mate-language-mode-spec.js +++ b/spec/text-mate-language-mode-spec.js @@ -1,12 +1,10 @@ const NullGrammar = require('../src/null-grammar') const TextMateLanguageMode = require('../src/text-mate-language-mode') const TextBuffer = require('text-buffer') -const {Point, Range} = TextBuffer +const { Point } = TextBuffer const _ = require('underscore-plus') const dedent = require('dedent') -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') -// Tests crash the renderer process on Electron 3.0, disabling for now. describe('TextMateLanguageMode', () => { let languageMode, buffer, config @@ -41,15 +39,15 @@ describe('TextMateLanguageMode', () => { // It treats the entire line as one big token let iterator = languageMode.buildHighlightIterator() - iterator.seek({row: 0, column: 0}) + iterator.seek({ row: 0, column: 0 }) iterator.moveToSuccessor() - expect(iterator.getPosition()).toEqual({row: 0, column: 7}) + expect(iterator.getPosition()).toEqual({ row: 0, column: 7 }) buffer.insert([0, 0], 'hey"') iterator = languageMode.buildHighlightIterator() - iterator.seek({row: 0, column: 0}) + iterator.seek({ row: 0, column: 0 }) iterator.moveToSuccessor() - expect(iterator.getPosition()).toEqual({row: 0, column: 11}) + expect(iterator.getPosition()).toEqual({ row: 0, column: 11 }) }) }) @@ -57,7 +55,11 @@ describe('TextMateLanguageMode', () => { describe('when the buffer is destroyed', () => { beforeEach(() => { buffer = atom.project.bufferForPathSync('sample.js') - languageMode = new TextMateLanguageMode({buffer, config, config, grammar: atom.grammars.grammarForScopeName('source.js')}) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.js') + }) languageMode.startTokenizing() }) @@ -72,7 +74,11 @@ describe('TextMateLanguageMode', () => { describe('when the buffer contains soft-tabs', () => { beforeEach(() => { buffer = atom.project.bufferForPathSync('sample.js') - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.grammarForScopeName('source.js')}) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.js') + }) buffer.setLanguageMode(languageMode) languageMode.startTokenizing() }) @@ -106,8 +112,7 @@ describe('TextMateLanguageMode', () => { advanceClock() expect(languageMode.tokenizedLines[10].ruleStack != null).toBeTruthy() expect(languageMode.tokenizedLines[12].ruleStack != null).toBeTruthy() - }) - ) + })) describe('when the buffer is partially tokenized', () => { beforeEach(() => { @@ -169,22 +174,44 @@ describe('TextMateLanguageMode', () => { it('updates tokens to reflect the change', () => { buffer.setTextInRange([[0, 0], [2, 0]], 'foo()\n7\n') - expect(languageMode.tokenizedLines[0].tokens[1]).toEqual({value: '(', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.begin.bracket.round.js']}) - expect(languageMode.tokenizedLines[1].tokens[0]).toEqual({value: '7', scopes: ['source.js', 'constant.numeric.decimal.js']}) + expect(languageMode.tokenizedLines[0].tokens[1]).toEqual({ + value: '(', + scopes: [ + 'source.js', + 'meta.function-call.js', + 'meta.arguments.js', + 'punctuation.definition.arguments.begin.bracket.round.js' + ] + }) + expect(languageMode.tokenizedLines[1].tokens[0]).toEqual({ + value: '7', + scopes: ['source.js', 'constant.numeric.decimal.js'] + }) // line 2 is unchanged - expect(languageMode.tokenizedLines[2].tokens[1]).toEqual({value: 'if', scopes: ['source.js', 'keyword.control.js']}) + expect(languageMode.tokenizedLines[2].tokens[1]).toEqual({ + value: 'if', + scopes: ['source.js', 'keyword.control.js'] + }) }) describe('when the change invalidates the tokenization of subsequent lines', () => { it('schedules the invalidated lines to be tokenized in the background', () => { buffer.insert([5, 30], '/* */') buffer.insert([2, 0], '/*') - expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual(['source.js']) + expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual( + ['source.js'] + ) advanceClock() - expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[4].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[5].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) + expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual( + ['source.js', 'comment.block.js'] + ) + expect(languageMode.tokenizedLines[4].tokens[0].scopes).toEqual( + ['source.js', 'comment.block.js'] + ) + expect(languageMode.tokenizedLines[5].tokens[0].scopes).toEqual( + ['source.js', 'comment.block.js'] + ) }) }) @@ -193,7 +220,10 @@ describe('TextMateLanguageMode', () => { buffer.insert([5, 0], '*/') buffer.insert([1, 0], 'var ') - expect(languageMode.tokenizedLines[1].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) + expect(languageMode.tokenizedLines[1].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) }) }) @@ -202,16 +232,38 @@ describe('TextMateLanguageMode', () => { buffer.setTextInRange([[1, 0], [3, 0]], 'foo()') // previous line 0 remains - expect(languageMode.tokenizedLines[0].tokens[0]).toEqual({value: 'var', scopes: ['source.js', 'storage.type.var.js']}) + expect(languageMode.tokenizedLines[0].tokens[0]).toEqual({ + value: 'var', + scopes: ['source.js', 'storage.type.var.js'] + }) // previous line 3 should be combined with input to form line 1 - expect(languageMode.tokenizedLines[1].tokens[0]).toEqual({value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js']}) - expect(languageMode.tokenizedLines[1].tokens[6]).toEqual({value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']}) + expect(languageMode.tokenizedLines[1].tokens[0]).toEqual({ + value: 'foo', + scopes: [ + 'source.js', + 'meta.function-call.js', + 'entity.name.function.js' + ] + }) + expect(languageMode.tokenizedLines[1].tokens[6]).toEqual({ + value: '=', + scopes: ['source.js', 'keyword.operator.assignment.js'] + }) // lines below deleted regions should be shifted upward - expect(languageMode.tokenizedLines[2].tokens[1]).toEqual({value: 'while', scopes: ['source.js', 'keyword.control.js']}) - expect(languageMode.tokenizedLines[3].tokens[1]).toEqual({value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']}) - expect(languageMode.tokenizedLines[4].tokens[1]).toEqual({value: '<', scopes: ['source.js', 'keyword.operator.comparison.js']}) + expect(languageMode.tokenizedLines[2].tokens[1]).toEqual({ + value: 'while', + scopes: ['source.js', 'keyword.control.js'] + }) + expect(languageMode.tokenizedLines[3].tokens[1]).toEqual({ + value: '=', + scopes: ['source.js', 'keyword.operator.assignment.js'] + }) + expect(languageMode.tokenizedLines[4].tokens[1]).toEqual({ + value: '<', + scopes: ['source.js', 'keyword.operator.comparison.js'] + }) }) }) @@ -219,33 +271,85 @@ describe('TextMateLanguageMode', () => { it('schedules the invalidated lines to be tokenized in the background', () => { buffer.insert([5, 30], '/* */') buffer.setTextInRange([[2, 0], [3, 0]], '/*') - expect(languageMode.tokenizedLines[2].tokens[0].scopes).toEqual(['source.js', 'comment.block.js', 'punctuation.definition.comment.begin.js']) - expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual(['source.js']) + expect(languageMode.tokenizedLines[2].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js', + 'punctuation.definition.comment.begin.js' + ]) + expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual([ + 'source.js' + ]) advanceClock() - expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[4].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) + expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) + expect(languageMode.tokenizedLines[4].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) }) }) describe('when lines are both updated and inserted', () => { it('updates tokens to reflect the change', () => { - buffer.setTextInRange([[1, 0], [2, 0]], 'foo()\nbar()\nbaz()\nquux()') + buffer.setTextInRange( + [[1, 0], [2, 0]], + 'foo()\nbar()\nbaz()\nquux()' + ) // previous line 0 remains - expect(languageMode.tokenizedLines[0].tokens[0]).toEqual({ value: 'var', scopes: ['source.js', 'storage.type.var.js']}) + expect(languageMode.tokenizedLines[0].tokens[0]).toEqual({ + value: 'var', + scopes: ['source.js', 'storage.type.var.js'] + }) // 3 new lines inserted - expect(languageMode.tokenizedLines[1].tokens[0]).toEqual({value: 'foo', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js']}) - expect(languageMode.tokenizedLines[2].tokens[0]).toEqual({value: 'bar', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js']}) - expect(languageMode.tokenizedLines[3].tokens[0]).toEqual({value: 'baz', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js']}) + expect(languageMode.tokenizedLines[1].tokens[0]).toEqual({ + value: 'foo', + scopes: [ + 'source.js', + 'meta.function-call.js', + 'entity.name.function.js' + ] + }) + expect(languageMode.tokenizedLines[2].tokens[0]).toEqual({ + value: 'bar', + scopes: [ + 'source.js', + 'meta.function-call.js', + 'entity.name.function.js' + ] + }) + expect(languageMode.tokenizedLines[3].tokens[0]).toEqual({ + value: 'baz', + scopes: [ + 'source.js', + 'meta.function-call.js', + 'entity.name.function.js' + ] + }) // previous line 2 is joined with quux() on line 4 - expect(languageMode.tokenizedLines[4].tokens[0]).toEqual({value: 'quux', scopes: ['source.js', 'meta.function-call.js', 'entity.name.function.js']}) - expect(languageMode.tokenizedLines[4].tokens[4]).toEqual({value: 'if', scopes: ['source.js', 'keyword.control.js']}) + expect(languageMode.tokenizedLines[4].tokens[0]).toEqual({ + value: 'quux', + scopes: [ + 'source.js', + 'meta.function-call.js', + 'entity.name.function.js' + ] + }) + expect(languageMode.tokenizedLines[4].tokens[4]).toEqual({ + value: 'if', + scopes: ['source.js', 'keyword.control.js'] + }) // previous line 3 is pushed down to become line 5 - expect(languageMode.tokenizedLines[5].tokens[3]).toEqual({value: '=', scopes: ['source.js', 'keyword.operator.assignment.js']}) + expect(languageMode.tokenizedLines[5].tokens[3]).toEqual({ + value: '=', + scopes: ['source.js', 'keyword.operator.assignment.js'] + }) }) }) @@ -253,31 +357,66 @@ describe('TextMateLanguageMode', () => { it('schedules the invalidated lines to be tokenized in the background', () => { buffer.insert([5, 30], '/* */') buffer.insert([2, 0], '/*\nabcde\nabcder') - expect(languageMode.tokenizedLines[2].tokens[0].scopes).toEqual(['source.js', 'comment.block.js', 'punctuation.definition.comment.begin.js']) - expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[4].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[5].tokens[0].scopes).toEqual(['source.js']) + expect(languageMode.tokenizedLines[2].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js', + 'punctuation.definition.comment.begin.js' + ]) + expect(languageMode.tokenizedLines[3].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) + expect(languageMode.tokenizedLines[4].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) + expect(languageMode.tokenizedLines[5].tokens[0].scopes).toEqual([ + 'source.js' + ]) advanceClock() // tokenize invalidated lines in background - expect(languageMode.tokenizedLines[5].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[6].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[7].tokens[0].scopes).toEqual(['source.js', 'comment.block.js']) - expect(languageMode.tokenizedLines[8].tokens[0].scopes).not.toBe(['source.js', 'comment.block.js']) + expect(languageMode.tokenizedLines[5].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) + expect(languageMode.tokenizedLines[6].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) + expect(languageMode.tokenizedLines[7].tokens[0].scopes).toEqual([ + 'source.js', + 'comment.block.js' + ]) + expect(languageMode.tokenizedLines[8].tokens[0].scopes).not.toBe([ + 'source.js', + 'comment.block.js' + ]) }) }) }) describe('when there is an insertion that is larger than the chunk size', () => { it('tokenizes the initial chunk synchronously, then tokenizes the remaining lines in the background', () => { - const commentBlock = _.multiplyString('// a comment\n', languageMode.chunkSize + 2) + const commentBlock = _.multiplyString( + '// a comment\n', + languageMode.chunkSize + 2 + ) buffer.insert([0, 0], commentBlock) - expect(languageMode.tokenizedLines[0].ruleStack != null).toBeTruthy() - expect(languageMode.tokenizedLines[4].ruleStack != null).toBeTruthy() + expect( + languageMode.tokenizedLines[0].ruleStack != null + ).toBeTruthy() + expect( + languageMode.tokenizedLines[4].ruleStack != null + ).toBeTruthy() expect(languageMode.tokenizedLines[5]).toBeUndefined() advanceClock() - expect(languageMode.tokenizedLines[5].ruleStack != null).toBeTruthy() - expect(languageMode.tokenizedLines[6].ruleStack != null).toBeTruthy() + expect( + languageMode.tokenizedLines[5].ruleStack != null + ).toBeTruthy() + expect( + languageMode.tokenizedLines[6].ruleStack != null + ).toBeTruthy() }) }) }) @@ -288,7 +427,11 @@ describe('TextMateLanguageMode', () => { atom.packages.activatePackage('language-coffee-script') buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee') - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.grammarForScopeName('source.coffee')}) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.coffee') + }) languageMode.startTokenizing() }) @@ -329,7 +472,9 @@ describe('TextMateLanguageMode', () => { let tokenizationCount = 0 const editor = await atom.workspace.open('coffee.coffee') - editor.onDidTokenize(() => { tokenizationCount++ }) + editor.onDidTokenize(() => { + tokenizationCount++ + }) fullyTokenize(editor.getBuffer().getLanguageMode()) tokenizationCount = 0 @@ -345,7 +490,11 @@ describe('TextMateLanguageMode', () => { buffer = atom.project.bufferForPathSync() buffer.setText("
    <%= User.find(2).full_name %>
    ") - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.selectGrammar('test.erb')}) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.selectGrammar('test.erb') + }) fullyTokenize(languageMode) expect(languageMode.tokenizedLines[0].tokens[0]).toEqual({ value: "
    ", @@ -356,7 +505,11 @@ describe('TextMateLanguageMode', () => { fullyTokenize(languageMode) expect(languageMode.tokenizedLines[0].tokens[0]).toEqual({ value: '<', - scopes: ['text.html.ruby', 'meta.tag.block.div.html', 'punctuation.definition.tag.begin.html'] + scopes: [ + 'text.html.ruby', + 'meta.tag.block.div.html', + 'punctuation.definition.tag.begin.html' + ] }) }) }) @@ -364,9 +517,11 @@ describe('TextMateLanguageMode', () => { describe('when the buffer is configured with the null grammar', () => { it('does not actually tokenize using the grammar', () => { spyOn(NullGrammar, 'tokenizeLine').andCallThrough() - buffer = atom.project.bufferForPathSync('sample.will-use-the-null-grammar') + buffer = atom.project.bufferForPathSync( + 'sample.will-use-the-null-grammar' + ) buffer.setText('a\nb\nc') - languageMode = new TextMateLanguageMode({buffer, config}) + languageMode = new TextMateLanguageMode({ buffer, config }) const tokenizeCallback = jasmine.createSpy('onDidTokenize') languageMode.onDidTokenize(tokenizeCallback) @@ -394,35 +549,64 @@ describe('TextMateLanguageMode', () => { it('returns the correct token (regression)', () => { buffer = atom.project.bufferForPathSync('sample.js') - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.grammarForScopeName('source.js')}) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.js') + }) fullyTokenize(languageMode) - expect(languageMode.tokenForPosition([1, 0]).scopes).toEqual(['source.js']) - expect(languageMode.tokenForPosition([1, 1]).scopes).toEqual(['source.js']) - expect(languageMode.tokenForPosition([1, 2]).scopes).toEqual(['source.js', 'storage.type.var.js']) + expect(languageMode.tokenForPosition([1, 0]).scopes).toEqual([ + 'source.js' + ]) + expect(languageMode.tokenForPosition([1, 1]).scopes).toEqual([ + 'source.js' + ]) + expect(languageMode.tokenForPosition([1, 2]).scopes).toEqual([ + 'source.js', + 'storage.type.var.js' + ]) }) }) describe('.bufferRangeForScopeAtPosition(selector, position)', () => { beforeEach(() => { buffer = atom.project.bufferForPathSync('sample.js') - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.grammarForScopeName('source.js')}) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.js') + }) fullyTokenize(languageMode) }) describe('when the selector does not match the token at the position', () => - it('returns a falsy value', () => expect(languageMode.bufferRangeForScopeAtPosition('.bogus', [0, 1])).toBeUndefined()) - ) + it('returns a falsy value', () => + expect( + languageMode.bufferRangeForScopeAtPosition('.bogus', [0, 1]) + ).toBeUndefined())) describe('when the selector matches a single token at the position', () => { it('returns the range covered by the token', () => { - expect(languageMode.bufferRangeForScopeAtPosition('.storage.type.var.js', [0, 1])).toEqual([[0, 0], [0, 3]]) - expect(languageMode.bufferRangeForScopeAtPosition('.storage.type.var.js', [0, 3])).toEqual([[0, 0], [0, 3]]) + expect( + languageMode.bufferRangeForScopeAtPosition('.storage.type.var.js', [ + 0, + 1 + ]) + ).toEqual([[0, 0], [0, 3]]) + expect( + languageMode.bufferRangeForScopeAtPosition('.storage.type.var.js', [ + 0, + 3 + ]) + ).toEqual([[0, 0], [0, 3]]) }) }) describe('when the selector matches a run of multiple tokens at the position', () => { it('returns the range covered by all contiguous tokens (within a single line)', () => { - expect(languageMode.bufferRangeForScopeAtPosition('.function', [1, 18])).toEqual([[1, 6], [1, 28]]) + expect( + languageMode.bufferRangeForScopeAtPosition('.function', [1, 18]) + ).toEqual([[1, 6], [1, 28]]) }) }) }) @@ -431,7 +615,7 @@ describe('TextMateLanguageMode', () => { it("returns the tokenized line for a row, or a placeholder line if it hasn't been tokenized yet", () => { buffer = atom.project.bufferForPathSync('sample.js') const grammar = atom.grammars.grammarForScopeName('source.js') - languageMode = new TextMateLanguageMode({buffer, config, grammar}) + languageMode = new TextMateLanguageMode({ buffer, config, grammar }) const line0 = buffer.lineForRow(0) const jsScopeStartId = grammar.startIdForScope(grammar.scopeName) @@ -439,93 +623,220 @@ describe('TextMateLanguageMode', () => { languageMode.startTokenizing() expect(languageMode.tokenizedLines[0]).toBeUndefined() expect(languageMode.tokenizedLineForRow(0).text).toBe(line0) - expect(languageMode.tokenizedLineForRow(0).tags).toEqual([jsScopeStartId, line0.length, jsScopeEndId]) + expect(languageMode.tokenizedLineForRow(0).tags).toEqual([ + jsScopeStartId, + line0.length, + jsScopeEndId + ]) advanceClock(1) expect(languageMode.tokenizedLines[0]).not.toBeUndefined() expect(languageMode.tokenizedLineForRow(0).text).toBe(line0) - expect(languageMode.tokenizedLineForRow(0).tags).not.toEqual([jsScopeStartId, line0.length, jsScopeEndId]) + expect(languageMode.tokenizedLineForRow(0).tags).not.toEqual([ + jsScopeStartId, + line0.length, + jsScopeEndId + ]) }) it('returns undefined if the requested row is outside the buffer range', () => { buffer = atom.project.bufferForPathSync('sample.js') const grammar = atom.grammars.grammarForScopeName('source.js') - languageMode = new TextMateLanguageMode({buffer, config, grammar}) + languageMode = new TextMateLanguageMode({ buffer, config, grammar }) fullyTokenize(languageMode) expect(languageMode.tokenizedLineForRow(999)).toBeUndefined() }) }) describe('.buildHighlightIterator', () => { - const {TextMateHighlightIterator} = TextMateLanguageMode + const { TextMateHighlightIterator } = TextMateLanguageMode it('iterates over the syntactic scope boundaries', () => { - buffer = new TextBuffer({text: 'var foo = 1 /*\nhello*/var bar = 2\n'}) - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.grammarForScopeName('source.js')}) + buffer = new TextBuffer({ text: 'var foo = 1 /*\nhello*/var bar = 2\n' }) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.js') + }) fullyTokenize(languageMode) const iterator = languageMode.buildHighlightIterator() iterator.seek(Point(0, 0)) const expectedBoundaries = [ - {position: Point(0, 0), closeTags: [], openTags: ['syntax--source syntax--js', 'syntax--storage syntax--type syntax--var syntax--js']}, - {position: Point(0, 3), closeTags: ['syntax--storage syntax--type syntax--var syntax--js'], openTags: []}, - {position: Point(0, 8), closeTags: [], openTags: ['syntax--keyword syntax--operator syntax--assignment syntax--js']}, - {position: Point(0, 9), closeTags: ['syntax--keyword syntax--operator syntax--assignment syntax--js'], openTags: []}, - {position: Point(0, 10), closeTags: [], openTags: ['syntax--constant syntax--numeric syntax--decimal syntax--js']}, - {position: Point(0, 11), closeTags: ['syntax--constant syntax--numeric syntax--decimal syntax--js'], openTags: []}, - {position: Point(0, 12), closeTags: [], openTags: ['syntax--comment syntax--block syntax--js', 'syntax--punctuation syntax--definition syntax--comment syntax--begin syntax--js']}, - {position: Point(0, 14), closeTags: ['syntax--punctuation syntax--definition syntax--comment syntax--begin syntax--js'], openTags: []}, - {position: Point(1, 5), closeTags: [], openTags: ['syntax--punctuation syntax--definition syntax--comment syntax--end syntax--js']}, - {position: Point(1, 7), closeTags: ['syntax--punctuation syntax--definition syntax--comment syntax--end syntax--js', 'syntax--comment syntax--block syntax--js'], openTags: ['syntax--storage syntax--type syntax--var syntax--js']}, - {position: Point(1, 10), closeTags: ['syntax--storage syntax--type syntax--var syntax--js'], openTags: []}, - {position: Point(1, 15), closeTags: [], openTags: ['syntax--keyword syntax--operator syntax--assignment syntax--js']}, - {position: Point(1, 16), closeTags: ['syntax--keyword syntax--operator syntax--assignment syntax--js'], openTags: []}, - {position: Point(1, 17), closeTags: [], openTags: ['syntax--constant syntax--numeric syntax--decimal syntax--js']}, - {position: Point(1, 18), closeTags: ['syntax--constant syntax--numeric syntax--decimal syntax--js'], openTags: []} + { + position: Point(0, 0), + closeTags: [], + openTags: [ + 'syntax--source syntax--js', + 'syntax--storage syntax--type syntax--var syntax--js' + ] + }, + { + position: Point(0, 3), + closeTags: ['syntax--storage syntax--type syntax--var syntax--js'], + openTags: [] + }, + { + position: Point(0, 8), + closeTags: [], + openTags: [ + 'syntax--keyword syntax--operator syntax--assignment syntax--js' + ] + }, + { + position: Point(0, 9), + closeTags: [ + 'syntax--keyword syntax--operator syntax--assignment syntax--js' + ], + openTags: [] + }, + { + position: Point(0, 10), + closeTags: [], + openTags: [ + 'syntax--constant syntax--numeric syntax--decimal syntax--js' + ] + }, + { + position: Point(0, 11), + closeTags: [ + 'syntax--constant syntax--numeric syntax--decimal syntax--js' + ], + openTags: [] + }, + { + position: Point(0, 12), + closeTags: [], + openTags: [ + 'syntax--comment syntax--block syntax--js', + 'syntax--punctuation syntax--definition syntax--comment syntax--begin syntax--js' + ] + }, + { + position: Point(0, 14), + closeTags: [ + 'syntax--punctuation syntax--definition syntax--comment syntax--begin syntax--js' + ], + openTags: [] + }, + { + position: Point(1, 5), + closeTags: [], + openTags: [ + 'syntax--punctuation syntax--definition syntax--comment syntax--end syntax--js' + ] + }, + { + position: Point(1, 7), + closeTags: [ + 'syntax--punctuation syntax--definition syntax--comment syntax--end syntax--js', + 'syntax--comment syntax--block syntax--js' + ], + openTags: ['syntax--storage syntax--type syntax--var syntax--js'] + }, + { + position: Point(1, 10), + closeTags: ['syntax--storage syntax--type syntax--var syntax--js'], + openTags: [] + }, + { + position: Point(1, 15), + closeTags: [], + openTags: [ + 'syntax--keyword syntax--operator syntax--assignment syntax--js' + ] + }, + { + position: Point(1, 16), + closeTags: [ + 'syntax--keyword syntax--operator syntax--assignment syntax--js' + ], + openTags: [] + }, + { + position: Point(1, 17), + closeTags: [], + openTags: [ + 'syntax--constant syntax--numeric syntax--decimal syntax--js' + ] + }, + { + position: Point(1, 18), + closeTags: [ + 'syntax--constant syntax--numeric syntax--decimal syntax--js' + ], + openTags: [] + } ] while (true) { const boundary = { position: iterator.getPosition(), - closeTags: iterator.getCloseScopeIds().map(scopeId => languageMode.classNameForScopeId(scopeId)), - openTags: iterator.getOpenScopeIds().map(scopeId => languageMode.classNameForScopeId(scopeId)) + closeTags: iterator + .getCloseScopeIds() + .map(scopeId => languageMode.classNameForScopeId(scopeId)), + openTags: iterator + .getOpenScopeIds() + .map(scopeId => languageMode.classNameForScopeId(scopeId)) } expect(boundary).toEqual(expectedBoundaries.shift()) - if (!iterator.moveToSuccessor()) { break } + if (!iterator.moveToSuccessor()) { + break + } } - expect(iterator.seek(Point(0, 1)).map(scopeId => languageMode.classNameForScopeId(scopeId))).toEqual([ + expect( + iterator + .seek(Point(0, 1)) + .map(scopeId => languageMode.classNameForScopeId(scopeId)) + ).toEqual([ 'syntax--source syntax--js', 'syntax--storage syntax--type syntax--var syntax--js' ]) expect(iterator.getPosition()).toEqual(Point(0, 3)) - expect(iterator.seek(Point(0, 8)).map(scopeId => languageMode.classNameForScopeId(scopeId))).toEqual([ - 'syntax--source syntax--js' - ]) + expect( + iterator + .seek(Point(0, 8)) + .map(scopeId => languageMode.classNameForScopeId(scopeId)) + ).toEqual(['syntax--source syntax--js']) expect(iterator.getPosition()).toEqual(Point(0, 8)) - expect(iterator.seek(Point(1, 0)).map(scopeId => languageMode.classNameForScopeId(scopeId))).toEqual([ + expect( + iterator + .seek(Point(1, 0)) + .map(scopeId => languageMode.classNameForScopeId(scopeId)) + ).toEqual([ 'syntax--source syntax--js', 'syntax--comment syntax--block syntax--js' ]) expect(iterator.getPosition()).toEqual(Point(1, 0)) - expect(iterator.seek(Point(1, 18)).map(scopeId => languageMode.classNameForScopeId(scopeId))).toEqual([ + expect( + iterator + .seek(Point(1, 18)) + .map(scopeId => languageMode.classNameForScopeId(scopeId)) + ).toEqual([ 'syntax--source syntax--js', 'syntax--constant syntax--numeric syntax--decimal syntax--js' ]) expect(iterator.getPosition()).toEqual(Point(1, 18)) - expect(iterator.seek(Point(2, 0)).map(scopeId => languageMode.classNameForScopeId(scopeId))).toEqual([ - 'syntax--source syntax--js' - ]) + expect( + iterator + .seek(Point(2, 0)) + .map(scopeId => languageMode.classNameForScopeId(scopeId)) + ).toEqual(['syntax--source syntax--js']) iterator.moveToSuccessor() }) // ensure we don't infinitely loop (regression test) it('does not report columns beyond the length of the line', async () => { await atom.packages.activatePackage('language-coffee-script') - buffer = new TextBuffer({text: '# hello\n# world'}) - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.grammarForScopeName('source.coffee')}) + buffer = new TextBuffer({ text: '# hello\n# world' }) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.coffee') + }) fullyTokenize(languageMode) const iterator = languageMode.buildHighlightIterator() @@ -546,24 +857,32 @@ describe('TextMateLanguageMode', () => { it('correctly terminates scopes at the beginning of the line (regression)', () => { const grammar = atom.grammars.createGrammar('test', { - 'scopeName': 'text.broken', - 'name': 'Broken grammar', - 'patterns': [ - {'begin': 'start', 'end': '(?=end)', 'name': 'blue.broken'}, - {'match': '.', 'name': 'yellow.broken'} + scopeName: 'text.broken', + name: 'Broken grammar', + patterns: [ + { begin: 'start', end: '(?=end)', name: 'blue.broken' }, + { match: '.', name: 'yellow.broken' } ] }) - buffer = new TextBuffer({text: 'start x\nend x\nx'}) - languageMode = new TextMateLanguageMode({buffer, config, grammar}) + buffer = new TextBuffer({ text: 'start x\nend x\nx' }) + languageMode = new TextMateLanguageMode({ buffer, config, grammar }) fullyTokenize(languageMode) const iterator = languageMode.buildHighlightIterator() iterator.seek(Point(1, 0)) expect(iterator.getPosition()).toEqual([1, 0]) - expect(iterator.getCloseScopeIds().map(scopeId => languageMode.classNameForScopeId(scopeId))).toEqual(['syntax--blue syntax--broken']) - expect(iterator.getOpenScopeIds().map(scopeId => languageMode.classNameForScopeId(scopeId))).toEqual(['syntax--yellow syntax--broken']) + expect( + iterator + .getCloseScopeIds() + .map(scopeId => languageMode.classNameForScopeId(scopeId)) + ).toEqual(['syntax--blue syntax--broken']) + expect( + iterator + .getOpenScopeIds() + .map(scopeId => languageMode.classNameForScopeId(scopeId)) + ).toEqual(['syntax--yellow syntax--broken']) }) describe('TextMateHighlightIterator.seek(position)', function () { @@ -676,7 +995,7 @@ describe('TextMateLanguageMode', () => { describe('javascript', () => { beforeEach(async () => { - editor = await atom.workspace.open('sample.js', {autoIndent: false}) + editor = await atom.workspace.open('sample.js', { autoIndent: false }) await atom.packages.activatePackage('language-javascript') }) @@ -691,7 +1010,7 @@ describe('TextMateLanguageMode', () => { }) it('does not take invisibles into account', () => { - editor.update({showInvisibles: true}) + editor.update({ showInvisibles: true }) expect(editor.suggestedIndentForBufferRow(0)).toBe(0) expect(editor.suggestedIndentForBufferRow(1)).toBe(1) expect(editor.suggestedIndentForBufferRow(2)).toBe(2) @@ -704,7 +1023,7 @@ describe('TextMateLanguageMode', () => { describe('css', () => { beforeEach(async () => { - editor = await atom.workspace.open('css.css', {autoIndent: true}) + editor = await atom.workspace.open('css.css', { autoIndent: true }) await atom.packages.activatePackage('language-source') await atom.packages.activatePackage('language-css') }) @@ -717,11 +1036,17 @@ describe('TextMateLanguageMode', () => { }) describe('.isFoldableAtRow(row)', () => { + let editor + beforeEach(() => { buffer = atom.project.bufferForPathSync('sample.js') buffer.insert([10, 0], ' // multi-line\n // comment\n // block\n') buffer.insert([0, 0], '// multi-line\n// comment\n// block\n') - languageMode = new TextMateLanguageMode({buffer, config, grammar: atom.grammars.grammarForScopeName('source.js')}) + languageMode = new TextMateLanguageMode({ + buffer, + config, + grammar: atom.grammars.grammarForScopeName('source.js') + }) buffer.setLanguageMode(languageMode) fullyTokenize(languageMode) }) @@ -820,8 +1145,10 @@ describe('TextMateLanguageMode', () => { }) describe('.getFoldableRangesAtIndentLevel', () => { + let editor + it('returns the ranges that can be folded at the given indent level', () => { - buffer = new TextBuffer(dedent ` + buffer = new TextBuffer(dedent` if (a) { b(); if (c) { @@ -839,9 +1166,10 @@ describe('TextMateLanguageMode', () => { } `) - languageMode = new TextMateLanguageMode({buffer, config}) + languageMode = new TextMateLanguageMode({ buffer, config }) - expect(simulateFold(languageMode.getFoldableRangesAtIndentLevel(0, 2))).toBe(dedent ` + expect(simulateFold(languageMode.getFoldableRangesAtIndentLevel(0, 2))) + .toBe(dedent` if (a) {⋯ } i() @@ -849,7 +1177,8 @@ describe('TextMateLanguageMode', () => { } `) - expect(simulateFold(languageMode.getFoldableRangesAtIndentLevel(1, 2))).toBe(dedent ` + expect(simulateFold(languageMode.getFoldableRangesAtIndentLevel(1, 2))) + .toBe(dedent` if (a) { b(); if (c) {⋯ @@ -862,7 +1191,8 @@ describe('TextMateLanguageMode', () => { } `) - expect(simulateFold(languageMode.getFoldableRangesAtIndentLevel(2, 2))).toBe(dedent ` + expect(simulateFold(languageMode.getFoldableRangesAtIndentLevel(2, 2))) + .toBe(dedent` if (a) { b(); if (c) { @@ -897,7 +1227,7 @@ describe('TextMateLanguageMode', () => { describe('.getFoldableRanges', () => { it('returns the ranges that can be folded', () => { - buffer = new TextBuffer(dedent ` + buffer = new TextBuffer(dedent` if (a) { b(); if (c) { @@ -915,18 +1245,24 @@ describe('TextMateLanguageMode', () => { } `) - languageMode = new TextMateLanguageMode({buffer, config}) + languageMode = new TextMateLanguageMode({ buffer, config }) - expect(languageMode.getFoldableRanges(2).map(r => r.toString())).toEqual([ - ...languageMode.getFoldableRangesAtIndentLevel(0, 2), - ...languageMode.getFoldableRangesAtIndentLevel(1, 2), - ...languageMode.getFoldableRangesAtIndentLevel(2, 2), - ].sort((a, b) => (a.start.row - b.start.row) || (a.end.row - b.end.row)).map(r => r.toString())) + expect(languageMode.getFoldableRanges(2).map(r => r.toString())).toEqual( + [ + ...languageMode.getFoldableRangesAtIndentLevel(0, 2), + ...languageMode.getFoldableRangesAtIndentLevel(1, 2), + ...languageMode.getFoldableRangesAtIndentLevel(2, 2) + ] + .sort((a, b) => a.start.row - b.start.row || a.end.row - b.end.row) + .map(r => r.toString()) + ) }) it('works with multi-line comments', async () => { await atom.packages.activatePackage('language-javascript') - editor = await atom.workspace.open('sample-with-comments.js', {autoIndent: false}) + const editor = await atom.workspace.open('sample-with-comments.js', { + autoIndent: false + }) fullyTokenize(editor.getBuffer().getLanguageMode()) editor.foldAll() @@ -945,7 +1281,7 @@ describe('TextMateLanguageMode', () => { describe('.getFoldableRangeContainingPoint', () => { it('returns the range for the smallest fold that contains the given range', () => { - buffer = new TextBuffer(dedent ` + buffer = new TextBuffer(dedent` if (a) { b(); if (c) { @@ -963,12 +1299,14 @@ describe('TextMateLanguageMode', () => { } `) - languageMode = new TextMateLanguageMode({buffer, config}) + languageMode = new TextMateLanguageMode({ buffer, config }) - expect(languageMode.getFoldableRangeContainingPoint(Point(0, 5), 2)).toBeNull() + expect( + languageMode.getFoldableRangeContainingPoint(Point(0, 5), 2) + ).toBeNull() let range = languageMode.getFoldableRangeContainingPoint(Point(0, 10), 2) - expect(simulateFold([range])).toBe(dedent ` + expect(simulateFold([range])).toBe(dedent` if (a) {⋯ } i() @@ -978,7 +1316,7 @@ describe('TextMateLanguageMode', () => { `) range = languageMode.getFoldableRangeContainingPoint(Point(7, 0), 2) - expect(simulateFold([range])).toBe(dedent ` + expect(simulateFold([range])).toBe(dedent` if (a) { b(); if (c) {⋯ @@ -991,8 +1329,11 @@ describe('TextMateLanguageMode', () => { } `) - range = languageMode.getFoldableRangeContainingPoint(Point(1, Infinity), 2) - expect(simulateFold([range])).toBe(dedent ` + range = languageMode.getFoldableRangeContainingPoint( + Point(1, Infinity), + 2 + ) + expect(simulateFold([range])).toBe(dedent` if (a) {⋯ } i() @@ -1002,7 +1343,7 @@ describe('TextMateLanguageMode', () => { `) range = languageMode.getFoldableRangeContainingPoint(Point(2, 20), 2) - expect(simulateFold([range])).toBe(dedent ` + expect(simulateFold([range])).toBe(dedent` if (a) { b(); if (c) {⋯ @@ -1022,10 +1363,18 @@ describe('TextMateLanguageMode', () => { buffer = editor.buffer languageMode = editor.languageMode - expect(languageMode.getFoldableRangeContainingPoint(Point(0, Infinity), 2)).toEqual([[0, Infinity], [20, Infinity]]) - expect(languageMode.getFoldableRangeContainingPoint(Point(1, Infinity), 2)).toEqual([[1, Infinity], [17, Infinity]]) - expect(languageMode.getFoldableRangeContainingPoint(Point(2, Infinity), 2)).toEqual([[1, Infinity], [17, Infinity]]) - expect(languageMode.getFoldableRangeContainingPoint(Point(19, Infinity), 2)).toEqual([[19, Infinity], [20, Infinity]]) + expect( + languageMode.getFoldableRangeContainingPoint(Point(0, Infinity), 2) + ).toEqual([[0, Infinity], [20, Infinity]]) + expect( + languageMode.getFoldableRangeContainingPoint(Point(1, Infinity), 2) + ).toEqual([[1, Infinity], [17, Infinity]]) + expect( + languageMode.getFoldableRangeContainingPoint(Point(2, Infinity), 2) + ).toEqual([[1, Infinity], [17, Infinity]]) + expect( + languageMode.getFoldableRangeContainingPoint(Point(19, Infinity), 2) + ).toEqual([[19, Infinity], [20, Infinity]]) }) it('works for javascript', async () => { @@ -1034,16 +1383,39 @@ describe('TextMateLanguageMode', () => { buffer = editor.buffer languageMode = editor.languageMode - expect(editor.languageMode.getFoldableRangeContainingPoint(Point(0, Infinity), 2)).toEqual([[0, Infinity], [12, Infinity]]) - expect(editor.languageMode.getFoldableRangeContainingPoint(Point(1, Infinity), 2)).toEqual([[1, Infinity], [9, Infinity]]) - expect(editor.languageMode.getFoldableRangeContainingPoint(Point(2, Infinity), 2)).toEqual([[1, Infinity], [9, Infinity]]) - expect(editor.languageMode.getFoldableRangeContainingPoint(Point(4, Infinity), 2)).toEqual([[4, Infinity], [7, Infinity]]) + expect( + editor.languageMode.getFoldableRangeContainingPoint( + Point(0, Infinity), + 2 + ) + ).toEqual([[0, Infinity], [12, Infinity]]) + expect( + editor.languageMode.getFoldableRangeContainingPoint( + Point(1, Infinity), + 2 + ) + ).toEqual([[1, Infinity], [9, Infinity]]) + expect( + editor.languageMode.getFoldableRangeContainingPoint( + Point(2, Infinity), + 2 + ) + ).toEqual([[1, Infinity], [9, Infinity]]) + expect( + editor.languageMode.getFoldableRangeContainingPoint( + Point(4, Infinity), + 2 + ) + ).toEqual([[4, Infinity], [7, Infinity]]) }) it('searches upward and downward for surrounding comment lines and folds them as a single fold', async () => { await atom.packages.activatePackage('language-javascript') - editor = await atom.workspace.open('sample-with-comments.js') - editor.buffer.insert([1, 0], ' //this is a comment\n // and\n //more docs\n\n//second comment') + const editor = await atom.workspace.open('sample-with-comments.js') + editor.buffer.insert( + [1, 0], + ' //this is a comment\n // and\n //more docs\n\n//second comment' + ) fullyTokenize(editor.getBuffer().getLanguageMode()) editor.foldBufferRow(1) const [fold] = editor.unfoldAll() @@ -1054,26 +1426,28 @@ describe('TextMateLanguageMode', () => { describe('TokenIterator', () => it('correctly terminates scopes at the beginning of the line (regression)', () => { const grammar = atom.grammars.createGrammar('test', { - 'scopeName': 'text.broken', - 'name': 'Broken grammar', - 'patterns': [ + scopeName: 'text.broken', + name: 'Broken grammar', + patterns: [ { - 'begin': 'start', - 'end': '(?=end)', - 'name': 'blue.broken' + begin: 'start', + end: '(?=end)', + name: 'blue.broken' }, { - 'match': '.', - 'name': 'yellow.broken' + match: '.', + name: 'yellow.broken' } ] }) - const buffer = new TextBuffer({text: dedent` + const buffer = new TextBuffer({ + text: dedent` start x end x x - `}) + ` + }) const languageMode = new TextMateLanguageMode({ buffer, @@ -1086,14 +1460,18 @@ describe('TextMateLanguageMode', () => { fullyTokenize(languageMode) - const tokenIterator = languageMode.tokenizedLineForRow(1).getTokenIterator() + const tokenIterator = languageMode + .tokenizedLineForRow(1) + .getTokenIterator() tokenIterator.next() expect(tokenIterator.getBufferStart()).toBe(0) expect(tokenIterator.getScopeEnds()).toEqual([]) - expect(tokenIterator.getScopeStarts()).toEqual(['text.broken', 'yellow.broken']) - }) - ) + expect(tokenIterator.getScopeStarts()).toEqual([ + 'text.broken', + 'yellow.broken' + ]) + })) function simulateFold (ranges) { buffer.transact(() => { diff --git a/spec/text-utils-spec.js b/spec/text-utils-spec.js index 3a4b29866..ca0e2a6d7 100644 --- a/spec/text-utils-spec.js +++ b/spec/text-utils-spec.js @@ -4,7 +4,9 @@ describe('text utilities', () => { describe('.hasPairedCharacter(string)', () => it('returns true when the string contains a surrogate pair, variation sequence, or combined character', () => { expect(textUtils.hasPairedCharacter('abc')).toBe(false) - expect(textUtils.hasPairedCharacter('a\uD835\uDF97b\uD835\uDF97c')).toBe(true) + expect(textUtils.hasPairedCharacter('a\uD835\uDF97b\uD835\uDF97c')).toBe( + true + ) expect(textUtils.hasPairedCharacter('\uD835\uDF97')).toBe(true) expect(textUtils.hasPairedCharacter('\u2714\uFE0E')).toBe(true) expect(textUtils.hasPairedCharacter('e\u0301')).toBe(true) @@ -16,18 +18,31 @@ describe('text utilities', () => { expect(textUtils.hasPairedCharacter('\uFE0E\uFE0E')).toBe(false) expect(textUtils.hasPairedCharacter('\u0301\u0301')).toBe(false) - }) - ) + })) describe('.isPairedCharacter(string, index)', () => it('returns true when the index is the start of a high/low surrogate pair, variation sequence, or combined character', () => { - expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 0)).toBe(false) - expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 1)).toBe(true) - expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 2)).toBe(false) - expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 3)).toBe(false) - expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 4)).toBe(true) - expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 5)).toBe(false) - expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 6)).toBe(false) + expect( + textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 0) + ).toBe(false) + expect( + textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 1) + ).toBe(true) + expect( + textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 2) + ).toBe(false) + expect( + textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 3) + ).toBe(false) + expect( + textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 4) + ).toBe(true) + expect( + textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 5) + ).toBe(false) + expect( + textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 6) + ).toBe(false) expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 0)).toBe(false) expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 1)).toBe(true) @@ -46,8 +61,7 @@ describe('text utilities', () => { expect(textUtils.isPairedCharacter('ae\u0301c', 2)).toBe(false) expect(textUtils.isPairedCharacter('ae\u0301c', 3)).toBe(false) expect(textUtils.isPairedCharacter('ae\u0301c', 4)).toBe(false) - }) - ) + })) describe('.isDoubleWidthCharacter(character)', () => it('returns true when the character is either japanese, chinese or a full width form', () => { @@ -60,8 +74,7 @@ describe('text utilities', () => { expect(textUtils.isDoubleWidthCharacter('¢')).toBe(true) expect(textUtils.isDoubleWidthCharacter('a')).toBe(false) - }) - ) + })) describe('.isHalfWidthCharacter(character)', () => it('returns true when the character is an half width form', () => { @@ -71,8 +84,7 @@ describe('text utilities', () => { expect(textUtils.isHalfWidthCharacter('■')).toBe(true) expect(textUtils.isHalfWidthCharacter('B')).toBe(false) - }) - ) + })) describe('.isKoreanCharacter(character)', () => it('returns true when the character is a korean character', () => { @@ -82,8 +94,7 @@ describe('text utilities', () => { expect(textUtils.isKoreanCharacter('ㄼ')).toBe(true) expect(textUtils.isKoreanCharacter('O')).toBe(false) - }) - ) + })) describe('.isWrapBoundary(previousCharacter, character)', () => it('returns true when the character is CJK or when the previous character is a space/tab', () => { @@ -105,6 +116,5 @@ describe('text utilities', () => { expect(textUtils.isWrapBoundary(' ', 'h')).toBe(true) expect(textUtils.isWrapBoundary('\t', 'h')).toBe(true) expect(textUtils.isWrapBoundary('a', 'h')).toBe(false) - }) - ) + })) }) diff --git a/spec/theme-manager-spec.js b/spec/theme-manager-spec.js index 48bc17ebe..2e4f74b50 100644 --- a/spec/theme-manager-spec.js +++ b/spec/theme-manager-spec.js @@ -29,8 +29,7 @@ describe('atom.themes', function () { it('gets all the loaded themes', function () { const themes = atom.themes.getLoadedThemes() expect(themes.length).toBeGreaterThan(2) - }) - ) + })) describe('getActiveThemes', () => it('gets all the active themes', function () { @@ -42,8 +41,7 @@ describe('atom.themes', function () { const themes = atom.themes.getActiveThemes() expect(themes).toHaveLength(names.length) }) - }) - ) + })) }) describe('when the core.themes config value contains invalid entry', () => @@ -60,13 +58,19 @@ describe('atom.themes', function () { 'atom-dark-ui' ]) - expect(atom.themes.getEnabledThemeNames()).toEqual(['atom-dark-ui', 'atom-light-ui']) - }) -) + expect(atom.themes.getEnabledThemeNames()).toEqual([ + 'atom-dark-ui', + 'atom-light-ui' + ]) + })) describe('::getImportPaths()', function () { it('returns the theme directories before the themes are loaded', function () { - atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui']) + atom.config.set('core.themes', [ + 'theme-with-index-less', + 'atom-dark-ui', + 'atom-light-ui' + ]) const paths = atom.themes.getImportPaths() @@ -85,7 +89,9 @@ describe('atom.themes', function () { describe('when the core.themes config value changes', function () { it('add/removes stylesheets to reflect the new config value', function () { let didChangeActiveThemesHandler - atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy()) + atom.themes.onDidChangeActiveThemes( + (didChangeActiveThemesHandler = jasmine.createSpy()) + ) spyOn(atom.styles, 'getUserStyleSheetPath').andCallFake(() => null) waitsForPromise(() => atom.themes.activateThemes()) @@ -108,7 +114,11 @@ describe('atom.themes', function () { runs(function () { didChangeActiveThemesHandler.reset() expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2) - expect(document.querySelector('style[priority="1"]').getAttribute('source-path')).toMatch(/atom-dark-ui/) + expect( + document + .querySelector('style[priority="1"]') + .getAttribute('source-path') + ).toMatch(/atom-dark-ui/) atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui']) }) @@ -117,8 +127,16 @@ describe('atom.themes', function () { runs(function () { didChangeActiveThemesHandler.reset() expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2) - expect(document.querySelectorAll('style[priority="1"]')[0].getAttribute('source-path')).toMatch(/atom-dark-ui/) - expect(document.querySelectorAll('style[priority="1"]')[1].getAttribute('source-path')).toMatch(/atom-light-ui/) + expect( + document + .querySelectorAll('style[priority="1"]')[0] + .getAttribute('source-path') + ).toMatch(/atom-dark-ui/) + expect( + document + .querySelectorAll('style[priority="1"]')[1] + .getAttribute('source-path') + ).toMatch(/atom-light-ui/) atom.config.set('core.themes', []) }) @@ -128,7 +146,10 @@ describe('atom.themes', function () { didChangeActiveThemesHandler.reset() expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2) // atom-dark-ui has a directory path, the syntax one doesn't - atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui']) + atom.config.set('core.themes', [ + 'theme-with-index-less', + 'atom-dark-ui' + ]) }) waitsFor(() => didChangeActiveThemesHandler.callCount === 1) @@ -145,15 +166,22 @@ describe('atom.themes', function () { atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax']) let didChangeActiveThemesHandler - atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy()) + atom.themes.onDidChangeActiveThemes( + (didChangeActiveThemesHandler = jasmine.createSpy()) + ) waitsForPromise(() => atom.themes.activateThemes()) const workspaceElement = atom.workspace.getElement() runs(function () { expect(workspaceElement).toHaveClass('theme-atom-dark-ui') - atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy()) - atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables']) + atom.themes.onDidChangeActiveThemes( + (didChangeActiveThemesHandler = jasmine.createSpy()) + ) + atom.config.set('core.themes', [ + 'theme-with-ui-variables', + 'theme-with-syntax-variables' + ]) }) waitsFor(() => didChangeActiveThemesHandler.callCount > 0) @@ -161,7 +189,9 @@ describe('atom.themes', function () { runs(function () { // `theme-` twice as it prefixes the name with `theme-` expect(workspaceElement).toHaveClass('theme-theme-with-ui-variables') - expect(workspaceElement).toHaveClass('theme-theme-with-syntax-variables') + expect(workspaceElement).toHaveClass( + 'theme-theme-with-syntax-variables' + ) expect(workspaceElement).not.toHaveClass('theme-atom-dark-ui') expect(workspaceElement).not.toHaveClass('theme-atom-dark-syntax') }) @@ -171,11 +201,14 @@ describe('atom.themes', function () { describe('when a theme fails to load', () => it('logs a warning', function () { console.warn.reset() - atom.packages.activatePackage('a-theme-that-will-not-be-found').then(function () {}, function () {}) + atom.packages + .activatePackage('a-theme-that-will-not-be-found') + .then(function () {}, function () {}) expect(console.warn.callCount).toBe(1) - expect(console.warn.argsForCall[0][0]).toContain("Could not resolve 'a-theme-that-will-not-be-found'") - }) - ) + expect(console.warn.argsForCall[0][0]).toContain( + "Could not resolve 'a-theme-that-will-not-be-found'" + ) + })) describe('::requireStylesheet(path)', function () { beforeEach(() => jasmine.snapshotDeprecations()) @@ -184,38 +217,60 @@ describe('atom.themes', function () { it('synchronously loads css at the given path and installs a style tag for it in the head', function () { let styleElementAddedHandler - atom.styles.onDidAddStyleElement(styleElementAddedHandler = jasmine.createSpy('styleElementAddedHandler')) + atom.styles.onDidAddStyleElement( + (styleElementAddedHandler = jasmine.createSpy( + 'styleElementAddedHandler' + )) + ) - const cssPath = getAbsolutePath(atom.project.getDirectories()[0], 'css.css') + const cssPath = getAbsolutePath( + atom.project.getDirectories()[0], + 'css.css' + ) const lengthBefore = document.querySelectorAll('head style').length atom.themes.requireStylesheet(cssPath) - expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1) + expect(document.querySelectorAll('head style').length).toBe( + lengthBefore + 1 + ) expect(styleElementAddedHandler).toHaveBeenCalled() - const element = document.querySelector('head style[source-path*="css.css"]') + const element = document.querySelector( + 'head style[source-path*="css.css"]' + ) expect(element.getAttribute('source-path')).toEqualPath(cssPath) expect(element.textContent).toBe(fs.readFileSync(cssPath, 'utf8')) // doesn't append twice styleElementAddedHandler.reset() atom.themes.requireStylesheet(cssPath) - expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1) + expect(document.querySelectorAll('head style').length).toBe( + lengthBefore + 1 + ) expect(styleElementAddedHandler).not.toHaveBeenCalled() - document.querySelectorAll('head style[id*="css.css"]').forEach((styleElement) => { - styleElement.remove() - }) + document + .querySelectorAll('head style[id*="css.css"]') + .forEach(styleElement => { + styleElement.remove() + }) }) it('synchronously loads and parses less files at the given path and installs a style tag for it in the head', function () { - const lessPath = getAbsolutePath(atom.project.getDirectories()[0], 'sample.less') + const lessPath = getAbsolutePath( + atom.project.getDirectories()[0], + 'sample.less' + ) const lengthBefore = document.querySelectorAll('head style').length atom.themes.requireStylesheet(lessPath) - expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1) + expect(document.querySelectorAll('head style').length).toBe( + lengthBefore + 1 + ) - const element = document.querySelector('head style[source-path*="sample.less"]') + const element = document.querySelector( + 'head style[source-path*="sample.less"]' + ) expect(element.getAttribute('source-path')).toEqualPath(lessPath) expect(element.textContent.toLowerCase()).toBe(`\ #header { @@ -225,24 +280,37 @@ h2 { color: #4d926f; } \ -` - ) +`) // doesn't append twice atom.themes.requireStylesheet(lessPath) - expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1) - document.querySelectorAll('head style[id*="sample.less"]').forEach((styleElement) => { - styleElement.remove() - }) + expect(document.querySelectorAll('head style').length).toBe( + lengthBefore + 1 + ) + document + .querySelectorAll('head style[id*="sample.less"]') + .forEach(styleElement => { + styleElement.remove() + }) }) it('supports requiring css and less stylesheets without an explicit extension', function () { atom.themes.requireStylesheet(path.join(__dirname, 'fixtures', 'css')) - expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')) - .toEqualPath(getAbsolutePath(atom.project.getDirectories()[0], 'css.css')) + expect( + document + .querySelector('head style[source-path*="css.css"]') + .getAttribute('source-path') + ).toEqualPath( + getAbsolutePath(atom.project.getDirectories()[0], 'css.css') + ) atom.themes.requireStylesheet(path.join(__dirname, 'fixtures', 'sample')) - expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')) - .toEqualPath(getAbsolutePath(atom.project.getDirectories()[0], 'sample.less')) + expect( + document + .querySelector('head style[source-path*="sample.less"]') + .getAttribute('source-path') + ).toEqualPath( + getAbsolutePath(atom.project.getDirectories()[0], 'sample.less') + ) document.querySelector('head style[source-path*="css.css"]').remove() document.querySelector('head style[source-path*="sample.less"]').remove() @@ -251,12 +319,16 @@ h2 { it('returns a disposable allowing styles applied by the given path to be removed', function () { const cssPath = require.resolve('./fixtures/css.css') - expect(getComputedStyle(document.body).fontWeight).not.toBe('700') + expect(getComputedStyle(document.body).fontWeight).not.toBe('bold') const disposable = atom.themes.requireStylesheet(cssPath) - expect(getComputedStyle(document.body).fontWeight).toBe('700') + expect(getComputedStyle(document.body).fontWeight).toBe('bold') let styleElementRemovedHandler - atom.styles.onDidRemoveStyleElement(styleElementRemovedHandler = jasmine.createSpy('styleElementRemovedHandler')) + atom.styles.onDidRemoveStyleElement( + (styleElementRemovedHandler = jasmine.createSpy( + 'styleElementRemovedHandler' + )) + ) disposable.dispose() @@ -277,46 +349,74 @@ h2 { it("loads the correct values from the theme's ui-variables file", function () { let didChangeActiveThemesHandler - atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy()) - atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables']) + atom.themes.onDidChangeActiveThemes( + (didChangeActiveThemesHandler = jasmine.createSpy()) + ) + atom.config.set('core.themes', [ + 'theme-with-ui-variables', + 'theme-with-syntax-variables' + ]) waitsFor(() => didChangeActiveThemesHandler.callCount > 0) runs(function () { // an override loaded in the base css - expect(getComputedStyle(atom.workspace.getElement())['background-color']).toBe('rgb(0, 0, 255)') + expect( + getComputedStyle(atom.workspace.getElement())['background-color'] + ).toBe('rgb(0, 0, 255)') // from within the theme itself - expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingTop).toBe('150px') - expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingRight).toBe('150px') - expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingBottom).toBe('150px') + expect( + getComputedStyle(document.querySelector('atom-text-editor')) + .paddingTop + ).toBe('150px') + expect( + getComputedStyle(document.querySelector('atom-text-editor')) + .paddingRight + ).toBe('150px') + expect( + getComputedStyle(document.querySelector('atom-text-editor')) + .paddingBottom + ).toBe('150px') }) }) describe('when there is a theme with incomplete variables', () => it('loads the correct values from the fallback ui-variables', function () { let didChangeActiveThemesHandler - atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy()) - atom.config.set('core.themes', ['theme-with-incomplete-ui-variables', 'theme-with-syntax-variables']) + atom.themes.onDidChangeActiveThemes( + (didChangeActiveThemesHandler = jasmine.createSpy()) + ) + atom.config.set('core.themes', [ + 'theme-with-incomplete-ui-variables', + 'theme-with-syntax-variables' + ]) waitsFor(() => didChangeActiveThemesHandler.callCount > 0) runs(function () { // an override loaded in the base css - expect(getComputedStyle(atom.workspace.getElement())['background-color']).toBe('rgb(0, 0, 255)') + expect( + getComputedStyle(atom.workspace.getElement())['background-color'] + ).toBe('rgb(0, 0, 255)') // from within the theme itself - expect(getComputedStyle(document.querySelector('atom-text-editor')).backgroundColor).toBe('rgb(0, 152, 255)') + expect( + getComputedStyle(document.querySelector('atom-text-editor')) + .backgroundColor + ).toBe('rgb(0, 152, 255)') }) - }) - ) + })) }) describe('user stylesheet', function () { let userStylesheetPath beforeEach(function () { userStylesheetPath = path.join(temp.mkdirSync('atom'), 'styles.less') - fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}') + fs.writeFileSync( + userStylesheetPath, + 'body {border-style: dotted !important;}' + ) spyOn(atom.styles, 'getUserStyleSheetPath').andReturn(userStylesheetPath) }) @@ -331,8 +431,16 @@ h2 { waitsForPromise(() => atom.themes.activateThemes()) runs(function () { - atom.styles.onDidRemoveStyleElement(styleElementRemovedHandler = jasmine.createSpy('styleElementRemovedHandler')) - atom.styles.onDidAddStyleElement(styleElementAddedHandler = jasmine.createSpy('styleElementAddedHandler')) + atom.styles.onDidRemoveStyleElement( + (styleElementRemovedHandler = jasmine.createSpy( + 'styleElementRemovedHandler' + )) + ) + atom.styles.onDidAddStyleElement( + (styleElementAddedHandler = jasmine.createSpy( + 'styleElementAddedHandler' + )) + ) spyOn(atom.themes, 'loadUserStylesheet').andCallThrough() @@ -346,10 +454,14 @@ h2 { expect(getComputedStyle(document.body).borderStyle).toBe('dashed') expect(styleElementRemovedHandler).toHaveBeenCalled() - expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain('dotted') + expect( + styleElementRemovedHandler.argsForCall[0][0].textContent + ).toContain('dotted') expect(styleElementAddedHandler).toHaveBeenCalled() - expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain('dashed') + expect( + styleElementAddedHandler.argsForCall[0][0].textContent + ).toContain('dashed') styleElementRemovedHandler.reset() fs.removeSync(userStylesheetPath) @@ -359,7 +471,9 @@ h2 { runs(function () { expect(styleElementRemovedHandler).toHaveBeenCalled() - expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain('dashed') + expect( + styleElementRemovedHandler.argsForCall[0][0].textContent + ).toContain('dashed') expect(getComputedStyle(document.body).borderStyle).toBe('none') }) }) @@ -372,7 +486,9 @@ h2 { spyOn(atom.themes.lessCache, 'cssForFile').andCallFake(function () { throw new Error('EACCES permission denied "styles.less"') }) - atom.notifications.onDidAddNotification(addErrorHandler = jasmine.createSpy()) + atom.notifications.onDidAddNotification( + (addErrorHandler = jasmine.createSpy()) + ) }) it('creates an error notification and does not add the stylesheet', function () { @@ -381,21 +497,25 @@ h2 { const note = addErrorHandler.mostRecentCall.args[0] expect(note.getType()).toBe('error') expect(note.getMessage()).toContain('Error loading') - expect(atom.styles.styleElementsBySourcePath[atom.styles.getUserStyleSheetPath()]).toBeUndefined() + expect( + atom.styles.styleElementsBySourcePath[atom.styles.getUserStyleSheetPath()] + ).toBeUndefined() }) }) describe('when there is an error watching the user stylesheet', function () { let addErrorHandler = null beforeEach(function () { - const {File} = require('pathwatcher') + const { File } = require('pathwatcher') spyOn(File.prototype, 'on').andCallFake(function (event) { if (event.indexOf('contents-changed') > -1) { throw new Error('Unable to watch path') } }) spyOn(atom.themes, 'loadStylesheet').andReturn('') - atom.notifications.onDidAddNotification(addErrorHandler = jasmine.createSpy()) + atom.notifications.onDidAddNotification( + (addErrorHandler = jasmine.createSpy()) + ) }) it('creates an error notification', function () { @@ -410,16 +530,25 @@ h2 { it("adds a notification when a theme's stylesheet is invalid", function () { const addErrorHandler = jasmine.createSpy() atom.notifications.onDidAddNotification(addErrorHandler) - expect(() => atom.packages.activatePackage('theme-with-invalid-styles').then(function () {}, function () {})).not.toThrow() + expect(() => + atom.packages + .activatePackage('theme-with-invalid-styles') + .then(function () {}, function () {}) + ).not.toThrow() expect(addErrorHandler.callCount).toBe(2) - expect(addErrorHandler.argsForCall[1][0].message).toContain('Failed to activate the theme-with-invalid-styles theme') + expect(addErrorHandler.argsForCall[1][0].message).toContain( + 'Failed to activate the theme-with-invalid-styles theme' + ) }) }) describe('when a non-existent theme is present in the config', function () { beforeEach(function () { console.warn.reset() - atom.config.set('core.themes', ['non-existent-dark-ui', 'non-existent-dark-syntax']) + atom.config.set('core.themes', [ + 'non-existent-dark-ui', + 'non-existent-dark-syntax' + ]) waitsForPromise(() => atom.themes.activateThemes()) }) @@ -451,7 +580,10 @@ h2 { describe('when the enabled UI and syntax themes are not bundled with Atom', function () { beforeEach(function () { - atom.config.set('core.themes', ['installed-dark-ui', 'installed-dark-syntax']) + atom.config.set('core.themes', [ + 'installed-dark-ui', + 'installed-dark-syntax' + ]) waitsForPromise(() => atom.themes.activateThemes()) }) @@ -466,7 +598,10 @@ h2 { describe('when the enabled UI theme is not bundled with Atom', function () { beforeEach(function () { - atom.config.set('core.themes', ['installed-dark-ui', 'atom-light-syntax']) + atom.config.set('core.themes', [ + 'installed-dark-ui', + 'atom-light-syntax' + ]) waitsForPromise(() => atom.themes.activateThemes()) }) @@ -481,7 +616,10 @@ h2 { describe('when the enabled syntax theme is not bundled with Atom', function () { beforeEach(function () { - atom.config.set('core.themes', ['atom-light-ui', 'installed-dark-syntax']) + atom.config.set('core.themes', [ + 'atom-light-ui', + 'installed-dark-syntax' + ]) waitsForPromise(() => atom.themes.activateThemes()) }) diff --git a/spec/title-bar-spec.js b/spec/title-bar-spec.js index b219a5819..c5d98ed03 100644 --- a/spec/title-bar-spec.js +++ b/spec/title-bar-spec.js @@ -8,20 +8,28 @@ describe('TitleBar', () => { themes: atom.themes, applicationDelegate: atom.applicationDelegate }) - expect(titleBar.element.querySelector('.title').textContent).toBe(document.title) + expect(titleBar.element.querySelector('.title').textContent).toBe( + document.title + ) const paneItem = new FakePaneItem('Title 1') atom.workspace.getActivePane().activateItem(paneItem) expect(document.title).toMatch('Title 1') - expect(titleBar.element.querySelector('.title').textContent).toBe(document.title) + expect(titleBar.element.querySelector('.title').textContent).toBe( + document.title + ) paneItem.setTitle('Title 2') expect(document.title).toMatch('Title 2') - expect(titleBar.element.querySelector('.title').textContent).toBe(document.title) + expect(titleBar.element.querySelector('.title').textContent).toBe( + document.title + ) atom.project.setPaths([temp.mkdirSync('project-1')]) expect(document.title).toMatch('project-1') - expect(titleBar.element.querySelector('.title').textContent).toBe(document.title) + expect(titleBar.element.querySelector('.title').textContent).toBe( + document.title + ) }) it('can update the sheet offset for the current window based on its height', () => { @@ -46,7 +54,9 @@ class FakePaneItem { onDidChangeTitle (callback) { this.didChangeTitleCallback = callback return { - dispose: () => { this.didChangeTitleCallback = null } + dispose: () => { + this.didChangeTitleCallback = null + } } } diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index e6ca01de2..183439a3c 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -1,4 +1,4 @@ -const {CompositeDisposable} = require('atom') +const { CompositeDisposable } = require('atom') const TooltipManager = require('../src/tooltip-manager') const Tooltip = require('../src/tooltip') const _ = require('underscore-plus') @@ -18,25 +18,30 @@ describe('TooltipManager', () => { } beforeEach(function () { - manager = new TooltipManager({keymapManager: atom.keymaps, viewRegistry: atom.views}) + manager = new TooltipManager({ + keymapManager: atom.keymaps, + viewRegistry: atom.views + }) element = createElement('foo') }) describe('::add(target, options)', () => { describe("when the trigger is 'hover' (the default)", () => { it('creates a tooltip when hovering over the target element', () => { - manager.add(element, {title: 'Title'}) - hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + manager.add(element, { title: 'Title' }) + hover(element, () => + expect(document.body.querySelector('.tooltip')).toHaveText('Title') + ) }) it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', () => { const disposables = new CompositeDisposable() const element1 = createElement('foo') - disposables.add(manager.add(element1, {title: 'Title'})) + disposables.add(manager.add(element1, { title: 'Title' })) const element2 = createElement('bar') - disposables.add(manager.add(element2, {title: 'Title'})) + disposables.add(manager.add(element2, { title: 'Title' })) const element3 = createElement('baz') - disposables.add(manager.add(element3, {title: 'Title'})) + disposables.add(manager.add(element3, { title: 'Title' })) hover(element1, () => {}) expect(document.body.querySelector('.tooltip')).toBeNull() @@ -57,12 +62,17 @@ describe('TooltipManager', () => { }) it('hides the tooltip on keydown events', () => { - const disposable = manager.add(element, { title: 'Title', trigger: 'hover' }) + const disposable = manager.add(element, { + title: 'Title', + trigger: 'hover' + }) hover(element, function () { expect(document.body.querySelector('.tooltip')).not.toBeNull() - window.dispatchEvent(new CustomEvent('keydown', { - bubbles: true - })) + window.dispatchEvent( + new CustomEvent('keydown', { + bubbles: true + }) + ) expect(document.body.querySelector('.tooltip')).toBeNull() disposable.dispose() }) @@ -71,16 +81,18 @@ describe('TooltipManager', () => { describe("when the trigger is 'manual'", () => it('creates a tooltip immediately and only hides it on dispose', () => { - const disposable = manager.add(element, {title: 'Title', trigger: 'manual'}) + const disposable = manager.add(element, { + title: 'Title', + trigger: 'manual' + }) expect(document.body.querySelector('.tooltip')).toHaveText('Title') disposable.dispose() expect(document.body.querySelector('.tooltip')).toBeNull() - }) - ) + })) describe("when the trigger is 'click'", () => it('shows and hides the tooltip when the target element is clicked', () => { - manager.add(element, {title: 'Title', trigger: 'click'}) + manager.add(element, { title: 'Title', trigger: 'click' }) expect(document.body.querySelector('.tooltip')).toBeNull() element.click() expect(document.body.querySelector('.tooltip')).not.toBeNull() @@ -102,16 +114,17 @@ describe('TooltipManager', () => { expect(document.body.querySelector('.tooltip')).not.toBeNull() element.click() expect(document.body.querySelector('.tooltip')).toBeNull() - }) - ) + })) it('does not hide the tooltip on keyboard input', () => { - manager.add(element, {title: 'Title', trigger: 'click'}) + manager.add(element, { title: 'Title', trigger: 'click' }) element.click() expect(document.body.querySelector('.tooltip')).not.toBeNull() - window.dispatchEvent(new CustomEvent('keydown', { - bubbles: true - })) + window.dispatchEvent( + new CustomEvent('keydown', { + bubbles: true + }) + ) expect(document.body.querySelector('.tooltip')).not.toBeNull() // click again to hide the tooltip because otherwise state leaks // into other tests. @@ -120,13 +133,21 @@ describe('TooltipManager', () => { it('allows a custom item to be specified for the content of the tooltip', () => { const tooltipElement = document.createElement('div') - manager.add(element, {item: {element: tooltipElement}}) - hover(element, () => expect(tooltipElement.closest('.tooltip')).not.toBeNull()) + manager.add(element, { item: { element: tooltipElement } }) + hover(element, () => + expect(tooltipElement.closest('.tooltip')).not.toBeNull() + ) }) it('allows a custom class to be specified for the tooltip', () => { - manager.add(element, {title: 'Title', class: 'custom-tooltip-class'}) - hover(element, () => expect(document.body.querySelector('.tooltip').classList.contains('custom-tooltip-class')).toBe(true)) + manager.add(element, { title: 'Title', class: 'custom-tooltip-class' }) + hover(element, () => + expect( + document.body + .querySelector('.tooltip') + .classList.contains('custom-tooltip-class') + ).toBe(true) + ) }) it('allows jQuery elements to be passed as the target', () => { @@ -139,63 +160,71 @@ describe('TooltipManager', () => { length: 2, jquery: 'any-version' } - const disposable = manager.add(fakeJqueryWrapper, {title: 'Title'}) + const disposable = manager.add(fakeJqueryWrapper, { title: 'Title' }) - hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + hover(element, () => + expect(document.body.querySelector('.tooltip')).toHaveText('Title') + ) expect(document.body.querySelector('.tooltip')).toBeNull() - hover(element2, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + hover(element2, () => + expect(document.body.querySelector('.tooltip')).toHaveText('Title') + ) expect(document.body.querySelector('.tooltip')).toBeNull() disposable.dispose() - hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull()) - hover(element2, () => expect(document.body.querySelector('.tooltip')).toBeNull()) + hover(element, () => + expect(document.body.querySelector('.tooltip')).toBeNull() + ) + hover(element2, () => + expect(document.body.querySelector('.tooltip')).toBeNull() + ) }) describe('when a keyBindingCommand is specified', () => { describe('when a title is specified', () => it('appends the key binding corresponding to the command to the title', () => { atom.keymaps.add('test', { - '.foo': { 'ctrl-x ctrl-y': 'test-command' - }, - '.bar': { 'ctrl-x ctrl-z': 'test-command' - } - } - ) + '.foo': { 'ctrl-x ctrl-y': 'test-command' }, + '.bar': { 'ctrl-x ctrl-z': 'test-command' } + }) - manager.add(element, {title: 'Title', keyBindingCommand: 'test-command'}) + manager.add(element, { + title: 'Title', + keyBindingCommand: 'test-command' + }) hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') expect(tooltipElement).toHaveText(`Title ${ctrlX} ${ctrlY}`) }) - }) - ) + })) describe('when no title is specified', () => it('shows the key binding corresponding to the command alone', () => { - atom.keymaps.add('test', {'.foo': {'ctrl-x ctrl-y': 'test-command'}}) + atom.keymaps.add('test', { + '.foo': { 'ctrl-x ctrl-y': 'test-command' } + }) - manager.add(element, {keyBindingCommand: 'test-command'}) + manager.add(element, { keyBindingCommand: 'test-command' }) hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`) }) - }) - ) + })) describe('when a keyBindingTarget is specified', () => { it('looks up the key binding relative to the target', () => { atom.keymaps.add('test', { - '.bar': { 'ctrl-x ctrl-z': 'test-command' - }, - '.foo': { 'ctrl-x ctrl-y': 'test-command' - } - } - ) + '.bar': { 'ctrl-x ctrl-z': 'test-command' }, + '.foo': { 'ctrl-x ctrl-y': 'test-command' } + }) - manager.add(element, {keyBindingCommand: 'test-command', keyBindingTarget: element}) + manager.add(element, { + keyBindingCommand: 'test-command', + keyBindingTarget: element + }) hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') @@ -204,7 +233,11 @@ describe('TooltipManager', () => { }) it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', () => { - manager.add(element, {title: 'A Title', keyBindingCommand: 'test-command', keyBindingTarget: element}) + manager.add(element, { + title: 'A Title', + keyBindingCommand: 'test-command', + keyBindingTarget: element + }) hover(element, function () { const tooltipElement = document.body.querySelector('.tooltip') @@ -216,34 +249,36 @@ describe('TooltipManager', () => { describe('when .dispose() is called on the returned disposable', () => it('no longer displays the tooltip on hover', () => { - const disposable = manager.add(element, {title: 'Title'}) + const disposable = manager.add(element, { title: 'Title' }) - hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title')) + hover(element, () => + expect(document.body.querySelector('.tooltip')).toHaveText('Title') + ) disposable.dispose() - hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull()) - }) - ) + hover(element, () => + expect(document.body.querySelector('.tooltip')).toBeNull() + ) + })) describe('when the window is resized', () => it('hides the tooltips', () => { - const disposable = manager.add(element, {title: 'Title'}) + const disposable = manager.add(element, { title: 'Title' }) hover(element, function () { expect(document.body.querySelector('.tooltip')).not.toBeNull() window.dispatchEvent(new CustomEvent('resize')) expect(document.body.querySelector('.tooltip')).toBeNull() disposable.dispose() }) - }) - ) + })) describe('findTooltips', () => { it('adds and remove tooltips correctly', () => { expect(manager.findTooltips(element).length).toBe(0) - const disposable1 = manager.add(element, {title: 'elem1'}) + const disposable1 = manager.add(element, { title: 'elem1' }) expect(manager.findTooltips(element).length).toBe(1) - const disposable2 = manager.add(element, {title: 'elem2'}) + const disposable2 = manager.add(element, { title: 'elem2' }) expect(manager.findTooltips(element).length).toBe(2) disposable1.dispose() expect(manager.findTooltips(element).length).toBe(1) @@ -252,7 +287,7 @@ describe('TooltipManager', () => { }) it('lets us hide tooltips programmatically', () => { - const disposable = manager.add(element, {title: 'Title'}) + const disposable = manager.add(element, { title: 'Title' }) hover(element, function () { expect(document.body.querySelector('.tooltip')).not.toBeNull() manager.findTooltips(element)[0].hide() @@ -272,11 +307,11 @@ function createElement (className) { } function mouseEnter (element) { - element.dispatchEvent(new CustomEvent('mouseenter', {bubbles: false})) - element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true})) + element.dispatchEvent(new CustomEvent('mouseenter', { bubbles: false })) + element.dispatchEvent(new CustomEvent('mouseover', { bubbles: true })) } function mouseLeave (element) { - element.dispatchEvent(new CustomEvent('mouseleave', {bubbles: false})) - element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true})) + element.dispatchEvent(new CustomEvent('mouseleave', { bubbles: false })) + element.dispatchEvent(new CustomEvent('mouseout', { bubbles: true })) } diff --git a/spec/tree-sitter-language-mode-spec.js b/spec/tree-sitter-language-mode-spec.js index 7b8344d9c..7cc210c40 100644 --- a/spec/tree-sitter-language-mode-spec.js +++ b/spec/tree-sitter-language-mode-spec.js @@ -1,22 +1,32 @@ -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') +/* eslint-disable no-template-curly-in-string */ const fs = require('fs') const path = require('path') const dedent = require('dedent') const TextBuffer = require('text-buffer') -const {Point} = TextBuffer +const { Point } = TextBuffer const TextEditor = require('../src/text-editor') const TreeSitterGrammar = require('../src/tree-sitter-grammar') const TreeSitterLanguageMode = require('../src/tree-sitter-language-mode') const Random = require('../script/node_modules/random-seed') -const {getRandomBufferRange, buildRandomLines} = require('./helpers/random') +const { getRandomBufferRange, buildRandomLines } = require('./helpers/random') const cGrammarPath = require.resolve('language-c/grammars/tree-sitter-c.cson') -const pythonGrammarPath = require.resolve('language-python/grammars/tree-sitter-python.cson') -const jsGrammarPath = require.resolve('language-javascript/grammars/tree-sitter-javascript.cson') -const htmlGrammarPath = require.resolve('language-html/grammars/tree-sitter-html.cson') -const ejsGrammarPath = require.resolve('language-html/grammars/tree-sitter-ejs.cson') -const rubyGrammarPath = require.resolve('language-ruby/grammars/tree-sitter-ruby.cson') +const pythonGrammarPath = require.resolve( + 'language-python/grammars/tree-sitter-python.cson' +) +const jsGrammarPath = require.resolve( + 'language-javascript/grammars/tree-sitter-javascript.cson' +) +const htmlGrammarPath = require.resolve( + 'language-html/grammars/tree-sitter-html.cson' +) +const ejsGrammarPath = require.resolve( + 'language-html/grammars/tree-sitter-ejs.cson' +) +const rubyGrammarPath = require.resolve( + 'language-ruby/grammars/tree-sitter-ruby.cson' +) describe('TreeSitterLanguageMode', () => { let editor, buffer @@ -24,7 +34,7 @@ describe('TreeSitterLanguageMode', () => { beforeEach(async () => { editor = await atom.workspace.open('') buffer = editor.getBuffer() - editor.displayLayer.reset({foldCharacter: '…'}) + editor.displayLayer.reset({ foldCharacter: '…' }) }) describe('highlighting', () => { @@ -32,82 +42,85 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'program': 'source', + program: 'source', 'call_expression > identifier': 'function', - 'property_identifier': 'property', + property_identifier: 'property', 'call_expression > member_expression > property_identifier': 'method' } }) buffer.setText('aa.bbb = cc(d.eee());') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) - expectTokensToEqual(editor, [[ - {text: 'aa.', scopes: ['source']}, - {text: 'bbb', scopes: ['source', 'property']}, - {text: ' = ', scopes: ['source']}, - {text: 'cc', scopes: ['source', 'function']}, - {text: '(d.', scopes: ['source']}, - {text: 'eee', scopes: ['source', 'method']}, - {text: '());', scopes: ['source']} - ]]) + expectTokensToEqual(editor, [ + [ + { text: 'aa.', scopes: ['source'] }, + { text: 'bbb', scopes: ['source', 'property'] }, + { text: ' = ', scopes: ['source'] }, + { text: 'cc', scopes: ['source', 'function'] }, + { text: '(d.', scopes: ['source'] }, + { text: 'eee', scopes: ['source', 'method'] }, + { text: '());', scopes: ['source'] } + ] + ]) }) it('can start or end multiple scopes at the same position', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'program': 'source', - 'call_expression': 'call', - 'member_expression': 'member', - 'identifier': 'variable', + program: 'source', + call_expression: 'call', + member_expression: 'member', + identifier: 'variable', '"("': 'open-paren', - '")"': 'close-paren', + '")"': 'close-paren' } }) buffer.setText('a = bb.ccc();') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) - expectTokensToEqual(editor, [[ - {text: 'a', scopes: ['source', 'variable']}, - {text: ' = ', scopes: ['source']}, - {text: 'bb', scopes: ['source', 'call', 'member', 'variable']}, - {text: '.ccc', scopes: ['source', 'call', 'member']}, - {text: '(', scopes: ['source', 'call', 'open-paren']}, - {text: ')', scopes: ['source', 'call', 'close-paren']}, - {text: ';', scopes: ['source']} - ]]) + expectTokensToEqual(editor, [ + [ + { text: 'a', scopes: ['source', 'variable'] }, + { text: ' = ', scopes: ['source'] }, + { text: 'bb', scopes: ['source', 'call', 'member', 'variable'] }, + { text: '.ccc', scopes: ['source', 'call', 'member'] }, + { text: '(', scopes: ['source', 'call', 'open-paren'] }, + { text: ')', scopes: ['source', 'call', 'close-paren'] }, + { text: ';', scopes: ['source'] } + ] + ]) }) it('can resume highlighting on a line that starts with whitespace', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'call_expression > member_expression > property_identifier': 'function', - 'property_identifier': 'member', - 'identifier': 'variable' + 'call_expression > member_expression > property_identifier': + 'function', + property_identifier: 'member', + identifier: 'variable' } }) buffer.setText('a\n .b();') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ + [{ text: 'a', scopes: ['variable'] }], [ - {text: 'a', scopes: ['variable']}, - ], - [ - {text: ' ', scopes: ['leading-whitespace']}, - {text: '.', scopes: []}, - {text: 'b', scopes: ['function']}, - {text: '();', scopes: []} + { text: ' ', scopes: ['leading-whitespace'] }, + { text: '.', scopes: [] }, + { text: 'b', scopes: ['function'] }, + { text: '();', scopes: [] } ] ]) }) @@ -116,77 +129,74 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, cGrammarPath, { parser: 'tree-sitter-c', scopes: { - 'primitive_type': 'type', - 'identifier': 'variable', + primitive_type: 'type', + identifier: 'variable' } }) - buffer.setText('int main() {\n int a\n int b;\n}'); + buffer.setText('int main() {\n int a\n int b;\n}') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expect( - languageMode.tree.rootNode.descendantForPosition(Point(1, 2), Point(1, 6)).toString() + languageMode.tree.rootNode + .descendantForPosition(Point(1, 2), Point(1, 6)) + .toString() ).toBe('(declaration (primitive_type) (identifier) (MISSING))') expectTokensToEqual(editor, [ [ - {text: 'int', scopes: ['type']}, - {text: ' ', scopes: []}, - {text: 'main', scopes: ['variable']}, - {text: '() {', scopes: []} + { text: 'int', scopes: ['type'] }, + { text: ' ', scopes: [] }, + { text: 'main', scopes: ['variable'] }, + { text: '() {', scopes: [] } ], [ - {text: ' ', scopes: ['leading-whitespace']}, - {text: 'int', scopes: ['type']}, - {text: ' ', scopes: []}, - {text: 'a', scopes: ['variable']} + { text: ' ', scopes: ['leading-whitespace'] }, + { text: 'int', scopes: ['type'] }, + { text: ' ', scopes: [] }, + { text: 'a', scopes: ['variable'] } ], [ - {text: ' ', scopes: ['leading-whitespace']}, - {text: 'int', scopes: ['type']}, - {text: ' ', scopes: []}, - {text: 'b', scopes: ['variable']}, - {text: ';', scopes: []} + { text: ' ', scopes: ['leading-whitespace'] }, + { text: 'int', scopes: ['type'] }, + { text: ' ', scopes: [] }, + { text: 'b', scopes: ['variable'] }, + { text: ';', scopes: [] } ], - [ - {text: '}', scopes: []} - ] + [{ text: '}', scopes: [] }] ]) }) - it('updates lines\' highlighting when they are affected by distant changes', async () => { + it("updates lines' highlighting when they are affected by distant changes", async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { 'call_expression > identifier': 'function', - 'property_identifier': 'member' + property_identifier: 'member' } }) buffer.setText('a(\nb,\nc\n') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) // missing closing paren expectTokensToEqual(editor, [ - [{text: 'a(', scopes: []}], - [{text: 'b,', scopes: []}], - [{text: 'c', scopes: []}], - [{text: '', scopes: []}] + [{ text: 'a(', scopes: [] }], + [{ text: 'b,', scopes: [] }], + [{ text: 'c', scopes: [] }], + [{ text: '', scopes: [] }] ]) buffer.append(')') expectTokensToEqual(editor, [ - [ - {text: 'a', scopes: ['function']}, - {text: '(', scopes: []} - ], - [{text: 'b,', scopes: []}], - [{text: 'c', scopes: []}], - [{text: ')', scopes: []}] + [{ text: 'a', scopes: ['function'] }, { text: '(', scopes: [] }], + [{ text: 'b,', scopes: [] }], + [{ text: 'c', scopes: [] }], + [{ text: ')', scopes: [] }] ]) }) @@ -195,7 +205,7 @@ describe('TreeSitterLanguageMode', () => { parser: 'tree-sitter-javascript', scopes: { 'identifier, call_expression > identifier': [ - {match: '^[A-Z]', scopes: 'constructor'} + { match: '^[A-Z]', scopes: 'constructor' } ], 'call_expression > identifier': 'function' @@ -204,17 +214,17 @@ describe('TreeSitterLanguageMode', () => { buffer.setText(`a(B(new C))`) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ [ - {text: 'a', scopes: ['function']}, - {text: '(', scopes: []}, - {text: 'B', scopes: ['constructor']}, - {text: '(new ', scopes: []}, - {text: 'C', scopes: ['constructor']}, - {text: '))', scopes: []}, + { text: 'a', scopes: ['function'] }, + { text: '(', scopes: [] }, + { text: 'B', scopes: ['constructor'] }, + { text: '(new ', scopes: [] }, + { text: 'C', scopes: ['constructor'] }, + { text: '))', scopes: [] } ] ]) }) @@ -223,42 +233,38 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'comment': 'comment', - 'string': 'string', - 'property_identifier': 'property', + comment: 'comment', + string: 'string', + property_identifier: 'property' } }) - buffer.setText([ - '// abc', - '', - 'a("b").c' - ].join('\r\n')) + buffer.setText(['// abc', '', 'a("b").c'].join('\r\n')) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ - [{text: '// abc', scopes: ['comment']}], - [{text: '', scopes: []}], + [{ text: '// abc', scopes: ['comment'] }], + [{ text: '', scopes: [] }], [ - {text: 'a(', scopes: []}, - {text: '"b"', scopes: ['string']}, - {text: ').', scopes: []}, - {text: 'c', scopes: ['property']} + { text: 'a(', scopes: [] }, + { text: '"b"', scopes: ['string'] }, + { text: ').', scopes: [] }, + { text: 'c', scopes: ['property'] } ] ]) buffer.insert([2, 0], ' ') expectTokensToEqual(editor, [ - [{text: '// abc', scopes: ['comment']}], - [{text: '', scopes: []}], + [{ text: '// abc', scopes: ['comment'] }], + [{ text: '', scopes: [] }], [ - {text: ' ', scopes: ['leading-whitespace']}, - {text: 'a(', scopes: []}, - {text: '"b"', scopes: ['string']}, - {text: ').', scopes: []}, - {text: 'c', scopes: ['property']} + { text: ' ', scopes: ['leading-whitespace'] }, + { text: 'a(', scopes: [] }, + { text: '"b"', scopes: ['string'] }, + { text: ').', scopes: [] }, + { text: 'c', scopes: ['property'] } ] ]) }) @@ -267,35 +273,32 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'template_string': 'string', + template_string: 'string', '"${"': 'interpolation', '"}"': 'interpolation' } - }); + }) buffer.setText('`\na${1}\nb${2}\n`;') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ + [{ text: '`', scopes: ['string'] }], [ - {text: '`', scopes: ['string']} - ], [ - {text: 'a', scopes: ['string']}, - {text: '${', scopes: ['string', 'interpolation']}, - {text: '1', scopes: ['string']}, - {text: '}', scopes: ['string', 'interpolation']} - ], [ - {text: 'b', scopes: ['string']}, - {text: '${', scopes: ['string', 'interpolation']}, - {text: '2', scopes: ['string']}, - {text: '}', scopes: ['string', 'interpolation']} + { text: 'a', scopes: ['string'] }, + { text: '${', scopes: ['string', 'interpolation'] }, + { text: '1', scopes: ['string'] }, + { text: '}', scopes: ['string', 'interpolation'] } ], [ - {text: '`', scopes: ['string']}, - {text: ';', scopes: []} - ] + { text: 'b', scopes: ['string'] }, + { text: '${', scopes: ['string', 'interpolation'] }, + { text: '2', scopes: ['string'] }, + { text: '}', scopes: ['string', 'interpolation'] } + ], + [{ text: '`', scopes: ['string'] }, { text: ';', scopes: [] }] ]) }) @@ -303,12 +306,12 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'comment': 'comment', - 'call_expression > identifier': 'function', + comment: 'comment', + 'call_expression > identifier': 'function' } }) - buffer.setText(dedent ` + buffer.setText(dedent` /* * Hello */ @@ -316,24 +319,19 @@ describe('TreeSitterLanguageMode', () => { hello(); `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) editor.foldBufferRange([[0, 2], [2, 0]]) expectTokensToEqual(editor, [ [ - {text: '/*', scopes: ['comment']}, - {text: '…', scopes: ['fold-marker']}, - {text: ' */', scopes: ['comment']} + { text: '/*', scopes: ['comment'] }, + { text: '…', scopes: ['fold-marker'] }, + { text: ' */', scopes: ['comment'] } ], - [ - {text: '', scopes: []} - ], - [ - {text: 'hello', scopes: ['function']}, - {text: '();', scopes: []}, - ] + [{ text: '', scopes: [] }], + [{ text: 'hello', scopes: ['function'] }, { text: '();', scopes: [] }] ]) }) @@ -341,30 +339,30 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'identifier': [ - {match: '^(exports|document|window|global)$', scopes: 'global'}, - {match: '^[A-Z_]+$', scopes: 'constant'}, - {match: '^[A-Z]', scopes: 'constructor'}, + identifier: [ + { match: '^(exports|document|window|global)$', scopes: 'global' }, + { match: '^[A-Z_]+$', scopes: 'constant' }, + { match: '^[A-Z]', scopes: 'constructor' }, 'variable' - ], + ] } }) buffer.setText(`exports.object = Class(SOME_CONSTANT, x)`) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ [ - {text: 'exports', scopes: ['global']}, - {text: '.object = ', scopes: []}, - {text: 'Class', scopes: ['constructor']}, - {text: '(', scopes: []}, - {text: 'SOME_CONSTANT', scopes: ['constant']}, - {text: ', ', scopes: []}, - {text: 'x', scopes: ['variable']}, - {text: ')', scopes: []}, + { text: 'exports', scopes: ['global'] }, + { text: '.object = ', scopes: [] }, + { text: 'Class', scopes: ['constructor'] }, + { text: '(', scopes: [] }, + { text: 'SOME_CONSTANT', scopes: ['constant'] }, + { text: ', ', scopes: [] }, + { text: 'x', scopes: ['variable'] }, + { text: ')', scopes: [] } ] ]) }) @@ -373,10 +371,10 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, rubyGrammarPath, { parser: 'tree-sitter-ruby', scopes: { - 'bare_string': 'string', - 'interpolation': 'embedded', + bare_string: 'string', + interpolation: 'embedded', '"#{"': 'punctuation', - '"}"': 'punctuation', + '"}"': 'punctuation' } }) @@ -384,18 +382,18 @@ describe('TreeSitterLanguageMode', () => { // starts later and ends earlier than the bare string. buffer.setText('a = %W( bc#{d}ef )') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ [ - {text: 'a = %W( ', scopes: []}, - {text: 'bc', scopes: ['string']}, - {text: '#{', scopes: ['string', 'embedded', 'punctuation']}, - {text: 'd', scopes: ['string', 'embedded']}, - {text: '}', scopes: ['string', 'embedded', 'punctuation']}, - {text: 'ef', scopes: ['string']}, - {text: ' )', scopes: []}, + { text: 'a = %W( ', scopes: [] }, + { text: 'bc', scopes: ['string'] }, + { text: '#{', scopes: ['string', 'embedded', 'punctuation'] }, + { text: 'd', scopes: ['string', 'embedded'] }, + { text: '}', scopes: ['string', 'embedded', 'punctuation'] }, + { text: 'ef', scopes: ['string'] }, + { text: ' )', scopes: [] } ] ]) }) @@ -405,59 +403,57 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'identifier': 'variable', + identifier: 'variable', 'call_expression > identifier': 'function', 'new_expression > identifier': 'constructor' } }) - buffer.setText('abc;'); + buffer.setText('abc;') - const languageMode = new TreeSitterLanguageMode({buffer, grammar, syncOperationLimit: 0}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar, + syncOperationLimit: 0 + }) buffer.setLanguageMode(languageMode) await nextHighlightingUpdate(languageMode) await new Promise(process.nextTick) expectTokensToEqual(editor, [ - [ - {text: 'abc', scopes: ['variable']}, - {text: ';', scopes: []} - ], + [{ text: 'abc', scopes: ['variable'] }, { text: ';', scopes: [] }] ]) - buffer.setTextInRange([[0, 3], [0, 3]], '()'); + buffer.setTextInRange([[0, 3], [0, 3]], '()') expectTokensToEqual(editor, [ - [ - {text: 'abc()', scopes: ['variable']}, - {text: ';', scopes: []} - ], + [{ text: 'abc()', scopes: ['variable'] }, { text: ';', scopes: [] }] ]) - buffer.setTextInRange([[0, 0], [0, 0]], 'new '); + buffer.setTextInRange([[0, 0], [0, 0]], 'new ') expectTokensToEqual(editor, [ [ - {text: 'new ', scopes: []}, - {text: 'abc()', scopes: ['variable']}, - {text: ';', scopes: []} - ], + { text: 'new ', scopes: [] }, + { text: 'abc()', scopes: ['variable'] }, + { text: ';', scopes: [] } + ] ]) await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ - {text: 'new ', scopes: []}, - {text: 'abc', scopes: ['function']}, - {text: '();', scopes: []} - ], + { text: 'new ', scopes: [] }, + { text: 'abc', scopes: ['function'] }, + { text: '();', scopes: [] } + ] ]) await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ - {text: 'new ', scopes: []}, - {text: 'abc', scopes: ['constructor']}, - {text: '();', scopes: []} - ], + { text: 'new ', scopes: [] }, + { text: 'abc', scopes: ['constructor'] }, + { text: '();', scopes: [] } + ] ]) }) }) @@ -467,42 +463,39 @@ describe('TreeSitterLanguageMode', () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { - 'property_identifier': 'property', + property_identifier: 'property', 'call_expression > identifier': 'function', - 'call_expression > member_expression > property_identifier': 'method', + 'call_expression > member_expression > property_identifier': + 'method' } }) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) - buffer.setText('a'); - expectTokensToEqual(editor, [[ - {text: 'a', scopes: []}, - ]]) + buffer.setText('a') + expectTokensToEqual(editor, [[{ text: 'a', scopes: [] }]]) buffer.append('.') - expectTokensToEqual(editor, [[ - {text: 'a.', scopes: []}, - ]]) + expectTokensToEqual(editor, [[{ text: 'a.', scopes: [] }]]) buffer.append('b') - expectTokensToEqual(editor, [[ - {text: 'a.', scopes: []}, - {text: 'b', scopes: ['property']}, - ]]) + expectTokensToEqual(editor, [ + [{ text: 'a.', scopes: [] }, { text: 'b', scopes: ['property'] }] + ]) buffer.append('()') - expectTokensToEqual(editor, [[ - {text: 'a.', scopes: []}, - {text: 'b', scopes: ['method']}, - {text: '()', scopes: []}, - ]]) + expectTokensToEqual(editor, [ + [ + { text: 'a.', scopes: [] }, + { text: 'b', scopes: ['method'] }, + { text: '()', scopes: [] } + ] + ]) buffer.delete([[0, 1], [0, 2]]) - expectTokensToEqual(editor, [[ - {text: 'ab', scopes: ['function']}, - {text: '()', scopes: []}, - ]]) + expectTokensToEqual(editor, [ + [{ text: 'ab', scopes: ['function'] }, { text: '()', scopes: [] }] + ]) }) }) @@ -514,9 +507,9 @@ describe('TreeSitterLanguageMode', () => { scopeName: 'javascript', parser: 'tree-sitter-javascript', scopes: { - 'property_identifier': 'property', + property_identifier: 'property', 'call_expression > identifier': 'function', - 'template_string': 'string', + template_string: 'string', 'template_substitution > "${"': 'interpolation', 'template_substitution > "}"': 'interpolation' }, @@ -542,32 +535,35 @@ describe('TreeSitterLanguageMode', () => { atom.grammars.addGrammar(htmlGrammar) buffer.setText('node.innerHTML = html `\na ${b}\n`;') - const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: jsGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ [ - {text: 'node.', scopes: []}, - {text: 'innerHTML', scopes: ['property']}, - {text: ' = ', scopes: []}, - {text: 'html', scopes: ['function']}, - {text: ' ', scopes: []}, - {text: '`', scopes: ['string']}, - {text: '', scopes: ['string', 'html']} - ], [ - {text: 'a ', scopes: ['string', 'html']}, - {text: '${', scopes: ['string', 'html', 'interpolation']}, - {text: 'b', scopes: ['string', 'html']}, - {text: '}', scopes: ['string', 'html', 'interpolation']}, - {text: '<', scopes: ['string', 'html']}, - {text: 'img', scopes: ['string', 'html', 'tag']}, - {text: ' ', scopes: ['string', 'html']}, - {text: 'src', scopes: ['string', 'html', 'attr']}, - {text: '="d">', scopes: ['string', 'html']} - ], [ - {text: '`', scopes: ['string']}, - {text: ';', scopes: []}, + { text: 'node.', scopes: [] }, + { text: 'innerHTML', scopes: ['property'] }, + { text: ' = ', scopes: [] }, + { text: 'html', scopes: ['function'] }, + { text: ' ', scopes: [] }, + { text: '`', scopes: ['string'] }, + { text: '', scopes: ['string', 'html'] } ], + [ + { text: 'a ', scopes: ['string', 'html'] }, + { text: '${', scopes: ['string', 'html', 'interpolation'] }, + { text: 'b', scopes: ['string', 'html'] }, + { text: '}', scopes: ['string', 'html', 'interpolation'] }, + { text: '<', scopes: ['string', 'html'] }, + { text: 'img', scopes: ['string', 'html', 'tag'] }, + { text: ' ', scopes: ['string', 'html'] }, + { text: 'src', scopes: ['string', 'html', 'attr'] }, + { text: '="d">', scopes: ['string', 'html'] } + ], + [{ text: '`', scopes: ['string'] }, { text: ';', scopes: [] }] ]) const range = buffer.findSync('html') @@ -576,22 +572,21 @@ describe('TreeSitterLanguageMode', () => { expectTokensToEqual(editor, [ [ - {text: 'node.', scopes: []}, - {text: 'innerHTML', scopes: ['property']}, - {text: ' = ', scopes: []}, - {text: 'xml', scopes: ['function']}, - {text: ' ', scopes: []}, - {text: '`', scopes: ['string']} - ], [ - {text: 'a ', scopes: ['string']}, - {text: '${', scopes: ['string', 'interpolation']}, - {text: 'b', scopes: ['string']}, - {text: '}', scopes: ['string', 'interpolation']}, - {text: '', scopes: ['string']}, - ], [ - {text: '`', scopes: ['string']}, - {text: ';', scopes: []}, + { text: 'node.', scopes: [] }, + { text: 'innerHTML', scopes: ['property'] }, + { text: ' = ', scopes: [] }, + { text: 'xml', scopes: ['function'] }, + { text: ' ', scopes: [] }, + { text: '`', scopes: ['string'] } ], + [ + { text: 'a ', scopes: ['string'] }, + { text: '${', scopes: ['string', 'interpolation'] }, + { text: 'b', scopes: ['string'] }, + { text: '}', scopes: ['string', 'interpolation'] }, + { text: '', scopes: ['string'] } + ], + [{ text: '`', scopes: ['string'] }, { text: ';', scopes: [] }] ]) }) @@ -600,33 +595,37 @@ describe('TreeSitterLanguageMode', () => { atom.grammars.addGrammar(htmlGrammar) buffer.setText('\n
    \n
    ') - const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ [ - {text: '<', scopes: ['html']}, - {text: 'script', scopes: ['html', 'tag']}, - {text: '>', scopes: ['html']}, + { text: '<', scopes: ['html'] }, + { text: 'script', scopes: ['html', 'tag'] }, + { text: '>', scopes: ['html'] } ], [ - {text: 'hello', scopes: ['html', 'function']}, - {text: '();', scopes: ['html']}, + { text: 'hello', scopes: ['html', 'function'] }, + { text: '();', scopes: ['html'] } ], [ - {text: '', scopes: ['html']}, + { text: '', scopes: ['html'] } ], [ - {text: '<', scopes: ['html']}, - {text: 'div', scopes: ['html', 'tag']}, - {text: '>', scopes: ['html']}, + { text: '<', scopes: ['html'] }, + { text: 'div', scopes: ['html', 'tag'] }, + { text: '>', scopes: ['html'] } ], [ - {text: '', scopes: ['html']}, + { text: '', scopes: ['html'] } ] ]) }) @@ -635,78 +634,92 @@ describe('TreeSitterLanguageMode', () => { atom.grammars.addGrammar(jsGrammar) buffer.setText('node.innerHTML = html `\na ${b}\n`;') - const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: jsGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ [ - {text: 'node.', scopes: []}, - {text: 'innerHTML', scopes: ['property']}, - {text: ' = ', scopes: []}, - {text: 'html', scopes: ['function']}, - {text: ' ', scopes: []}, - {text: '`', scopes: ['string']} - ], [ - {text: 'a ', scopes: ['string']}, - {text: '${', scopes: ['string', 'interpolation']}, - {text: 'b', scopes: ['string']}, - {text: '}', scopes: ['string', 'interpolation']}, - {text: '', scopes: ['string']}, - ], [ - {text: '`', scopes: ['string']}, - {text: ';', scopes: []}, + { text: 'node.', scopes: [] }, + { text: 'innerHTML', scopes: ['property'] }, + { text: ' = ', scopes: [] }, + { text: 'html', scopes: ['function'] }, + { text: ' ', scopes: [] }, + { text: '`', scopes: ['string'] } ], + [ + { text: 'a ', scopes: ['string'] }, + { text: '${', scopes: ['string', 'interpolation'] }, + { text: 'b', scopes: ['string'] }, + { text: '}', scopes: ['string', 'interpolation'] }, + { text: '', scopes: ['string'] } + ], + [{ text: '`', scopes: ['string'] }, { text: ';', scopes: [] }] ]) atom.grammars.addGrammar(htmlGrammar) await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ - {text: 'node.', scopes: []}, - {text: 'innerHTML', scopes: ['property']}, - {text: ' = ', scopes: []}, - {text: 'html', scopes: ['function']}, - {text: ' ', scopes: []}, - {text: '`', scopes: ['string']}, - {text: '', scopes: ['string', 'html']} - ], [ - {text: 'a ', scopes: ['string', 'html']}, - {text: '${', scopes: ['string', 'html', 'interpolation']}, - {text: 'b', scopes: ['string', 'html']}, - {text: '}', scopes: ['string', 'html', 'interpolation']}, - {text: '<', scopes: ['string', 'html']}, - {text: 'img', scopes: ['string', 'html', 'tag']}, - {text: ' ', scopes: ['string', 'html']}, - {text: 'src', scopes: ['string', 'html', 'attr']}, - {text: '="d">', scopes: ['string', 'html']} - ], [ - {text: '`', scopes: ['string']}, - {text: ';', scopes: []}, + { text: 'node.', scopes: [] }, + { text: 'innerHTML', scopes: ['property'] }, + { text: ' = ', scopes: [] }, + { text: 'html', scopes: ['function'] }, + { text: ' ', scopes: [] }, + { text: '`', scopes: ['string'] }, + { text: '', scopes: ['string', 'html'] } ], + [ + { text: 'a ', scopes: ['string', 'html'] }, + { text: '${', scopes: ['string', 'html', 'interpolation'] }, + { text: 'b', scopes: ['string', 'html'] }, + { text: '}', scopes: ['string', 'html', 'interpolation'] }, + { text: '<', scopes: ['string', 'html'] }, + { text: 'img', scopes: ['string', 'html', 'tag'] }, + { text: ' ', scopes: ['string', 'html'] }, + { text: 'src', scopes: ['string', 'html', 'attr'] }, + { text: '="d">', scopes: ['string', 'html'] } + ], + [{ text: '`', scopes: ['string'] }, { text: ';', scopes: [] }] ]) }) it('handles injections that intersect', async () => { - const ejsGrammar = new TreeSitterGrammar(atom.grammars, ejsGrammarPath, { - id: 'ejs', - parser: 'tree-sitter-embedded-template', - scopes: { - '"<%="': 'directive', - '"%>"': 'directive', - }, - injectionPoints: [ - { - type: 'template', - language (node) { return 'javascript' }, - content (node) { return node.descendantsOfType('code') } + const ejsGrammar = new TreeSitterGrammar( + atom.grammars, + ejsGrammarPath, + { + id: 'ejs', + parser: 'tree-sitter-embedded-template', + scopes: { + '"<%="': 'directive', + '"%>"': 'directive' }, - { - type: 'template', - language (node) { return 'html' }, - content (node) { return node.descendantsOfType('content') } - } - ] - }) + injectionPoints: [ + { + type: 'template', + language (node) { + return 'javascript' + }, + content (node) { + return node.descendantsOfType('code') + } + }, + { + type: 'template', + language (node) { + return 'html' + }, + content (node) { + return node.descendantsOfType('content') + } + } + ] + } + ) atom.grammars.addGrammar(jsGrammar) atom.grammars.addGrammar(htmlGrammar) @@ -715,41 +728,41 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({ buffer, grammar: ejsGrammar, - grammars: atom.grammars, + grammars: atom.grammars }) buffer.setLanguageMode(languageMode) expectTokensToEqual(editor, [ [ - {text: '<', scopes: ['html']}, - {text: 'body', scopes: ['html', 'tag']}, - {text: '>', scopes: ['html']} + { text: '<', scopes: ['html'] }, + { text: 'body', scopes: ['html', 'tag'] }, + { text: '>', scopes: ['html'] } ], [ - {text: '<', scopes: ['html']}, - {text: 'script', scopes: ['html', 'tag']}, - {text: '>', scopes: ['html']} + { text: '<', scopes: ['html'] }, + { text: 'script', scopes: ['html', 'tag'] }, + { text: '>', scopes: ['html'] } ], [ - {text: 'b', scopes: ['html', 'function']}, - {text: '(', scopes: ['html']}, - {text: '<%=', scopes: ['html', 'directive']}, - {text: ' c.', scopes: ['html']}, - {text: 'd', scopes: ['html', 'property']}, - {text: ' ', scopes: ['html']}, - {text: '%>', scopes: ['html', 'directive']}, - {text: ')', scopes: ['html']}, + { text: 'b', scopes: ['html', 'function'] }, + { text: '(', scopes: ['html'] }, + { text: '<%=', scopes: ['html', 'directive'] }, + { text: ' c.', scopes: ['html'] }, + { text: 'd', scopes: ['html', 'property'] }, + { text: ' ', scopes: ['html'] }, + { text: '%>', scopes: ['html', 'directive'] }, + { text: ')', scopes: ['html'] } ], [ - {text: '', scopes: ['html']} + { text: '', scopes: ['html'] } ], [ - {text: '', scopes: ['html']} - ], + { text: '', scopes: ['html'] } + ] ]) }) @@ -758,18 +771,18 @@ describe('TreeSitterLanguageMode', () => { editor.onDidTokenize(event => { expectTokensToEqual(editor, [ [ - {text: '<', scopes: ['html']}, - {text: 'script', scopes: ['html', 'tag']}, - {text: '>', scopes: ['html']}, + { text: '<', scopes: ['html'] }, + { text: 'script', scopes: ['html', 'tag'] }, + { text: '>', scopes: ['html'] } ], [ - {text: 'hello', scopes: ['html', 'function']}, - {text: '();', scopes: ['html']}, + { text: 'hello', scopes: ['html', 'function'] }, + { text: '();', scopes: ['html'] } ], [ - {text: '', scopes: ['html']}, + { text: '', scopes: ['html'] } ] ]) resolve() @@ -808,13 +821,16 @@ describe('TreeSitterLanguageMode', () => { it('matches the highlighting of a freshly-opened editor', async () => { jasmine.useRealClock() - const text = fs.readFileSync(path.join(__dirname, 'fixtures', 'sample.js'), 'utf8') + const text = fs.readFileSync( + path.join(__dirname, 'fixtures', 'sample.js'), + 'utf8' + ) atom.grammars.loadGrammarSync(jsGrammarPath) atom.grammars.assignLanguageMode(buffer, 'source.js') buffer.getLanguageMode().syncOperationLimit = 0 const initialSeed = Date.now() - for (let i = 0, trial_count = 10; i < trial_count; i++) { + for (let i = 0, trialCount = 10; i < trialCount; i++) { let seed = initialSeed + i // seed = 1541201470759 const random = Random(seed) @@ -826,12 +842,15 @@ describe('TreeSitterLanguageMode', () => { editor.displayLayer.getScreenLines() // Make several random edits. - for (let j = 0, edit_count = 1 + random(4); j < edit_count; j++) { + for (let j = 0, editCount = 1 + random(4); j < editCount; j++) { const editRoll = random(10) const range = getRandomBufferRange(random, buffer) if (editRoll < 2) { - const linesToInsert = buildRandomLines(random, range.getExtent().row + 1) + const linesToInsert = buildRandomLines( + random, + range.getExtent().row + 1 + ) // console.log('replace', range.toString(), JSON.stringify(linesToInsert)) buffer.setTextInRange(range, linesToInsert) } else if (editRoll < 5) { @@ -857,14 +876,15 @@ describe('TreeSitterLanguageMode', () => { // Create a fresh buffer and editor with the same text. const buffer2 = new TextBuffer(buffer.getText()) - const editor2 = new TextEditor({buffer: buffer2}) + const editor2 = new TextEditor({ buffer: buffer2 }) atom.grammars.assignLanguageMode(buffer2, 'source.js') // Verify that the the two buffers have the same syntax highlighting. await buffer.getLanguageMode().parseCompletePromise() await buffer2.getLanguageMode().parseCompletePromise() expect(buffer.getLanguageMode().tree.rootNode.toString()).toEqual( - buffer2.getLanguageMode().tree.rootNode.toString(), `Seed: ${seed}` + buffer2.getLanguageMode().tree.rootNode.toString(), + `Seed: ${seed}` ) for (let j = 0, n = editor.getScreenLineCount(); j < n; j++) { @@ -874,7 +894,7 @@ describe('TreeSitterLanguageMode', () => { if (jasmine.getEnv().currentSpec.results().failedCount > 0) { console.log(tokens1) console.log(tokens2) - debugger + debugger // eslint-disable-line no-debugger break } } @@ -890,17 +910,17 @@ describe('TreeSitterLanguageMode', () => { parser: 'tree-sitter-javascript', folds: [ { - start: {type: '{', index: 0}, - end: {type: '}', index: -1} + start: { type: '{', index: 0 }, + end: { type: '}', index: -1 } }, { - start: {type: '(', index: 0}, - end: {type: ')', index: -1} + start: { type: '(', index: 0 }, + end: { type: ')', index: -1 } } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` module.exports = class A { getB (c, @@ -911,7 +931,7 @@ describe('TreeSitterLanguageMode', () => { } `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(false) @@ -922,7 +942,7 @@ describe('TreeSitterLanguageMode', () => { expect(editor.isFoldableAtBufferRow(5)).toBe(false) editor.foldBufferRow(2) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` module.exports = class A { getB (c,…) { @@ -932,7 +952,7 @@ describe('TreeSitterLanguageMode', () => { `) editor.foldBufferRow(4) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` module.exports = class A { getB (c,…) {…} @@ -945,17 +965,17 @@ describe('TreeSitterLanguageMode', () => { parser: 'tree-sitter-javascript', folds: [ { - start: {type: '{', index: 0}, - end: {type: '}', index: -1} + start: { type: '{', index: 0 }, + end: { type: '}', index: -1 } }, { - start: {type: '(', index: 0}, - end: {type: ')', index: -1} + start: { type: '(', index: 0 }, + end: { type: ')', index: -1 } } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` if (a) { b } else if (c) { @@ -965,13 +985,13 @@ describe('TreeSitterLanguageMode', () => { } `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) // Avoid bringing the `else if...` up onto the same screen line as the preceding `if`. editor.foldBufferRow(1) editor.foldBufferRow(3) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` if (a) {… } else if (c) {… } else { @@ -981,7 +1001,7 @@ describe('TreeSitterLanguageMode', () => { // It's ok to bring the final `}` onto the same screen line as the preceding `else`. editor.foldBufferRow(5) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` if (a) {… } else if (c) {… } else {…} @@ -996,20 +1016,20 @@ describe('TreeSitterLanguageMode', () => { // (the closing tag). { type: 'jsx_element', - start: {index: 0}, - end: {index: -1} + start: { index: 0 }, + end: { index: -1 } }, // End the fold at the *second* to last child of the self-closing tag: the `/`. { type: 'jsx_self_closing_element', - start: {index: 1}, - end: {index: -2} + start: { index: 1 }, + end: { index: -2 } } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` const element1 = @@ -1020,7 +1040,7 @@ describe('TreeSitterLanguageMode', () => { `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(true) @@ -1031,7 +1051,7 @@ describe('TreeSitterLanguageMode', () => { expect(editor.isFoldableAtBufferRow(5)).toBe(false) editor.foldBufferRow(0) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` const element1 = const element2 = @@ -1041,7 +1061,7 @@ describe('TreeSitterLanguageMode', () => { `) editor.foldBufferRow(4) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` const element1 = const element2 = … @@ -1055,11 +1075,11 @@ describe('TreeSitterLanguageMode', () => { folds: [ // By default, for a node with no children, folds are started at the *end* of the first // line of a node, and ended at the *beginning* of the last line. - {type: 'comment'} + { type: 'comment' } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` /** * Important */ @@ -1068,7 +1088,7 @@ describe('TreeSitterLanguageMode', () => { */ `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(true) @@ -1078,7 +1098,7 @@ describe('TreeSitterLanguageMode', () => { expect(editor.isFoldableAtBufferRow(4)).toBe(false) editor.foldBufferRow(0) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` /**… */ const x = 1 /* Also important @@ -1086,7 +1106,7 @@ describe('TreeSitterLanguageMode', () => { `) editor.foldBufferRow(3) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` /**… */ const x = 1 /*…*/ `) @@ -1099,26 +1119,26 @@ describe('TreeSitterLanguageMode', () => { // If the #ifdef has an `#else` clause, then end the fold there. { type: ['preproc_ifdef', 'preproc_elif'], - start: {index: 1}, - end: {type: ['preproc_else', 'preproc_elif']} + start: { index: 1 }, + end: { type: ['preproc_else', 'preproc_elif'] } }, // Otherwise, end the fold at the last child - the `#endif`. { type: 'preproc_ifdef', - start: {index: 1}, - end: {index: -1} + start: { index: 1 }, + end: { index: -1 } }, // When folding an `#else` clause, the fold extends to the end of the clause. { type: 'preproc_else', - start: {index: 0} + start: { index: 0 } } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` #ifndef FOO_H_ #define FOO_H_ @@ -1142,11 +1162,11 @@ describe('TreeSitterLanguageMode', () => { #endif `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) editor.foldBufferRow(3) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` #ifndef FOO_H_ #define FOO_H_ @@ -1167,7 +1187,7 @@ describe('TreeSitterLanguageMode', () => { `) editor.foldBufferRow(8) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` #ifndef FOO_H_ #define FOO_H_ @@ -1184,13 +1204,13 @@ describe('TreeSitterLanguageMode', () => { `) editor.foldBufferRow(0) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` #ifndef FOO_H_… #endif `) editor.foldAllAtIndentLevel(1) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` #ifndef FOO_H_ #define FOO_H_ @@ -1210,20 +1230,20 @@ describe('TreeSitterLanguageMode', () => { folds: [ { type: 'element', - start: {index: 0}, - end: {index: -1} + start: { index: 0 }, + end: { index: -1 } } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) // Void elements have only one child @@ -1231,7 +1251,7 @@ describe('TreeSitterLanguageMode', () => { expect(editor.isFoldableAtBufferRow(2)).toBe(false) editor.foldBufferRow(0) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` … `) @@ -1245,13 +1265,13 @@ describe('TreeSitterLanguageMode', () => { // just to demonstrate the targeting of named vs anonymous nodes. { type: 'elsif', - start: {index: 1}, + start: { index: 1 }, // There are no double quotes around the `elsif` type. This indicates // that we're targeting a *named* node in the syntax tree. The fold // should end at the nested `elsif` node, not at the token that represents // the literal string "elsif". - end: {type: ['else', 'elsif']} + end: { type: ['else', 'elsif'] } }, { type: 'else', @@ -1260,12 +1280,12 @@ describe('TreeSitterLanguageMode', () => { // we're targetting an *anonymous* node in the syntax tree. The fold // should start at the token representing the literal string "else", // not at an `else` node. - start: {type: '"else"'} + start: { type: '"else"' } } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` if a b elsif c @@ -1275,20 +1295,20 @@ describe('TreeSitterLanguageMode', () => { end `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expect(languageMode.tree.rootNode.toString()).toBe( - "(program (if (identifier) (then " + - "(identifier)) " + - "(elsif (identifier) (then " + - "(identifier)) " + - "(else " + - "(identifier)))))" + '(program (if (identifier) (then ' + + '(identifier)) ' + + '(elsif (identifier) (then ' + + '(identifier)) ' + + '(else ' + + '(identifier)))))' ) editor.foldBufferRow(2) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` if a b elsif c… @@ -1298,7 +1318,7 @@ describe('TreeSitterLanguageMode', () => { `) editor.foldBufferRow(4) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` if a b elsif c… @@ -1312,13 +1332,13 @@ describe('TreeSitterLanguageMode', () => { parser: 'tree-sitter-javascript', folds: [ { - start: {type: '{', index: 0}, - end: {type: '}', index: -1} + start: { type: '{', index: 0 }, + end: { type: '}', index: -1 } } ] }) - buffer.setText(dedent ` + buffer.setText(dedent` class A { // a constructor (b) { @@ -1327,7 +1347,7 @@ describe('TreeSitterLanguageMode', () => { } `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expect(languageMode.isFoldableAtRow(0)).toBe(true) expect(languageMode.isFoldableAtRow(1)).toBe(false) @@ -1345,17 +1365,21 @@ describe('TreeSitterLanguageMode', () => { describe('when folding a node that ends with a line break', () => { it('ends the fold at the end of the previous line', async () => { - const grammar = new TreeSitterGrammar(atom.grammars, pythonGrammarPath, { - parser: 'tree-sitter-python', - folds: [ - { - type: 'function_definition', - start: {type: ':'} - } - ] - }) + const grammar = new TreeSitterGrammar( + atom.grammars, + pythonGrammarPath, + { + parser: 'tree-sitter-python', + folds: [ + { + type: 'function_definition', + start: { type: ':' } + } + ] + } + ) - buffer.setText(dedent ` + buffer.setText(dedent` def ab(): print 'a' print 'b' @@ -1365,10 +1389,10 @@ describe('TreeSitterLanguageMode', () => { print 'd' `) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) editor.foldBufferRow(0) - expect(getDisplayText(editor)).toBe(dedent ` + expect(getDisplayText(editor)).toBe(dedent` def ab():… def cd(): @@ -1379,31 +1403,39 @@ describe('TreeSitterLanguageMode', () => { }) it('folds code in injected languages', async () => { - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'html', - parser: 'tree-sitter-html', - scopes: {}, - folds: [{ - type: ['element', 'raw_element'], - start: {index: 0}, - end: {index: -1} - }], - injectionRegExp: 'html' - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + folds: [ + { + type: ['element', 'raw_element'], + start: { index: 0 }, + end: { index: -1 } + } + ], + injectionRegExp: 'html' + } + ) const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { scopeName: 'javascript', parser: 'tree-sitter-javascript', scopes: {}, - folds: [{ - type: ['template_string'], - start: {index: 0}, - end: {index: -1}, - }, - { - start: {index: 0, type: '('}, - end: {index: -1, type: ')'} - }], + folds: [ + { + type: ['template_string'], + start: { index: 0 }, + end: { index: -1 } + }, + { + start: { index: 0, type: '(' }, + end: { index: -1, type: ')' } + } + ], injectionRegExp: 'javascript', injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) @@ -1422,7 +1454,11 @@ describe('TreeSitterLanguageMode', () => { \` ` ) - const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: jsGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) editor.foldBufferRow(2) @@ -1467,32 +1503,32 @@ describe('TreeSitterLanguageMode', () => { buffer.setText('foo({bar: baz});') - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.scopeDescriptorForBufferPosition([0, 'foo({b'.length]).getScopesArray()).toEqual([ - 'source.js', - 'property.name' - ]) - expect(editor.scopeDescriptorForBufferPosition([0, 'foo({'.length]).getScopesArray()).toEqual([ - 'source.js', - 'property.name' - ]) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect( + editor + .scopeDescriptorForBufferPosition([0, 'foo({b'.length]) + .getScopesArray() + ).toEqual(['source.js', 'property.name']) + expect( + editor + .scopeDescriptorForBufferPosition([0, 'foo({'.length]) + .getScopesArray() + ).toEqual(['source.js', 'property.name']) // Drive-by test for .tokenForPosition() const token = editor.tokenForBufferPosition([0, 'foo({b'.length]) expect(token.value).toBe('bar') - expect(token.scopes).toEqual([ - 'source.js', - 'property.name' - ]) + expect(token.scopes).toEqual(['source.js', 'property.name']) buffer.setText('// baz\n') // Adjust position when at end of line - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.scopeDescriptorForBufferPosition([0, '// baz'.length]).getScopesArray()).toEqual([ - 'source.js', - 'comment.block' - ]) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect( + editor + .scopeDescriptorForBufferPosition([0, '// baz'.length]) + .getScopesArray() + ).toEqual(['source.js', 'comment.block']) }) it('includes nodes in injected syntax trees', async () => { @@ -1509,16 +1545,20 @@ describe('TreeSitterLanguageMode', () => { injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'text.html', - parser: 'tree-sitter-html', - scopes: { - fragment: 'text.html', - raw_element: 'script.tag' - }, - injectionRegExp: 'html', - injectionPoints: [SCRIPT_TAG_INJECTION_POINT] - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'text.html', + parser: 'tree-sitter-html', + scopes: { + fragment: 'text.html', + raw_element: 'script.tag' + }, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + } + ) atom.grammars.addGrammar(jsGrammar) atom.grammars.addGrammar(htmlGrammar) @@ -1533,11 +1573,17 @@ describe('TreeSitterLanguageMode', () => {
    `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) const position = buffer.findSync('name').start - expect(languageMode.scopeDescriptorForPosition(position).getScopesArray()).toEqual([ + expect( + languageMode.scopeDescriptorForPosition(position).getScopesArray() + ).toEqual([ 'text.html', 'script.tag', 'source.js', @@ -1558,10 +1604,10 @@ describe('TreeSitterLanguageMode', () => { }) buffer.setText('a; ') - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.scopeDescriptorForBufferPosition([0, 3]).getScopesArray()).toEqual([ - 'source.js' - ]) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect( + editor.scopeDescriptorForBufferPosition([0, 3]).getScopesArray() + ).toEqual(['source.js']) }) it('works when the given position is between tokens', () => { @@ -1570,19 +1616,18 @@ describe('TreeSitterLanguageMode', () => { parser: 'tree-sitter-javascript', scopes: { program: 'source.js', - comment: 'comment.block', + comment: 'comment.block' } }) buffer.setText('a // b') - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.scopeDescriptorForBufferPosition([0, 2]).getScopesArray()).toEqual([ - 'source.js' - ]) - expect(editor.scopeDescriptorForBufferPosition([0, 3]).getScopesArray()).toEqual([ - 'source.js', - 'comment.block' - ]) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect( + editor.scopeDescriptorForBufferPosition([0, 2]).getScopesArray() + ).toEqual(['source.js']) + expect( + editor.scopeDescriptorForBufferPosition([0, 3]).getScopesArray() + ).toEqual(['source.js', 'comment.block']) }) }) @@ -1595,8 +1640,12 @@ describe('TreeSitterLanguageMode', () => { buffer.setText('foo({bar: baz});') - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.syntaxTreeScopeDescriptorForBufferPosition([0, 6]).getScopesArray()).toEqual([ + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect( + editor + .syntaxTreeScopeDescriptorForBufferPosition([0, 6]) + .getScopesArray() + ).toEqual([ 'source.js', 'program', 'expression_statement', @@ -1609,12 +1658,12 @@ describe('TreeSitterLanguageMode', () => { buffer.setText('//bar\n') - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.syntaxTreeScopeDescriptorForBufferPosition([0, 5]).getScopesArray()).toEqual([ - 'source.js', - 'program', - 'comment' - ]) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect( + editor + .syntaxTreeScopeDescriptorForBufferPosition([0, 5]) + .getScopesArray() + ).toEqual(['source.js', 'program', 'comment']) }) it('includes nodes in injected syntax trees', async () => { @@ -1626,13 +1675,17 @@ describe('TreeSitterLanguageMode', () => { injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'text.html', - parser: 'tree-sitter-html', - scopes: {}, - injectionRegExp: 'html', - injectionPoints: [SCRIPT_TAG_INJECTION_POINT] - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'text.html', + parser: 'tree-sitter-html', + scopes: {}, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + } + ) atom.grammars.addGrammar(jsGrammar) atom.grammars.addGrammar(htmlGrammar) @@ -1647,11 +1700,19 @@ describe('TreeSitterLanguageMode', () => {
    `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) const position = buffer.findSync('name').start - expect(editor.syntaxTreeScopeDescriptorForBufferPosition(position).getScopesArray()).toEqual([ + expect( + editor + .syntaxTreeScopeDescriptorForBufferPosition(position) + .getScopesArray() + ).toEqual([ 'text.html', 'fragment', 'element', @@ -1680,13 +1741,15 @@ describe('TreeSitterLanguageMode', () => { buffer.setText('foo({bar: baz});') - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.bufferRangeForScopeAtPosition(null, [0, 6])).toEqual( - [[0, 5], [0, 8]] - ) - expect(editor.bufferRangeForScopeAtPosition(null, [0, 9])).toEqual( - [[0, 8], [0, 9]] - ) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect(editor.bufferRangeForScopeAtPosition(null, [0, 6])).toEqual([ + [0, 5], + [0, 8] + ]) + expect(editor.bufferRangeForScopeAtPosition(null, [0, 9])).toEqual([ + [0, 8], + [0, 9] + ]) }) it('includes nodes in injected syntax trees', async () => { @@ -1698,13 +1761,17 @@ describe('TreeSitterLanguageMode', () => { injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'html', - parser: 'tree-sitter-html', - scopes: {}, - injectionRegExp: 'html', - injectionPoints: [SCRIPT_TAG_INJECTION_POINT] - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + } + ) atom.grammars.addGrammar(jsGrammar) atom.grammars.addGrammar(htmlGrammar) @@ -1719,14 +1786,19 @@ describe('TreeSitterLanguageMode', () => {
    `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) const nameProperty = buffer.findSync('name') - const {start} = nameProperty - const position = Object.assign({}, start, {column: start.column + 2}) - expect(languageMode.bufferRangeForScopeAtPosition(null, position)) - .toEqual(nameProperty) + const { start } = nameProperty + const position = Object.assign({}, start, { column: start.column + 2 }) + expect( + languageMode.bufferRangeForScopeAtPosition(null, position) + ).toEqual(nameProperty) }) }) @@ -1736,20 +1808,20 @@ describe('TreeSitterLanguageMode', () => { scopeName: 'javascript', parser: 'tree-sitter-javascript', scopes: { - 'property_identifier': 'variable.other.object.property', - 'template_string': 'string.quoted.template' + property_identifier: 'variable.other.object.property', + template_string: 'string.quoted.template' } }) buffer.setText('a(`${b({ccc: ddd})} eee`);') - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - expect(editor.bufferRangeForScopeAtPosition('.variable.property', [0, 9])).toEqual( - [[0, 8], [0, 11]] - ) - expect(editor.bufferRangeForScopeAtPosition('.string.quoted', [0, 6])).toEqual( - [[0, 2], [0, 24]] - ) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) + expect( + editor.bufferRangeForScopeAtPosition('.variable.property', [0, 9]) + ).toEqual([[0, 8], [0, 11]]) + expect( + editor.bufferRangeForScopeAtPosition('.string.quoted', [0, 6]) + ).toEqual([[0, 2], [0, 24]]) }) it('includes nodes in injected syntax trees', async () => { @@ -1757,21 +1829,25 @@ describe('TreeSitterLanguageMode', () => { scopeName: 'javascript', parser: 'tree-sitter-javascript', scopes: { - 'property_identifier': 'variable.other.object.property', + property_identifier: 'variable.other.object.property' }, injectionRegExp: 'javascript', injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'html', - parser: 'tree-sitter-html', - scopes: { - 'element': 'meta.element.html' - }, - injectionRegExp: 'html', - injectionPoints: [SCRIPT_TAG_INJECTION_POINT] - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: { + element: 'meta.element.html' + }, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + } + ) atom.grammars.addGrammar(jsGrammar) atom.grammars.addGrammar(htmlGrammar) @@ -1786,16 +1862,28 @@ describe('TreeSitterLanguageMode', () => {
    `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) const nameProperty = buffer.findSync('name') - const {start} = nameProperty - const position = Object.assign({}, start, {column: start.column + 2}) - expect(languageMode.bufferRangeForScopeAtPosition('.object.property', position)) - .toEqual(nameProperty) - expect(languageMode.bufferRangeForScopeAtPosition('.meta.element.html', position)) - .toEqual(buffer.findSync('\\${person\\.name}')) + const { start } = nameProperty + const position = Object.assign({}, start, { column: start.column + 2 }) + expect( + languageMode.bufferRangeForScopeAtPosition( + '.object.property', + position + ) + ).toEqual(nameProperty) + expect( + languageMode.bufferRangeForScopeAtPosition( + '.meta.element.html', + position + ) + ).toEqual(buffer.findSync('\\${person\\.name}')) }) it('accepts node-matching functions as selectors', async () => { @@ -1807,13 +1895,17 @@ describe('TreeSitterLanguageMode', () => { injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'html', - parser: 'tree-sitter-html', - scopes: {}, - injectionRegExp: 'html', - injectionPoints: [SCRIPT_TAG_INJECTION_POINT] - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + } + ) atom.grammars.addGrammar(jsGrammar) atom.grammars.addGrammar(htmlGrammar) @@ -1828,16 +1920,25 @@ describe('TreeSitterLanguageMode', () => { `) - const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) const nameProperty = buffer.findSync('name') - const {start} = nameProperty - const position = Object.assign({}, start, {column: start.column + 2}) + const { start } = nameProperty + const position = Object.assign({}, start, { column: start.column + 2 }) const templateStringInCallExpression = node => - node.type === 'template_string' && node.parent.type === 'call_expression' - expect(languageMode.bufferRangeForScopeAtPosition(templateStringInCallExpression, position)) - .toEqual([[3, 19], [5, 15]]) + node.type === 'template_string' && + node.parent.type === 'call_expression' + expect( + languageMode.bufferRangeForScopeAtPosition( + templateStringInCallExpression, + position + ) + ).toEqual([[3, 19], [5, 15]]) }) }) }) @@ -1850,16 +1951,16 @@ describe('TreeSitterLanguageMode', () => { }) buffer.setText('foo(bar({x: 2}));') - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + const languageMode = new TreeSitterLanguageMode({ buffer, grammar }) buffer.setLanguageMode(languageMode) expect(languageMode.getSyntaxNodeAtPosition([0, 6]).range).toEqual( buffer.findSync('bar') ) const findFoo = node => node.type === 'call_expression' && node.firstChild.text === 'foo' - expect(languageMode.getSyntaxNodeAtPosition([0, 6], findFoo).range).toEqual( - [[0, 0], [0, buffer.getText().length - 1]] - ) + expect( + languageMode.getSyntaxNodeAtPosition([0, 6], findFoo).range + ).toEqual([[0, 0], [0, buffer.getText().length - 1]]) }) }) @@ -1868,26 +1969,35 @@ describe('TreeSitterLanguageMode', () => { const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { scopeName: 'javascript', parser: 'tree-sitter-javascript', - comments: {start: '//'}, + comments: { start: '//' }, injectionRegExp: 'javascript', injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'html', - parser: 'tree-sitter-html', - scopes: {}, - comments: {start: ''}, - injectionRegExp: 'html', - injectionPoints: [SCRIPT_TAG_INJECTION_POINT] - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + comments: { start: '' }, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + } + ) atom.grammars.addGrammar(jsGrammar) atom.grammars.addGrammar(htmlGrammar) - const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) - buffer.setText(` + buffer.setText( + `
    hi
    - `.trim()) + `.trim() + ) + const htmlCommentStrings = { + commentStartString: '' + } + const jsCommentStrings = { + commentStartString: '//', + commentEndString: undefined + } - const htmlCommentStrings = {commentStartString: ''} - const jsCommentStrings = {commentStartString: '//', commentEndString: undefined} - - expect(languageMode.commentStringsForPosition(new Point(0, 0))).toEqual(htmlCommentStrings) - expect(languageMode.commentStringsForPosition(new Point(1, 0))).toEqual(htmlCommentStrings) - expect(languageMode.commentStringsForPosition(new Point(2, 0))).toEqual(jsCommentStrings) - expect(languageMode.commentStringsForPosition(new Point(3, 0))).toEqual(jsCommentStrings) - expect(languageMode.commentStringsForPosition(new Point(4, 0))).toEqual(htmlCommentStrings) - expect(languageMode.commentStringsForPosition(new Point(5, 0))).toEqual(jsCommentStrings) - expect(languageMode.commentStringsForPosition(new Point(6, 0))).toEqual(htmlCommentStrings) + expect(languageMode.commentStringsForPosition(new Point(0, 0))).toEqual( + htmlCommentStrings + ) + expect(languageMode.commentStringsForPosition(new Point(1, 0))).toEqual( + htmlCommentStrings + ) + expect(languageMode.commentStringsForPosition(new Point(2, 0))).toEqual( + jsCommentStrings + ) + expect(languageMode.commentStringsForPosition(new Point(3, 0))).toEqual( + jsCommentStrings + ) + expect(languageMode.commentStringsForPosition(new Point(4, 0))).toEqual( + htmlCommentStrings + ) + expect(languageMode.commentStringsForPosition(new Point(5, 0))).toEqual( + jsCommentStrings + ) + expect(languageMode.commentStringsForPosition(new Point(6, 0))).toEqual( + htmlCommentStrings + ) }) }) @@ -1915,17 +2045,17 @@ describe('TreeSitterLanguageMode', () => { it('expands and contracts the selection based on the syntax tree', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', - scopes: {'program': 'source'} + scopes: { program: 'source' } }) - buffer.setText(dedent ` + buffer.setText(dedent` function a (b, c, d) { eee.f() g() } `) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) + buffer.setLanguageMode(new TreeSitterLanguageMode({ buffer, grammar })) editor.setCursorBufferPosition([1, 3]) editor.selectLargerSyntaxNode() @@ -1937,7 +2067,9 @@ describe('TreeSitterLanguageMode', () => { editor.selectLargerSyntaxNode() expect(editor.getSelectedText()).toBe('{\n eee.f()\n g()\n}') editor.selectLargerSyntaxNode() - expect(editor.getSelectedText()).toBe('function a (b, c, d) {\n eee.f()\n g()\n}') + expect(editor.getSelectedText()).toBe( + 'function a (b, c, d) {\n eee.f()\n g()\n}' + ) editor.selectSmallerSyntaxNode() expect(editor.getSelectedText()).toBe('{\n eee.f()\n g()\n}') @@ -1956,9 +2088,9 @@ describe('TreeSitterLanguageMode', () => { scopeName: 'javascript', parser: 'tree-sitter-javascript', scopes: { - 'property_identifier': 'property', + property_identifier: 'property', 'call_expression > identifier': 'function', - 'template_string': 'string', + template_string: 'string', 'template_substitution > "${"': 'interpolation', 'template_substitution > "}"': 'interpolation' }, @@ -1966,24 +2098,35 @@ describe('TreeSitterLanguageMode', () => { injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) - const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'html', - parser: 'tree-sitter-html', - scopes: { - fragment: 'html', - tag_name: 'tag', - attribute_name: 'attr' - }, - injectionRegExp: 'html' - }) + const htmlGrammar = new TreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: { + fragment: 'html', + tag_name: 'tag', + attribute_name: 'attr' + }, + injectionRegExp: 'html' + } + ) atom.grammars.addGrammar(htmlGrammar) buffer.setText('a = html ` c${def()}e${f}g `') - const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: jsGrammar, + grammars: atom.grammars + }) buffer.setLanguageMode(languageMode) - editor.setCursorBufferPosition({row: 0, column: buffer.getText().indexOf('ef()')}) + editor.setCursorBufferPosition({ + row: 0, + column: buffer.getText().indexOf('ef()') + }) editor.selectLargerSyntaxNode() expect(editor.getSelectedText()).toBe('def') editor.selectLargerSyntaxNode() @@ -2023,7 +2166,6 @@ function expectTokensToEqual (editor, expectedTokenLines) { // Assert that the correct tokens are returned regardless of which row // the highlighting iterator starts on. for (let startRow = 0; startRow <= lastRow; startRow++) { - // Clear the screen line cache between iterations, but not on the first // iteration, so that the first iteration tests that the cache has been // correctly invalidated by any changes. @@ -2035,13 +2177,17 @@ function expectTokensToEqual (editor, expectedTokenLines) { const tokenLines = [] for (let row = startRow; row <= lastRow; row++) { - tokenLines[row] = editor.tokensForScreenRow(row).map(({text, scopes}) => ({ - text, - scopes: scopes.map(scope => scope - .split(' ') - .map(className => className.replace('syntax--', '')) - .join(' ')) - })) + tokenLines[row] = editor + .tokensForScreenRow(row) + .map(({ text, scopes }) => ({ + text, + scopes: scopes.map(scope => + scope + .split(' ') + .map(className => className.replace('syntax--', '')) + .join(' ') + ) + })) } for (let row = startRow; row <= lastRow; row++) { @@ -2050,7 +2196,10 @@ function expectTokensToEqual (editor, expectedTokenLines) { expect(tokenLine.length).toEqual(expectedTokenLine.length) for (let i = 0; i < tokenLine.length; i++) { - expect(tokenLine[i]).toEqual(expectedTokenLine[i], `Token ${i}, startRow: ${startRow}`) + expect(tokenLine[i]).toEqual( + expectedTokenLine[i], + `Token ${i}, startRow: ${startRow}` + ) } } } @@ -2063,7 +2212,10 @@ function expectTokensToEqual (editor, expectedTokenLines) { const HTML_TEMPLATE_LITERAL_INJECTION_POINT = { type: 'call_expression', language (node) { - if (node.lastChild.type === 'template_string' && node.firstChild.type === 'identifier') { + if ( + node.lastChild.type === 'template_string' && + node.firstChild.type === 'identifier' + ) { return node.firstChild.text } }, @@ -2074,6 +2226,10 @@ const HTML_TEMPLATE_LITERAL_INJECTION_POINT = { const SCRIPT_TAG_INJECTION_POINT = { type: 'raw_element', - language () { return 'javascript' }, - content (node) { return node.child(1) } + language () { + return 'javascript' + }, + content (node) { + return node.child(1) + } } diff --git a/spec/update-process-env-spec.js b/spec/update-process-env-spec.js index f7948d998..5185f0198 100644 --- a/spec/update-process-env-spec.js +++ b/spec/update-process-env-spec.js @@ -1,12 +1,12 @@ /** @babel */ -/* eslint-env jasmine */ -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' import path from 'path' import childProcess from 'child_process' -import {updateProcessEnv, shouldGetEnvFromShell} from '../src/update-process-env' +import { + updateProcessEnv, + shouldGetEnvFromShell +} from '../src/update-process-env' import dedent from 'dedent' -import {EventEmitter} from 'events' import mockSpawn from 'mock-spawn' const temp = require('temp').track() @@ -46,7 +46,13 @@ describe('updateProcessEnv(launchEnv)', function () { const initialProcessEnv = process.env - await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', TERM: 'xterm-something', KEY1: 'value1', KEY2: 'value2'}) + await updateProcessEnv({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PWD: '/the/dir', + TERM: 'xterm-something', + KEY1: 'value1', + KEY2: 'value2' + }) expect(process.env).toEqual({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', @@ -74,7 +80,12 @@ describe('updateProcessEnv(launchEnv)', function () { const initialProcessEnv = process.env - await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PROMPT: '$P$G', KEY1: 'value1', KEY2: 'value2'}) + await updateProcessEnv({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PROMPT: '$P$G', + KEY1: 'value1', + KEY2: 'value2' + }) expect(process.env).toEqual({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PROMPT: '$P$G', @@ -103,13 +114,15 @@ describe('updateProcessEnv(launchEnv)', function () { await updateProcessEnv({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', - PSModulePath: 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\', + PSModulePath: + 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\', KEY1: 'value1', KEY2: 'value2' }) expect(process.env).toEqual({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', - PSModulePath: 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\', + PSModulePath: + 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\', KEY1: 'value1', KEY2: 'value2', NODE_ENV: 'the-node-env', @@ -133,7 +146,10 @@ describe('updateProcessEnv(launchEnv)', function () { ATOM_HOME: '/the/atom/home' } - await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir'}) + await updateProcessEnv({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PWD: '/the/dir' + }) expect(process.env).toEqual({ PWD: '/the/dir', ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', @@ -142,7 +158,11 @@ describe('updateProcessEnv(launchEnv)', function () { ATOM_HOME: '/the/atom/home' }) - await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: path.join(newAtomHomePath, 'non-existent')}) + await updateProcessEnv({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PWD: '/the/dir', + ATOM_HOME: path.join(newAtomHomePath, 'non-existent') + }) expect(process.env).toEqual({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', @@ -151,7 +171,11 @@ describe('updateProcessEnv(launchEnv)', function () { ATOM_HOME: '/the/atom/home' }) - await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: newAtomHomePath}) + await updateProcessEnv({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PWD: '/the/dir', + ATOM_HOME: newAtomHomePath + }) expect(process.env).toEqual({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', @@ -169,7 +193,13 @@ describe('updateProcessEnv(launchEnv)', function () { ATOM_HOME: '/the/atom/home' } - await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'}) + await updateProcessEnv({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PWD: '/the/dir', + NODE_ENV: 'the-node-env', + NODE_PATH: '/the/node/path', + ATOM_HOME: '/the/atom/home' + }) expect(process.env).toEqual({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', @@ -178,7 +208,12 @@ describe('updateProcessEnv(launchEnv)', function () { ATOM_HOME: '/the/atom/home' }) - await updateProcessEnv({PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'}) + await updateProcessEnv({ + PWD: '/the/dir', + NODE_ENV: 'the-node-env', + NODE_PATH: '/the/node/path', + ATOM_HOME: '/the/atom/home' + }) expect(process.env).toEqual({ ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', @@ -215,16 +250,21 @@ describe('updateProcessEnv(launchEnv)', function () { describe('when the launch environment does not come from a shell', function () { describe('on macOS', function () { - it('updates process.env to match the environment in the user\'s login shell', async function () { + it("updates process.env to match the environment in the user's login shell", async function () { if (process.platform === 'win32') return // TestsThatFailOnWin32 process.platform = 'darwin' process.env.SHELL = '/my/custom/bash' - spawn.setDefault(spawn.simple(0, dedent` + spawn.setDefault( + spawn.simple( + 0, + dedent` FOO=BAR=BAZ=QUUX TERM=xterm-something PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path - `)) + ` + ) + ) await updateProcessEnv(process.env) expect(spawn.calls.length).toBe(1) expect(spawn.calls[0].command).toBe('/my/custom/bash') @@ -241,16 +281,21 @@ describe('updateProcessEnv(launchEnv)', function () { }) describe('on linux', function () { - it('updates process.env to match the environment in the user\'s login shell', async function () { + it("updates process.env to match the environment in the user's login shell", async function () { if (process.platform === 'win32') return // TestsThatFailOnWin32 process.platform = 'linux' process.env.SHELL = '/my/custom/bash' - spawn.setDefault(spawn.simple(0, dedent` + spawn.setDefault( + spawn.simple( + 0, + dedent` FOO=BAR=BAZ=QUUX TERM=xterm-something PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path - `)) + ` + ) + ) await updateProcessEnv(process.env) expect(spawn.calls.length).toBe(1) expect(spawn.calls[0].command).toBe('/my/custom/bash') @@ -270,11 +315,11 @@ describe('updateProcessEnv(launchEnv)', function () { it('does not update process.env', async function () { process.platform = 'win32' spyOn(childProcess, 'spawn') - process.env = {FOO: 'bar'} + process.env = { FOO: 'bar' } await updateProcessEnv(process.env) expect(childProcess.spawn).not.toHaveBeenCalled() - expect(process.env).toEqual({FOO: 'bar'}) + expect(process.env).toEqual({ FOO: 'bar' }) }) }) @@ -283,30 +328,52 @@ describe('updateProcessEnv(launchEnv)', function () { if (process.platform === 'win32') return // TestsThatFailOnWin32 process.platform = 'darwin' - expect(shouldGetEnvFromShell({SHELL: '/bin/sh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/sh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/bin/bash'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/bash'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/bin/zsh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/zsh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/bin/fish'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/fish'})).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/bin/sh' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/sh' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/bin/bash' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/bash' })).toBe( + true + ) + expect(shouldGetEnvFromShell({ SHELL: '/bin/zsh' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/zsh' })).toBe( + true + ) + expect(shouldGetEnvFromShell({ SHELL: '/bin/fish' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/fish' })).toBe( + true + ) process.platform = 'linux' - expect(shouldGetEnvFromShell({SHELL: '/bin/sh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/sh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/bin/bash'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/bash'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/bin/zsh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/zsh'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/bin/fish'})).toBe(true) - expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/fish'})).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/bin/sh' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/sh' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/bin/bash' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/bash' })).toBe( + true + ) + expect(shouldGetEnvFromShell({ SHELL: '/bin/zsh' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/zsh' })).toBe( + true + ) + expect(shouldGetEnvFromShell({ SHELL: '/bin/fish' })).toBe(true) + expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/fish' })).toBe( + true + ) }) it('returns false when the environment indicates that Atom was launched from a shell', function () { process.platform = 'darwin' - expect(shouldGetEnvFromShell({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', SHELL: '/bin/sh'})).toBe(false) + expect( + shouldGetEnvFromShell({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + SHELL: '/bin/sh' + }) + ).toBe(false) process.platform = 'linux' - expect(shouldGetEnvFromShell({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', SHELL: '/bin/sh'})).toBe(false) + expect( + shouldGetEnvFromShell({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + SHELL: '/bin/sh' + }) + ).toBe(false) }) it('returns false when the shell is undefined or empty', function () { diff --git a/spec/uri-handler-registry-spec.js b/spec/uri-handler-registry-spec.js index d2da93087..a8faa4500 100644 --- a/spec/uri-handler-registry-spec.js +++ b/spec/uri-handler-registry-spec.js @@ -2,8 +2,6 @@ import url from 'url' -import {it} from './async-spec-helpers' - import URIHandlerRegistry from '../src/uri-handler-registry' describe('URIHandlerRegistry', () => { @@ -24,11 +22,17 @@ describe('URIHandlerRegistry', () => { expect(otherPackageSpy).not.toHaveBeenCalled() registry.handleURI('atom://test-package/path') - expect(testPackageSpy).toHaveBeenCalledWith(url.parse('atom://test-package/path', true), 'atom://test-package/path') + expect(testPackageSpy).toHaveBeenCalledWith( + url.parse('atom://test-package/path', true), + 'atom://test-package/path' + ) expect(otherPackageSpy).not.toHaveBeenCalled() registry.handleURI('atom://other-package/path') - expect(otherPackageSpy).toHaveBeenCalledWith(url.parse('atom://other-package/path', true), 'atom://other-package/path') + expect(otherPackageSpy).toHaveBeenCalledWith( + url.parse('atom://other-package/path', true), + 'atom://other-package/path' + ) }) it('keeps track of the most recent URIs', () => { @@ -50,9 +54,18 @@ describe('URIHandlerRegistry', () => { uris.forEach(u => registry.handleURI(u)) expect(changeSpy.callCount).toBe(5) - expect(registry.getRecentlyHandledURIs()).toEqual(uris.map((u, idx) => { - return {id: idx + 1, uri: u, handled: !u.match(/fake/), host: url.parse(u).host} - }).reverse()) + expect(registry.getRecentlyHandledURIs()).toEqual( + uris + .map((u, idx) => { + return { + id: idx + 1, + uri: u, + handled: !u.match(/fake/), + host: url.parse(u).host + } + }) + .reverse() + ) registry.handleURI('atom://another/url') expect(changeSpy.callCount).toBe(6) @@ -63,7 +76,7 @@ describe('URIHandlerRegistry', () => { }) it('refuses to handle bad URLs', () => { - [ + ;[ 'atom:package/path', 'atom:8080://package/path', 'user:pass@atom://package/path', diff --git a/spec/view-registry-spec.js b/spec/view-registry-spec.js index db8b077f1..b38ffa5f3 100644 --- a/spec/view-registry-spec.js +++ b/spec/view-registry-spec.js @@ -22,8 +22,7 @@ describe('ViewRegistry', () => { it('returns the given DOM node', () => { const node = document.createElement('div') expect(registry.getView(node)).toBe(node) - }) - ) + })) describe('when passed an object with an element property', () => it("returns the element property if it's an instance of HTMLElement", () => { @@ -35,8 +34,7 @@ describe('ViewRegistry', () => { const component = new TestComponent() expect(registry.getView(component)).toBe(component.element) - }) - ) + })) describe('when passed an object with a getElement function', () => it("returns the return value of getElement if it's an instance of HTMLElement", () => { @@ -51,8 +49,7 @@ describe('ViewRegistry', () => { const component = new TestComponent() expect(registry.getView(component)).toBe(component.myElement) - }) - ) + })) describe('when passed a model object', () => { describe("when a view provider is registered matching the object's constructor", () => @@ -70,7 +67,7 @@ describe('ViewRegistry', () => { const model = new TestModel() - registry.addViewProvider(TestModel, (model) => + registry.addViewProvider(TestModel, model => new TestView().initialize(model) ) @@ -82,12 +79,11 @@ describe('ViewRegistry', () => { const view2 = registry.getView(subclassModel) expect(view2 instanceof TestView).toBe(true) expect(view2.model).toBe(subclassModel) - }) - ) + })) describe('when a view provider is registered generically, and works with the object', () => it('constructs a view element and assigns the model on it', () => { - registry.addViewProvider((model) => { + registry.addViewProvider(model => { if (model.a === 'b') { const element = document.createElement('div') element.className = 'test-element' @@ -95,18 +91,16 @@ describe('ViewRegistry', () => { } }) - const view = registry.getView({a: 'b'}) + const view = registry.getView({ a: 'b' }) expect(view.className).toBe('test-element') - expect(() => registry.getView({a: 'c'})).toThrow() - }) - ) + expect(() => registry.getView({ a: 'c' })).toThrow() + })) describe("when no view provider is registered for the object's constructor", () => it('throws an exception', () => { expect(() => registry.getView({})).toThrow() - }) - ) + })) }) }) @@ -120,22 +114,23 @@ describe('ViewRegistry', () => { } } - const disposable = registry.addViewProvider(TestModel, (model) => + const disposable = registry.addViewProvider(TestModel, model => new TestView().initialize(model) ) expect(registry.getView(new TestModel()) instanceof TestView).toBe(true) disposable.dispose() expect(() => registry.getView(new TestModel())).toThrow() - }) - ) + })) describe('::updateDocument(fn) and ::readDocument(fn)', () => { let frameRequests = null beforeEach(() => { frameRequests = [] - spyOn(window, 'requestAnimationFrame').andCallFake(fn => frameRequests.push(fn)) + spyOn(window, 'requestAnimationFrame').andCallFake(fn => + frameRequests.push(fn) + ) }) it('performs all pending writes before all pending reads on the next animation frame', () => { @@ -201,16 +196,19 @@ describe('ViewRegistry', () => { let updateCalled = false let readCalled = false - waitsFor('getNextUpdatePromise to resolve', (done) => { + waitsFor('getNextUpdatePromise to resolve', done => { registry.getNextUpdatePromise().then(() => { expect(updateCalled).toBe(true) expect(readCalled).toBe(true) done() }) - registry.updateDocument(() => { updateCalled = true }) - registry.readDocument(() => { readCalled = true }) + registry.updateDocument(() => { + updateCalled = true + }) + registry.readDocument(() => { + readCalled = true + }) }) - }) - ) + })) }) diff --git a/spec/window-event-handler-spec.js b/spec/window-event-handler-spec.js index b5000388c..6f840f3bf 100644 --- a/spec/window-event-handler-spec.js +++ b/spec/window-event-handler-spec.js @@ -14,7 +14,10 @@ describe('WindowEventHandler', () => { return loadSettings }) atom.project.destroy() - windowEventHandler = new WindowEventHandler({atomEnvironment: atom, applicationDelegate: atom.applicationDelegate}) + windowEventHandler = new WindowEventHandler({ + atomEnvironment: atom, + applicationDelegate: atom.applicationDelegate + }) windowEventHandler.initialize(window, document) }) @@ -25,45 +28,44 @@ describe('WindowEventHandler', () => { describe('when the window is loaded', () => it("doesn't have .is-blurred on the body tag", () => { - if (process.platform === 'win32') { return } // Win32TestFailures - can not steal focus + if (process.platform === 'win32') { + return + } // Win32TestFailures - can not steal focus expect(document.body.className).not.toMatch('is-blurred') - }) - ) + })) describe('when the window is blurred', () => { beforeEach(() => window.dispatchEvent(new CustomEvent('blur'))) afterEach(() => document.body.classList.remove('is-blurred')) - it('adds the .is-blurred class on the body', () => expect(document.body.className).toMatch('is-blurred')) + it('adds the .is-blurred class on the body', () => + expect(document.body.className).toMatch('is-blurred')) describe('when the window is focused again', () => it('removes the .is-blurred class from the body', () => { window.dispatchEvent(new CustomEvent('focus')) expect(document.body.className).not.toMatch('is-blurred') - }) - ) + })) }) - + describe('resize event', () => it('calls storeWindowDimensions', () => { spyOn(atom, 'storeWindowDimensions') window.dispatchEvent(new CustomEvent('resize')) expect(atom.storeWindowDimensions).toHaveBeenCalled() - }) - ) - + })) + describe('window:close event', () => it('closes the window', () => { spyOn(atom, 'close') window.dispatchEvent(new CustomEvent('window:close')) expect(atom.close).toHaveBeenCalled() - }) - ) + })) describe('when a link is clicked', () => { it('opens the http/https links in an external application', () => { - const {shell} = require('electron') + const { shell } = require('electron') spyOn(shell, 'openExternal') const link = document.createElement('a') @@ -71,7 +73,11 @@ describe('WindowEventHandler', () => { link.appendChild(linkChild) link.href = 'http://github.com' jasmine.attachToDOM(link) - const fakeEvent = {target: linkChild, currentTarget: link, preventDefault: () => {}} + const fakeEvent = { + target: linkChild, + currentTarget: link, + preventDefault: () => {} + } windowEventHandler.handleLinkClick(fakeEvent) expect(shell.openExternal).toHaveBeenCalled() @@ -104,7 +110,11 @@ describe('WindowEventHandler', () => { link.appendChild(linkChild) link.href = 'atom://github.com' jasmine.attachToDOM(link) - const fakeEvent = {target: linkChild, currentTarget: link, preventDefault: () => {}} + const fakeEvent = { + target: linkChild, + currentTarget: link, + preventDefault: () => {} + } windowEventHandler.handleLinkClick(fakeEvent) expect(uriHandler.handleURI).toHaveBeenCalled() @@ -118,12 +128,13 @@ describe('WindowEventHandler', () => { jasmine.attachToDOM(form) let defaultPrevented = false - const event = new CustomEvent('submit', {bubbles: true}) - event.preventDefault = () => { defaultPrevented = true } + const event = new CustomEvent('submit', { bubbles: true }) + event.preventDefault = () => { + defaultPrevented = true + } form.dispatchEvent(event) expect(defaultPrevented).toBe(true) - }) - ) + })) describe('core:focus-next and core:focus-previous', () => { describe('when there is no currently focused element', () => @@ -138,14 +149,17 @@ describe('WindowEventHandler', () => { const elements = wrapperDiv.firstChild jasmine.attachToDOM(elements) - elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-next', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(1) document.body.focus() - elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-previous', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(2) - }) - ) + })) describe('when a tabindex is set on the currently focused element', () => it('focuses the element with the next highest/lowest tabindex, skipping disabled elements', () => { @@ -166,60 +180,85 @@ describe('WindowEventHandler', () => { elements.querySelector('[tabindex="1"]').focus() - elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-next', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(2) - elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-next', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(3) - elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-next', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(5) - elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-next', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(7) - elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-next', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(1) - elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-previous', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(7) - elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-previous', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(5) - elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-previous', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(3) - elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-previous', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(2) - elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-previous', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(1) - elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true})) + elements.dispatchEvent( + new CustomEvent('core:focus-previous', { bubbles: true }) + ) expect(document.activeElement.tabIndex).toBe(7) - }) - ) + })) }) describe('when keydown events occur on the document', () => it('dispatches the event via the KeymapManager and CommandRegistry', () => { const dispatchedCommands = [] atom.commands.onWillDispatch(command => dispatchedCommands.push(command)) - atom.commands.add('*', {'foo-command': () => {}}) - atom.keymaps.add('source-name', {'*': {'x': 'foo-command'}}) + atom.commands.add('*', { 'foo-command': () => {} }) + atom.keymaps.add('source-name', { '*': { x: 'foo-command' } }) - const event = KeymapManager.buildKeydownEvent('x', {target: document.createElement('div')}) + const event = KeymapManager.buildKeydownEvent('x', { + target: document.createElement('div') + }) document.dispatchEvent(event) expect(dispatchedCommands.length).toBe(1) expect(dispatchedCommands[0].type).toBe('foo-command') - }) - ) + })) describe('native key bindings', () => it("correctly dispatches them to active elements with the '.native-key-bindings' class", () => { - const webContentsSpy = jasmine.createSpyObj('webContents', ['copy', 'paste']) + const webContentsSpy = jasmine.createSpyObj('webContents', [ + 'copy', + 'paste' + ]) spyOn(atom.applicationDelegate, 'getCurrentWindow').andReturn({ webContents: webContentsSpy, on: () => {} @@ -248,6 +287,5 @@ describe('WindowEventHandler', () => { expect(webContentsSpy.copy).not.toHaveBeenCalled() expect(webContentsSpy.paste).not.toHaveBeenCalled() - }) - ) + })) }) diff --git a/spec/workspace-center-spec.js b/spec/workspace-center-spec.js index 7c01078c3..b5bf6ba91 100644 --- a/spec/workspace-center-spec.js +++ b/spec/workspace-center-spec.js @@ -2,8 +2,6 @@ const TextEditor = require('../src/text-editor') -import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' - describe('WorkspaceCenter', () => { describe('.observeTextEditors()', () => { it('invokes the observer with current and future text editors', () => { @@ -12,20 +10,25 @@ describe('WorkspaceCenter', () => { const observed = [] const editorAddedBeforeRegisteringObserver = new TextEditor() - const nonEditorItemAddedBeforeRegisteringObserver = document.createElement('div') + const nonEditorItemAddedBeforeRegisteringObserver = document.createElement( + 'div' + ) pane.activateItem(editorAddedBeforeRegisteringObserver) pane.activateItem(nonEditorItemAddedBeforeRegisteringObserver) workspaceCenter.observeTextEditors(editor => observed.push(editor)) const editorAddedAfterRegisteringObserver = new TextEditor() - const nonEditorItemAddedAfterRegisteringObserver = document.createElement('div') + const nonEditorItemAddedAfterRegisteringObserver = document.createElement( + 'div' + ) pane.activateItem(editorAddedAfterRegisteringObserver) pane.activateItem(nonEditorItemAddedAfterRegisteringObserver) - expect(observed).toEqual( - [editorAddedBeforeRegisteringObserver, editorAddedAfterRegisteringObserver] - ) + expect(observed).toEqual([ + editorAddedBeforeRegisteringObserver, + editorAddedAfterRegisteringObserver + ]) }) }) }) diff --git a/spec/workspace-element-spec.js b/spec/workspace-element-spec.js index 7564f6931..afcb9446a 100644 --- a/spec/workspace-element-spec.js +++ b/spec/workspace-element-spec.js @@ -1,11 +1,10 @@ /** @babel */ -const {ipcRenderer} = require('electron') +const { ipcRenderer } = require('electron') const etch = require('etch') const path = require('path') const temp = require('temp').track() -const {Disposable} = require('event-kit') -const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') +const { Disposable } = require('event-kit') const getNextUpdatePromise = () => etch.getScheduler().nextUpdatePromise @@ -35,19 +34,39 @@ describe('WorkspaceElement', () => { const dock = atom.workspace.getLeftDock() dock.show() jasmine.attachToDOM(atom.workspace.getElement()) - expect(atom.workspace.getActivePaneContainer()).toBe(atom.workspace.getCenter()) - dock.getActivePane().getElement().focus() + expect(atom.workspace.getActivePaneContainer()).toBe( + atom.workspace.getCenter() + ) + dock + .getActivePane() + .getElement() + .focus() expect(atom.workspace.getActivePaneContainer()).toBe(dock) }) }) describe('finding the nearest visible pane in a specific direction', () => { - let pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9, - leftDockPane, rightDockPane, bottomDockPane, workspace, workspaceElement + let nearestPaneElement, + pane1, + pane2, + pane3, + pane4, + pane5, + pane6, + pane7, + pane8, + leftDockPane, + rightDockPane, + bottomDockPane, + workspace, + workspaceElement beforeEach(function () { atom.config.set('core.destroyEmptyPanes', false) - expect(document.hasFocus()).toBe(true, 'Document needs to be focused to run this test') + expect(document.hasFocus()).toBe( + true, + 'Document needs to be focused to run this test' + ) workspace = atom.workspace @@ -77,7 +96,7 @@ describe('WorkspaceElement', () => { pane6 = pane5.splitRight() pane8 = pane7.splitRight() - pane9 = pane8.splitRight() + pane8.splitRight() const leftDock = workspace.getLeftDock() const rightDock = workspace.getRightDock() @@ -104,14 +123,20 @@ describe('WorkspaceElement', () => { describe('finding the nearest pane above', () => { describe('when there are multiple rows above the pane', () => { it('returns the pane in the adjacent row above', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('above', pane8) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'above', + pane8 + ) expect(nearestPaneElement).toBe(pane5.getElement()) }) }) describe('when there are no rows above the pane', () => { it('returns null', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('above', pane2) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'above', + pane2 + ) expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull() }) }) @@ -119,7 +144,10 @@ describe('WorkspaceElement', () => { describe('when the bottom dock contains the pane', () => { it('returns the pane in the adjacent row above', () => { workspace.getBottomDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('above', bottomDockPane) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'above', + bottomDockPane + ) expect(nearestPaneElement).toBe(pane7.getElement()) }) }) @@ -128,14 +156,20 @@ describe('WorkspaceElement', () => { describe('finding the nearest pane below', () => { describe('when there are multiple rows below the pane', () => { it('returns the pane in the adjacent row below', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('below', pane2) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'below', + pane2 + ) expect(nearestPaneElement).toBe(pane5.getElement()) }) }) describe('when there are no rows below the pane', () => { it('returns null', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('below', pane8) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'below', + pane8 + ) expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull() }) }) @@ -144,7 +178,10 @@ describe('WorkspaceElement', () => { describe("when the workspace center's bottommost row contains the pane", () => { it("returns the pane in the bottom dock's adjacent row below", () => { workspace.getBottomDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('below', pane8) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'below', + pane8 + ) expect(nearestPaneElement).toBe(bottomDockPane.getElement()) }) }) @@ -154,14 +191,20 @@ describe('WorkspaceElement', () => { describe('finding the nearest pane to the left', () => { describe('when there are multiple columns to the left of the pane', () => { it('returns the pane in the adjacent column to the left', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', pane6) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'left', + pane6 + ) expect(nearestPaneElement).toBe(pane5.getElement()) }) }) describe('when there are no columns to the left of the pane', () => { it('returns null', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', pane4) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'left', + pane4 + ) expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull() }) }) @@ -169,7 +212,10 @@ describe('WorkspaceElement', () => { describe('when the right dock contains the pane', () => { it('returns the pane in the adjacent column to the left', () => { workspace.getRightDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', rightDockPane) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'left', + rightDockPane + ) expect(nearestPaneElement).toBe(pane3.getElement()) }) }) @@ -178,7 +224,10 @@ describe('WorkspaceElement', () => { describe("when the workspace center's leftmost column contains the pane", () => { it("returns the pane in the left dock's adjacent column to the left", () => { workspace.getLeftDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', pane4) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'left', + pane4 + ) expect(nearestPaneElement).toBe(leftDockPane.getElement()) }) }) @@ -187,7 +236,10 @@ describe('WorkspaceElement', () => { it("returns the pane in the left dock's adjacent column to the left", () => { workspace.getLeftDock().show() workspace.getBottomDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', bottomDockPane) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'left', + bottomDockPane + ) expect(nearestPaneElement).toBe(leftDockPane.getElement()) }) }) @@ -197,14 +249,20 @@ describe('WorkspaceElement', () => { describe('finding the nearest pane to the right', () => { describe('when there are multiple columns to the right of the pane', () => { it('returns the pane in the adjacent column to the right', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', pane4) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'right', + pane4 + ) expect(nearestPaneElement).toBe(pane5.getElement()) }) }) describe('when there are no columns to the right of the pane', () => { it('returns null', () => { - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', pane6) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'right', + pane6 + ) expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull() }) }) @@ -212,7 +270,10 @@ describe('WorkspaceElement', () => { describe('when the left dock contains the pane', () => { it('returns the pane in the adjacent column to the right', () => { workspace.getLeftDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', leftDockPane) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'right', + leftDockPane + ) expect(nearestPaneElement).toBe(pane1.getElement()) }) }) @@ -221,7 +282,10 @@ describe('WorkspaceElement', () => { describe("when the workspace center's rightmost column contains the pane", () => { it("returns the pane in the right dock's adjacent column to the right", () => { workspace.getRightDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', pane6) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'right', + pane6 + ) expect(nearestPaneElement).toBe(rightDockPane.getElement()) }) }) @@ -230,7 +294,10 @@ describe('WorkspaceElement', () => { it("returns the pane in the right dock's adjacent column to the right", () => { workspace.getRightDock().show() workspace.getBottomDock().show() - nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', bottomDockPane) + nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection( + 'right', + bottomDockPane + ) expect(nearestPaneElement).toBe(rightDockPane.getElement()) }) }) @@ -243,7 +310,10 @@ describe('WorkspaceElement', () => { beforeEach(function () { atom.config.set('core.destroyEmptyPanes', false) - expect(document.hasFocus()).toBe(true, 'Document needs to be focused to run this test') + expect(document.hasFocus()).toBe( + true, + 'Document needs to be focused to run this test' + ) workspace = atom.workspace expect(workspace.getLeftDock().isVisible()).toBe(false) @@ -267,16 +337,14 @@ describe('WorkspaceElement', () => { startingPane.activate() workspaceElement.focusPaneViewAbove() expect(document.activeElement).toBe(paneAbove.getElement()) - }) - ) + })) describe('when there are no rows above the focused pane', () => it('keeps the current pane focused', function () { startingPane.activate() workspaceElement.focusPaneViewAbove() expect(document.activeElement).toBe(startingPane.getElement()) - }) - ) + })) }) describe('::focusPaneViewBelow()', function () { @@ -286,16 +354,14 @@ describe('WorkspaceElement', () => { startingPane.activate() workspaceElement.focusPaneViewBelow() expect(document.activeElement).toBe(paneBelow.getElement()) - }) - ) + })) describe('when there are no rows below the focused pane', () => it('keeps the current pane focused', function () { startingPane.activate() workspaceElement.focusPaneViewBelow() expect(document.activeElement).toBe(startingPane.getElement()) - }) - ) + })) }) describe('::focusPaneViewOnLeft()', function () { @@ -305,16 +371,14 @@ describe('WorkspaceElement', () => { startingPane.activate() workspaceElement.focusPaneViewOnLeft() expect(document.activeElement).toBe(paneOnLeft.getElement()) - }) - ) + })) describe('when there are no columns to the left of the focused pane', () => it('keeps the current pane focused', function () { startingPane.activate() workspaceElement.focusPaneViewOnLeft() expect(document.activeElement).toBe(startingPane.getElement()) - }) - ) + })) }) describe('::focusPaneViewOnRight()', function () { @@ -324,16 +388,14 @@ describe('WorkspaceElement', () => { startingPane.activate() workspaceElement.focusPaneViewOnRight() expect(document.activeElement).toBe(paneOnRight.getElement()) - }) - ) + })) describe('when there are no columns to the right of the focused pane', () => it('keeps the current pane focused', function () { startingPane.activate() workspaceElement.focusPaneViewOnRight() expect(document.activeElement).toBe(startingPane.getElement()) - }) - ) + })) }) describe('::moveActiveItemToPaneAbove(keepOriginal)', function () { @@ -346,8 +408,7 @@ describe('WorkspaceElement', () => { workspaceElement.moveActiveItemToPaneAbove() expect(workspace.paneForItem(item)).toBe(paneAbove) expect(paneAbove.getActiveItem()).toBe(item) - }) - ) + })) describe('when there are no rows above the focused pane', () => it('keeps the active pane focused', function () { @@ -356,8 +417,7 @@ describe('WorkspaceElement', () => { startingPane.activateItem(item) workspaceElement.moveActiveItemToPaneAbove() expect(workspace.paneForItem(item)).toBe(startingPane) - }) - ) + })) describe('when `keepOriginal: true` is passed in the params', () => it('keeps the item and adds a copy of it to the adjacent pane', function () { @@ -367,11 +427,10 @@ describe('WorkspaceElement', () => { const paneAbove = startingPane.splitUp() startingPane.activate() startingPane.activateItem(itemA) - workspaceElement.moveActiveItemToPaneAbove({keepOriginal: true}) + workspaceElement.moveActiveItemToPaneAbove({ keepOriginal: true }) expect(workspace.paneForItem(itemA)).toBe(startingPane) expect(paneAbove.getActiveItem()).toBe(itemB) - }) - ) + })) }) describe('::moveActiveItemToPaneBelow(keepOriginal)', function () { @@ -384,8 +443,7 @@ describe('WorkspaceElement', () => { workspaceElement.moveActiveItemToPaneBelow() expect(workspace.paneForItem(item)).toBe(paneBelow) expect(paneBelow.getActiveItem()).toBe(item) - }) - ) + })) describe('when there are no rows below the focused pane', () => it('keeps the active item in the focused pane', function () { @@ -394,8 +452,7 @@ describe('WorkspaceElement', () => { startingPane.activateItem(item) workspaceElement.moveActiveItemToPaneBelow() expect(workspace.paneForItem(item)).toBe(startingPane) - }) - ) + })) describe('when `keepOriginal: true` is passed in the params', () => it('keeps the item and adds a copy of it to the adjacent pane', function () { @@ -405,11 +462,10 @@ describe('WorkspaceElement', () => { const paneBelow = startingPane.splitDown() startingPane.activate() startingPane.activateItem(itemA) - workspaceElement.moveActiveItemToPaneBelow({keepOriginal: true}) + workspaceElement.moveActiveItemToPaneBelow({ keepOriginal: true }) expect(workspace.paneForItem(itemA)).toBe(startingPane) expect(paneBelow.getActiveItem()).toBe(itemB) - }) - ) + })) }) describe('::moveActiveItemToPaneOnLeft(keepOriginal)', function () { @@ -422,8 +478,7 @@ describe('WorkspaceElement', () => { workspaceElement.moveActiveItemToPaneOnLeft() expect(workspace.paneForItem(item)).toBe(paneOnLeft) expect(paneOnLeft.getActiveItem()).toBe(item) - }) - ) + })) describe('when there are no columns to the left of the focused pane', () => it('keeps the active item in the focused pane', function () { @@ -432,8 +487,7 @@ describe('WorkspaceElement', () => { startingPane.activateItem(item) workspaceElement.moveActiveItemToPaneOnLeft() expect(workspace.paneForItem(item)).toBe(startingPane) - }) - ) + })) describe('when `keepOriginal: true` is passed in the params', () => it('keeps the item and adds a copy of it to the adjacent pane', function () { @@ -443,11 +497,10 @@ describe('WorkspaceElement', () => { const paneOnLeft = startingPane.splitLeft() startingPane.activate() startingPane.activateItem(itemA) - workspaceElement.moveActiveItemToPaneOnLeft({keepOriginal: true}) + workspaceElement.moveActiveItemToPaneOnLeft({ keepOriginal: true }) expect(workspace.paneForItem(itemA)).toBe(startingPane) expect(paneOnLeft.getActiveItem()).toBe(itemB) - }) - ) + })) }) describe('::moveActiveItemToPaneOnRight(keepOriginal)', function () { @@ -460,8 +513,7 @@ describe('WorkspaceElement', () => { workspaceElement.moveActiveItemToPaneOnRight() expect(workspace.paneForItem(item)).toBe(paneOnRight) expect(paneOnRight.getActiveItem()).toBe(item) - }) - ) + })) describe('when there are no columns to the right of the focused pane', () => it('keeps the active item in the focused pane', function () { @@ -470,8 +522,7 @@ describe('WorkspaceElement', () => { startingPane.activateItem(item) workspaceElement.moveActiveItemToPaneOnRight() expect(workspace.paneForItem(item)).toBe(startingPane) - }) - ) + })) describe('when `keepOriginal: true` is passed in the params', () => it('keeps the item and adds a copy of it to the adjacent pane', function () { @@ -481,11 +532,10 @@ describe('WorkspaceElement', () => { const paneOnRight = startingPane.splitRight() startingPane.activate() startingPane.activateItem(itemA) - workspaceElement.moveActiveItemToPaneOnRight({keepOriginal: true}) + workspaceElement.moveActiveItemToPaneOnRight({ keepOriginal: true }) expect(workspace.paneForItem(itemA)).toBe(startingPane) expect(paneOnRight.getActiveItem()).toBe(itemB) - }) - ) + })) }) describe('::moveActiveItemToNearestPaneInDirection(direction, params)', () => { @@ -499,10 +549,14 @@ describe('WorkspaceElement', () => { workspace.getBottomDock().show() startingPane.activate() startingPane.activateItem(item) - workspaceElement.moveActiveItemToNearestPaneInDirection('below', {keepOriginal: false}) + workspaceElement.moveActiveItemToNearestPaneInDirection('below', { + keepOriginal: false + }) expect(workspace.paneForItem(item)).toBe(startingPane) - workspaceElement.moveActiveItemToNearestPaneInDirection('below', {keepOriginal: true}) + workspaceElement.moveActiveItemToNearestPaneInDirection('below', { + keepOriginal: true + }) expect(workspace.paneForItem(item)).toBe(startingPane) }) }) @@ -516,7 +570,9 @@ describe('WorkspaceElement', () => { startingPane.activate() startingPane.activateItem(item) workspaceElement.focusPaneViewAbove() - workspaceElement.moveActiveItemToNearestPaneInDirection('below', {keepOriginal: true}) + workspaceElement.moveActiveItemToNearestPaneInDirection('below', { + keepOriginal: true + }) expect(workspace.paneForItem(item)).toBe(startingPane) expect(paneBelow.getItems().length).toEqual(0) }) @@ -538,18 +594,30 @@ describe('WorkspaceElement', () => { await Promise.all([ atom.workspace.open({ element: document.createElement('div'), - getDefaultLocation() { return 'left' }, - getPreferredWidth() { return 150 } + getDefaultLocation () { + return 'left' + }, + getPreferredWidth () { + return 150 + } }), atom.workspace.open({ element: document.createElement('div'), - getDefaultLocation() { return 'right' }, - getPreferredWidth() { return 150 } + getDefaultLocation () { + return 'right' + }, + getPreferredWidth () { + return 150 + } }), atom.workspace.open({ element: document.createElement('div'), - getDefaultLocation() { return 'bottom' }, - getPreferredHeight() { return 100 } + getDefaultLocation () { + return 'bottom' + }, + getPreferredHeight () { + return 100 + } }) ]) @@ -567,21 +635,21 @@ describe('WorkspaceElement', () => { // --- Right Dock --- // Mouse over where the toggle button would be if the dock were hovered - moveMouse({clientX: 440, clientY: 150}) + moveMouse({ clientX: 440, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the dock - moveMouse({clientX: 460, clientY: 150}) + moveMouse({ clientX: 460, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonVisible(rightDock, 'icon-chevron-right') expectToggleButtonHidden(bottomDock) // Mouse over the toggle button - moveMouse({clientX: 440, clientY: 150}) + moveMouse({ clientX: 440, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonVisible(rightDock, 'icon-chevron-right') @@ -594,10 +662,10 @@ describe('WorkspaceElement', () => { expectToggleButtonHidden(rightDock) // Mouse to edge of the window - moveMouse({clientX: 575, clientY: 150}) + moveMouse({ clientX: 575, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonHidden(rightDock) - moveMouse({clientX: 598, clientY: 150}) + moveMouse({ clientX: 598, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonVisible(rightDock, 'icon-chevron-left') @@ -610,21 +678,21 @@ describe('WorkspaceElement', () => { // --- Left Dock --- // Mouse over where the toggle button would be if the dock were hovered - moveMouse({clientX: 160, clientY: 150}) + moveMouse({ clientX: 160, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the dock - moveMouse({clientX: 140, clientY: 150}) + moveMouse({ clientX: 140, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonVisible(leftDock, 'icon-chevron-left') expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the toggle button - moveMouse({clientX: 160, clientY: 150}) + moveMouse({ clientX: 160, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonVisible(leftDock, 'icon-chevron-left') expectToggleButtonHidden(rightDock) @@ -637,10 +705,10 @@ describe('WorkspaceElement', () => { expectToggleButtonHidden(leftDock) // Mouse to edge of the window - moveMouse({clientX: 25, clientY: 150}) + moveMouse({ clientX: 25, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) - moveMouse({clientX: 2, clientY: 150}) + moveMouse({ clientX: 2, clientY: 150 }) await getNextUpdatePromise() expectToggleButtonVisible(leftDock, 'icon-chevron-right') @@ -653,21 +721,21 @@ describe('WorkspaceElement', () => { // --- Bottom Dock --- // Mouse over where the toggle button would be if the dock were hovered - moveMouse({clientX: 300, clientY: 190}) + moveMouse({ clientX: 300, clientY: 190 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the dock - moveMouse({clientX: 300, clientY: 210}) + moveMouse({ clientX: 300, clientY: 210 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonVisible(bottomDock, 'icon-chevron-down') // Mouse over the toggle button - moveMouse({clientX: 300, clientY: 195}) + moveMouse({ clientX: 300, clientY: 195 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) @@ -680,10 +748,10 @@ describe('WorkspaceElement', () => { expectToggleButtonHidden(bottomDock) // Mouse to edge of the window - moveMouse({clientX: 300, clientY: 290}) + moveMouse({ clientX: 300, clientY: 290 }) await getNextUpdatePromise() expectToggleButtonHidden(leftDock) - moveMouse({clientX: 300, clientY: 299}) + moveMouse({ clientX: 300, clientY: 299 }) await getNextUpdatePromise() expectToggleButtonVisible(bottomDock, 'icon-chevron-up') @@ -699,12 +767,16 @@ describe('WorkspaceElement', () => { advanceClock(100) } - function expectToggleButtonHidden(dock) { - expect(dock.refs.toggleButton.element).not.toHaveClass('atom-dock-toggle-button-visible') + function expectToggleButtonHidden (dock) { + expect(dock.refs.toggleButton.element).not.toHaveClass( + 'atom-dock-toggle-button-visible' + ) } - function expectToggleButtonVisible(dock, iconClass) { - expect(dock.refs.toggleButton.element).toHaveClass('atom-dock-toggle-button-visible') + function expectToggleButtonVisible (dock, iconClass) { + expect(dock.refs.toggleButton.element).toHaveClass( + 'atom-dock-toggle-button-visible' + ) expect(dock.refs.toggleButton.refs.iconElement).toHaveClass(iconClass) } }) @@ -713,10 +785,12 @@ describe('WorkspaceElement', () => { it('has a class based on the style of the scrollbar', () => { let observeCallback const scrollbarStyle = require('scrollbar-style') - spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake(cb => { - observeCallback = cb - return new Disposable(() => {}) - }) + spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake( + cb => { + observeCallback = cb + return new Disposable(() => {}) + } + ) const workspaceElement = atom.workspace.getElement() observeCallback('legacy') @@ -741,11 +815,15 @@ describe('WorkspaceElement', () => { it("updates the font-size based on the 'editor.fontSize' config value", async () => { const initialCharWidth = editor.getDefaultCharWidth() - expect(getComputedStyle(editorElement).fontSize).toBe(atom.config.get('editor.fontSize') + 'px') + expect(getComputedStyle(editorElement).fontSize).toBe( + atom.config.get('editor.fontSize') + 'px' + ) atom.config.set('editor.fontSize', atom.config.get('editor.fontSize') + 5) await editorElement.component.getNextUpdatePromise() - expect(getComputedStyle(editorElement).fontSize).toBe(atom.config.get('editor.fontSize') + 'px') + expect(getComputedStyle(editorElement).fontSize).toBe( + atom.config.get('editor.fontSize') + 'px' + ) expect(editor.getDefaultCharWidth()).toBeGreaterThan(initialCharWidth) }) @@ -765,7 +843,9 @@ describe('WorkspaceElement', () => { const initialLineHeight = editor.getLineHeightInPixels() atom.config.set('editor.lineHeight', '30px') await editorElement.component.getNextUpdatePromise() - expect(getComputedStyle(editorElement).lineHeight).toBe(atom.config.get('editor.lineHeight')) + expect(getComputedStyle(editorElement).lineHeight).toBe( + atom.config.get('editor.lineHeight') + ) expect(editor.getLineHeightInPixels()).not.toBe(initialLineHeight) }) @@ -774,37 +854,47 @@ describe('WorkspaceElement', () => { atom.config.set('editor.fontSize', 12) // Zoom out - editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', { - wheelDeltaY: -10, - ctrlKey: true - })) + editorElement.querySelector('span').dispatchEvent( + new WheelEvent('mousewheel', { + wheelDeltaY: -10, + ctrlKey: true + }) + ) expect(atom.config.get('editor.fontSize')).toBe(11) // Zoom in - editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', { - wheelDeltaY: 10, - ctrlKey: true - })) + editorElement.querySelector('span').dispatchEvent( + new WheelEvent('mousewheel', { + wheelDeltaY: 10, + ctrlKey: true + }) + ) expect(atom.config.get('editor.fontSize')).toBe(12) // Not on an atom-text-editor - workspaceElement.dispatchEvent(new WheelEvent('mousewheel', { - wheelDeltaY: 10, - ctrlKey: true - })) + workspaceElement.dispatchEvent( + new WheelEvent('mousewheel', { + wheelDeltaY: 10, + ctrlKey: true + }) + ) expect(atom.config.get('editor.fontSize')).toBe(12) // No ctrl key - editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', { - wheelDeltaY: 10 - })) + editorElement.querySelector('span').dispatchEvent( + new WheelEvent('mousewheel', { + wheelDeltaY: 10 + }) + ) expect(atom.config.get('editor.fontSize')).toBe(12) atom.config.set('editor.zoomFontWhenCtrlScrolling', false) - editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', { - wheelDeltaY: 10, - ctrlKey: true - })) + editorElement.querySelector('span').dispatchEvent( + new WheelEvent('mousewheel', { + wheelDeltaY: 10, + ctrlKey: true + }) + ) expect(atom.config.get('editor.fontSize')).toBe(12) }) }) @@ -813,22 +903,40 @@ describe('WorkspaceElement', () => { it('inserts panel container elements in the correct places in the DOM', () => { const workspaceElement = atom.workspace.getElement() - const leftContainer = workspaceElement.querySelector('atom-panel-container.left') - const rightContainer = workspaceElement.querySelector('atom-panel-container.right') + const leftContainer = workspaceElement.querySelector( + 'atom-panel-container.left' + ) + const rightContainer = workspaceElement.querySelector( + 'atom-panel-container.right' + ) expect(leftContainer.nextSibling).toBe(workspaceElement.verticalAxis) expect(rightContainer.previousSibling).toBe(workspaceElement.verticalAxis) - const topContainer = workspaceElement.querySelector('atom-panel-container.top') - const bottomContainer = workspaceElement.querySelector('atom-panel-container.bottom') + const topContainer = workspaceElement.querySelector( + 'atom-panel-container.top' + ) + const bottomContainer = workspaceElement.querySelector( + 'atom-panel-container.bottom' + ) expect(topContainer.nextSibling).toBe(workspaceElement.paneContainer) - expect(bottomContainer.previousSibling).toBe(workspaceElement.paneContainer) + expect(bottomContainer.previousSibling).toBe( + workspaceElement.paneContainer + ) - const headerContainer = workspaceElement.querySelector('atom-panel-container.header') - const footerContainer = workspaceElement.querySelector('atom-panel-container.footer') + const headerContainer = workspaceElement.querySelector( + 'atom-panel-container.header' + ) + const footerContainer = workspaceElement.querySelector( + 'atom-panel-container.footer' + ) expect(headerContainer.nextSibling).toBe(workspaceElement.horizontalAxis) - expect(footerContainer.previousSibling).toBe(workspaceElement.horizontalAxis) + expect(footerContainer.previousSibling).toBe( + workspaceElement.horizontalAxis + ) - const modalContainer = workspaceElement.querySelector('atom-panel-container.modal') + const modalContainer = workspaceElement.querySelector( + 'atom-panel-container.modal' + ) expect(modalContainer.parentNode).toBe(workspaceElement) }) @@ -838,18 +946,20 @@ describe('WorkspaceElement', () => { expect(workspaceElement.offsetWidth).toBeGreaterThan(0) const headerItem = document.createElement('div') - atom.workspace.addHeaderPanel({item: headerItem}) + atom.workspace.addHeaderPanel({ item: headerItem }) expect(headerItem.offsetWidth).toEqual(workspaceElement.offsetWidth) const footerItem = document.createElement('div') - atom.workspace.addFooterPanel({item: footerItem}) + atom.workspace.addFooterPanel({ item: footerItem }) expect(footerItem.offsetWidth).toEqual(workspaceElement.offsetWidth) }) it('shrinks horizontal axis according to header/footer panels height', () => { const workspaceElement = atom.workspace.getElement() workspaceElement.style.height = '100px' - const horizontalAxisElement = workspaceElement.querySelector('atom-workspace-axis.horizontal') + const horizontalAxisElement = workspaceElement.querySelector( + 'atom-workspace-axis.horizontal' + ) jasmine.attachToDOM(workspaceElement) const originalHorizontalAxisHeight = horizontalAxisElement.offsetHeight @@ -858,15 +968,19 @@ describe('WorkspaceElement', () => { const headerItem = document.createElement('div') headerItem.style.height = '10px' - atom.workspace.addHeaderPanel({item: headerItem}) + atom.workspace.addHeaderPanel({ item: headerItem }) expect(headerItem.offsetHeight).toBeGreaterThan(0) const footerItem = document.createElement('div') footerItem.style.height = '15px' - atom.workspace.addFooterPanel({item: footerItem}) + atom.workspace.addFooterPanel({ item: footerItem }) expect(footerItem.offsetHeight).toBeGreaterThan(0) - expect(horizontalAxisElement.offsetHeight).toEqual(originalHorizontalAxisHeight - headerItem.offsetHeight - footerItem.offsetHeight) + expect(horizontalAxisElement.offsetHeight).toEqual( + originalHorizontalAxisHeight - + headerItem.offsetHeight - + footerItem.offsetHeight + ) }) }) @@ -895,39 +1009,60 @@ describe('WorkspaceElement', () => { // No active item. Use first project directory. atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {}) + expect(ipcRenderer.send).toHaveBeenCalledWith( + 'run-package-specs', + path.join(projectPaths[0], 'spec'), + {} + ) ipcRenderer.send.reset() // Active item doesn't implement ::getPath(). Use first project directory. const item = document.createElement('div') atom.workspace.getActivePane().activateItem(item) atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {}) + expect(ipcRenderer.send).toHaveBeenCalledWith( + 'run-package-specs', + path.join(projectPaths[0], 'spec'), + {} + ) ipcRenderer.send.reset() // Active item has no path. Use first project directory. item.getPath = () => null atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {}) + expect(ipcRenderer.send).toHaveBeenCalledWith( + 'run-package-specs', + path.join(projectPaths[0], 'spec'), + {} + ) ipcRenderer.send.reset() // Active item has path. Use project path for item path. item.getPath = () => path.join(projectPaths[1], 'a-file.txt') atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[1], 'spec'), {}) + expect(ipcRenderer.send).toHaveBeenCalledWith( + 'run-package-specs', + path.join(projectPaths[1], 'spec'), + {} + ) ipcRenderer.send.reset() }) - it("passes additional options to the spec window", () => { + it('passes additional options to the spec window', () => { const workspaceElement = atom.workspace.getElement() spyOn(ipcRenderer, 'send') const projectPath = temp.mkdirSync('dir1-') atom.project.setPaths([projectPath]) - workspaceElement.runPackageSpecs({env: {ATOM_GITHUB_BABEL_ENV: 'coverage'}}) + workspaceElement.runPackageSpecs({ + env: { ATOM_GITHUB_BABEL_ENV: 'coverage' } + }) expect(ipcRenderer.send).toHaveBeenCalledWith( - 'run-package-specs', path.join(projectPath, 'spec'), {env: {ATOM_GITHUB_BABEL_ENV: 'coverage'}}) + 'run-package-specs', + path.join(projectPath, 'spec'), + { env: { ATOM_GITHUB_BABEL_ENV: 'coverage' } } + ) }) }) }) diff --git a/spec/workspace-spec.js b/spec/workspace-spec.js index 7bfbe5408..50dd57682 100644 --- a/spec/workspace-spec.js +++ b/spec/workspace-spec.js @@ -10,7 +10,7 @@ const _ = require('underscore-plus') const fstream = require('fstream') const fs = require('fs-plus') const AtomEnvironment = require('../src/atom-environment') -const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers') +const { conditionPromise } = require('./async-spec-helpers') describe('Workspace', () => { let workspace @@ -20,7 +20,10 @@ describe('Workspace', () => { workspace = atom.workspace workspace.resetFontSize() spyOn(atom.applicationDelegate, 'confirm') - setDocumentEdited = spyOn(atom.applicationDelegate, 'setWindowDocumentEdited') + setDocumentEdited = spyOn( + atom.applicationDelegate, + 'setWindowDocumentEdited' + ) atom.project.setPaths([atom.project.getDirectories()[0].resolve('dir')]) waits(1) @@ -35,10 +38,10 @@ describe('Workspace', () => { } }) - function simulateReload() { + function simulateReload () { waitsForPromise(() => { const workspaceState = workspace.serialize() - const projectState = atom.project.serialize({isUnloading: true}) + const projectState = atom.project.serialize({ isUnloading: true }) workspace.destroy() atom.project.destroy() atom.project = new Project({ @@ -71,18 +74,26 @@ describe('Workspace', () => { describe('when the workspace contains text editors', () => { it('constructs the view with the same panes', () => { const pane1 = atom.workspace.getActivePane() - const pane2 = pane1.splitRight({copyActiveItem: true}) - const pane3 = pane2.splitRight({copyActiveItem: true}) + const pane2 = pane1.splitRight({ copyActiveItem: true }) + const pane3 = pane2.splitRight({ copyActiveItem: true }) let pane4 = null - waitsForPromise(() => atom.workspace.open(null).then(editor => editor.setText('An untitled editor.'))) - waitsForPromise(() => - atom.workspace.open('b').then(editor => pane2.activateItem(editor.copy())) + atom.workspace + .open(null) + .then(editor => editor.setText('An untitled editor.')) ) waitsForPromise(() => - atom.workspace.open('../sample.js').then(editor => pane3.activateItem(editor)) + atom.workspace + .open('b') + .then(editor => pane2.activateItem(editor.copy())) + ) + + waitsForPromise(() => + atom.workspace + .open('../sample.js') + .then(editor => pane3.activateItem(editor)) ) runs(() => { @@ -91,7 +102,9 @@ describe('Workspace', () => { }) waitsForPromise(() => - atom.workspace.open('../sample.txt').then(editor => pane4.activateItem(editor)) + atom.workspace + .open('../sample.txt') + .then(editor => pane4.activateItem(editor)) ) runs(() => { @@ -103,11 +116,19 @@ describe('Workspace', () => { runs(() => { expect(atom.workspace.getTextEditors().length).toBe(5) - const [editor1, editor2, untitledEditor, editor3, editor4] = atom.workspace.getTextEditors() + const [ + editor1, + editor2, + untitledEditor, + editor3, + editor4 + ] = atom.workspace.getTextEditors() const firstDirectory = atom.project.getDirectories()[0] expect(firstDirectory).toBeDefined() expect(editor1.getPath()).toBe(firstDirectory.resolve('b')) - expect(editor2.getPath()).toBe(firstDirectory.resolve('../sample.txt')) + expect(editor2.getPath()).toBe( + firstDirectory.resolve('../sample.txt') + ) expect(editor2.getCursorScreenPosition()).toEqual([0, 2]) expect(editor3.getPath()).toBe(firstDirectory.resolve('b')) expect(editor4.getPath()).toBe(firstDirectory.resolve('../sample.js')) @@ -115,9 +136,17 @@ describe('Workspace', () => { expect(untitledEditor.getPath()).toBeUndefined() expect(untitledEditor.getText()).toBe('An untitled editor.') - expect(atom.workspace.getActiveTextEditor().getPath()).toBe(editor3.getPath()) - const pathEscaped = fs.tildify(escapeStringRegex(atom.project.getPaths()[0])) - expect(document.title).toMatch(new RegExp(`^${path.basename(editor3.getLongTitle())} \\u2014 ${pathEscaped}`)) + expect(atom.workspace.getActiveTextEditor().getPath()).toBe( + editor3.getPath() + ) + const pathEscaped = fs.tildify( + escapeStringRegex(atom.project.getPaths()[0]) + ) + expect(document.title).toMatch( + new RegExp( + `^${path.basename(editor3.getLongTitle())} \\u2014 ${pathEscaped}` + ) + ) }) }) }) @@ -150,25 +179,47 @@ describe('Workspace', () => { let editor1 let editor2 - waitsForPromise(() => workspace.open().then(editor => { editor1 = editor })) + waitsForPromise(() => + workspace.open().then(editor => { + editor1 = editor + }) + ) runs(() => { expect(editor1.getPath()).toBeUndefined() expect(workspace.getActivePane().items).toEqual([editor1]) expect(workspace.getActivePaneItem()).toBe(editor1) expect(workspace.getActivePane().activate).toHaveBeenCalled() - expect(openEvents).toEqual([{uri: undefined, pane: workspace.getActivePane(), item: editor1, index: 0}]) + expect(openEvents).toEqual([ + { + uri: undefined, + pane: workspace.getActivePane(), + item: editor1, + index: 0 + } + ]) openEvents = [] }) - waitsForPromise(() => workspace.open().then(editor => { editor2 = editor })) + waitsForPromise(() => + workspace.open().then(editor => { + editor2 = editor + }) + ) runs(() => { expect(editor2.getPath()).toBeUndefined() expect(workspace.getActivePane().items).toEqual([editor1, editor2]) expect(workspace.getActivePaneItem()).toBe(editor2) expect(workspace.getActivePane().activate).toHaveBeenCalled() - expect(openEvents).toEqual([{uri: undefined, pane: workspace.getActivePane(), item: editor2, index: 1}]) + expect(openEvents).toEqual([ + { + uri: undefined, + pane: workspace.getActivePane(), + item: editor2, + index: 1 + } + ]) }) }) }) @@ -185,7 +236,9 @@ describe('Workspace', () => { editor1 = o return workspace.open('b').then(o => { editor2 = o - return workspace.open('a').then(o => { editor = o }) + return workspace.open('a').then(o => { + editor = o + }) }) }) ) @@ -229,7 +282,9 @@ describe('Workspace', () => { } dock.getActivePane().addItem(item) expect(dock.getPaneItems()).toHaveLength(1) - waitsForPromise(() => atom.workspace.open(ITEM_URI, {searchAllPanes: true})) + waitsForPromise(() => + atom.workspace.open(ITEM_URI, { searchAllPanes: true }) + ) runs(() => { expect(atom.workspace.getPaneItems()).toHaveLength(1) expect(dock.getPaneItems()).toHaveLength(1) @@ -242,7 +297,11 @@ describe('Workspace', () => { it('adds the item to the workspace', () => { let editor waitsForPromise(() => workspace.open('a')) - waitsForPromise(() => workspace.open('b', {activateItem: false}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('b', { activateItem: false }).then(o => { + editor = o + }) + ) runs(() => { expect(workspace.getPaneItems()).toContain(editor) expect(workspace.getActivePaneItem()).not.toBe(editor) @@ -262,7 +321,11 @@ describe('Workspace', () => { it('adds and activates a new editor for the given path on the active pane', () => { let editor = null - waitsForPromise(() => workspace.open('a').then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a').then(o => { + editor = o + }) + ) runs(() => { const firstDirectory = atom.project.getDirectories()[0] @@ -278,10 +341,16 @@ describe('Workspace', () => { let editor0 = null let editor1 = null - waitsForPromise(() => Promise.all([ - workspace.open('spartacus.txt').then(o0 => { editor0 = o0 }), - workspace.open('spartacus.txt').then(o1 => { editor1 = o1 }), - ])) + waitsForPromise(() => + Promise.all([ + workspace.open('spartacus.txt').then(o0 => { + editor0 = o0 + }), + workspace.open('spartacus.txt').then(o1 => { + editor1 = o1 + }) + ]) + ) runs(() => { expect(editor0).toEqual(editor1) @@ -296,7 +365,9 @@ describe('Workspace', () => { } const opener = jasmine.createSpy().andReturn(item) const dock = atom.workspace.getRightDock() - spyOn(atom.workspace.itemLocationStore, 'load').andReturn(Promise.resolve()) + spyOn(atom.workspace.itemLocationStore, 'load').andReturn( + Promise.resolve() + ) spyOn(atom.workspace, 'getOpeners').andReturn([opener]) expect(dock.getPaneItems()).toHaveLength(0) waitsForPromise(() => atom.workspace.open('a')) @@ -314,10 +385,12 @@ describe('Workspace', () => { getDefaultLocation: () => 'left', getElement: () => document.createElement('div') } - const opener = uri => uri === ITEM_URI ? item : null + const opener = uri => (uri === ITEM_URI ? item : null) const dock = atom.workspace.getRightDock() spyOn(atom.workspace.itemLocationStore, 'load').andCallFake(uri => - uri === 'atom://test' ? Promise.resolve('right') : Promise.resolve() + uri === 'atom://test' + ? Promise.resolve('right') + : Promise.resolve() ) spyOn(atom.workspace, 'getOpeners').andReturn([opener]) expect(dock.getPaneItems()).toHaveLength(0) @@ -331,18 +404,26 @@ describe('Workspace', () => { }) describe('when an item with the given uri exists in an inactive pane container', () => { - it('activates that item if it is in that container\'s active pane', async () => { + it("activates that item if it is in that container's active pane", async () => { const item = await atom.workspace.open('a') atom.workspace.getLeftDock().activate() - expect(await atom.workspace.open('a', {searchAllPanes: false})).toBe(item) - expect(atom.workspace.getActivePaneContainer().getLocation()).toBe('center') + expect( + await atom.workspace.open('a', { searchAllPanes: false }) + ).toBe(item) + expect(atom.workspace.getActivePaneContainer().getLocation()).toBe( + 'center' + ) expect(atom.workspace.getPaneItems()).toEqual([item]) atom.workspace.getActivePane().splitRight() atom.workspace.getLeftDock().activate() - const item2 = await atom.workspace.open('a', {searchAllPanes: false}) + const item2 = await atom.workspace.open('a', { + searchAllPanes: false + }) expect(item2).not.toBe(item) - expect(atom.workspace.getActivePaneContainer().getLocation()).toBe('center') + expect(atom.workspace.getActivePaneContainer().getLocation()).toBe( + 'center' + ) expect(atom.workspace.getPaneItems()).toEqual([item, item2]) }) }) @@ -358,17 +439,21 @@ describe('Workspace', () => { waitsForPromise(() => { pane1.activate() - return workspace.open('a').then(o => { editor1 = o }) + return workspace.open('a').then(o => { + editor1 = o + }) }) waitsForPromise(() => { pane2.activate() - return workspace.open('b').then(o => { editor2 = o }) + return workspace.open('b').then(o => { + editor2 = o + }) }) runs(() => expect(workspace.getActivePaneItem()).toBe(editor2)) - waitsForPromise(() => workspace.open('a', {searchAllPanes: true})) + waitsForPromise(() => workspace.open('a', { searchAllPanes: true })) runs(() => { expect(workspace.getActivePane()).toBe(pane1) @@ -383,9 +468,17 @@ describe('Workspace', () => { const pane1 = workspace.getActivePane().splitRight() pane0.activate() - const promise0 = workspace.open('spartacus.txt', {searchAllPanes: true}).then(o0 => { editor0 = o0 }) + const promise0 = workspace + .open('spartacus.txt', { searchAllPanes: true }) + .then(o0 => { + editor0 = o0 + }) pane1.activate() - const promise1 = workspace.open('spartacus.txt', {searchAllPanes: true}).then(o1 => { editor1 = o1 }) + const promise1 = workspace + .open('spartacus.txt', { searchAllPanes: true }) + .then(o1 => { + editor1 = o1 + }) waitsForPromise(() => Promise.all([promise0, promise1])) @@ -408,7 +501,9 @@ describe('Workspace', () => { } dock.getActivePane().addItem(item) spyOn(dock.paneForItem(item), 'activate') - waitsForPromise(() => atom.workspace.open(ITEM_URI, {searchAllPanes: true})) + waitsForPromise(() => + atom.workspace.open(ITEM_URI, { searchAllPanes: true }) + ) runs(() => expect(dock.paneForItem(item).activate).toHaveBeenCalled()) }) }) @@ -416,7 +511,11 @@ describe('Workspace', () => { describe('when no editor for the given uri is open in any pane', () => { it('opens an editor for the given uri in the active pane', () => { let editor = null - waitsForPromise(() => workspace.open('a', {searchAllPanes: true}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { searchAllPanes: true }).then(o => { + editor = o + }) + ) runs(() => expect(workspace.getActivePaneItem()).toBe(editor)) }) @@ -425,8 +524,13 @@ describe('Workspace', () => { describe('when attempting to open an editor in a dock', () => { it('opens the editor in the workspace center', async () => { - await atom.workspace.open('sample.txt', {location: 'right'}) - expect(atom.workspace.getCenter().getActivePaneItem().getFileName()).toEqual('sample.txt') + await atom.workspace.open('sample.txt', { location: 'right' }) + expect( + atom.workspace + .getCenter() + .getActivePaneItem() + .getFileName() + ).toEqual('sample.txt') }) }) @@ -457,7 +561,7 @@ describe('Workspace', () => { const item = document.createElement('div') await atom.workspace.open(item) - await atom.workspace.open(null, {split: 'right'}) + await atom.workspace.open(null, { split: 'right' }) expect(atom.workspace.getActivePaneItem()).not.toBe(item) expect(atom.workspace.getActivePane().getItems().length).toBe(1) @@ -468,7 +572,9 @@ describe('Workspace', () => { rejection = error } - expect(rejection.message).toMatch(/The workspace can only contain one instance of item/) + expect(rejection.message).toMatch( + /The workspace can only contain one instance of item/ + ) }) }) }) @@ -481,7 +587,11 @@ describe('Workspace', () => { expect(workspace.getActivePane()).toBe(pane2) let editor = null - waitsForPromise(() => workspace.open('a', {split: 'left'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'left' }).then(o => { + editor = o + }) + ) runs(() => { expect(workspace.getActivePane()).toBe(pane1) @@ -492,7 +602,9 @@ describe('Workspace', () => { // Focus right pane and reopen the file on the left waitsForPromise(() => { pane2.focus() - return workspace.open('a', {split: 'left'}).then(o => { editor = o }) + return workspace.open('a', { split: 'left' }).then(o => { + editor = o + }) }) runs(() => { @@ -512,7 +624,11 @@ describe('Workspace', () => { pane1.activate() expect(workspace.getActivePane()).toBe(pane1) - waitsForPromise(() => workspace.open('a', {split: 'left'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'left' }).then(o => { + editor = o + }) + ) runs(() => { expect(workspace.getActivePane()).toBe(pane1) @@ -526,7 +642,11 @@ describe('Workspace', () => { let editor = null const pane1 = workspace.getActivePane() let pane2 = null - waitsForPromise(() => workspace.open('a', {split: 'right'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'right' }).then(o => { + editor = o + }) + ) runs(() => { pane2 = workspace.getPanes().filter(p => p !== pane1)[0] @@ -538,7 +658,9 @@ describe('Workspace', () => { // Focus right pane and reopen the file on the right waitsForPromise(() => { pane1.focus() - return workspace.open('a', {split: 'right'}).then(o => { editor = o }) + return workspace.open('a', { split: 'right' }).then(o => { + editor = o + }) }) runs(() => { @@ -558,14 +680,22 @@ describe('Workspace', () => { expect(workspace.getActivePane()).toBe(pane1) let pane4 = null - waitsForPromise(() => workspace.open('a', {split: 'right'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'right' }).then(o => { + editor = o + }) + ) runs(() => { pane4 = workspace.getPanes().filter(p => p !== pane1)[0] expect(workspace.getActivePane()).toBe(pane4) expect(pane4.items).toEqual([editor]) - expect(workspace.getCenter().paneContainer.root.children[0]).toBe(pane1) - expect(workspace.getCenter().paneContainer.root.children[1]).toBe(pane4) + expect(workspace.getCenter().paneContainer.root.children[0]).toBe( + pane1 + ) + expect(workspace.getCenter().paneContainer.root.children[1]).toBe( + pane4 + ) }) }) }) @@ -578,7 +708,11 @@ describe('Workspace', () => { expect(workspace.getActivePane()).toBe(pane2) let editor = null - waitsForPromise(() => workspace.open('a', {split: 'up'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'up' }).then(o => { + editor = o + }) + ) runs(() => { expect(workspace.getActivePane()).toBe(pane1) @@ -589,7 +723,9 @@ describe('Workspace', () => { // Focus bottom pane and reopen the file on the top waitsForPromise(() => { pane2.focus() - return workspace.open('a', {split: 'up'}).then(o => { editor = o }) + return workspace.open('a', { split: 'up' }).then(o => { + editor = o + }) }) runs(() => { @@ -609,7 +745,11 @@ describe('Workspace', () => { pane1.activate() expect(workspace.getActivePane()).toBe(pane1) - waitsForPromise(() => workspace.open('a', {split: 'up'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'up' }).then(o => { + editor = o + }) + ) runs(() => { expect(workspace.getActivePane()).toBe(pane1) @@ -623,7 +763,11 @@ describe('Workspace', () => { let editor = null const pane1 = workspace.getActivePane() let pane2 = null - waitsForPromise(() => workspace.open('a', {split: 'down'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'down' }).then(o => { + editor = o + }) + ) runs(() => { pane2 = workspace.getPanes().filter(p => p !== pane1)[0] @@ -635,7 +779,9 @@ describe('Workspace', () => { // Focus bottom pane and reopen the file on the right waitsForPromise(() => { pane1.focus() - return workspace.open('a', {split: 'down'}).then(o => { editor = o }) + return workspace.open('a', { split: 'down' }).then(o => { + editor = o + }) }) runs(() => { @@ -654,14 +800,22 @@ describe('Workspace', () => { expect(workspace.getActivePane()).toBe(pane1) let pane4 = null - waitsForPromise(() => workspace.open('a', {split: 'down'}).then(o => { editor = o })) + waitsForPromise(() => + workspace.open('a', { split: 'down' }).then(o => { + editor = o + }) + ) runs(() => { pane4 = workspace.getPanes().filter(p => p !== pane1)[0] expect(workspace.getActivePane()).toBe(pane4) expect(pane4.items).toEqual([editor]) - expect(workspace.getCenter().paneContainer.root.children[0]).toBe(pane1) - expect(workspace.getCenter().paneContainer.root.children[1]).toBe(pane2) + expect(workspace.getCenter().paneContainer.root.children[0]).toBe( + pane1 + ) + expect(workspace.getCenter().paneContainer.root.children[1]).toBe( + pane2 + ) }) }) }) @@ -670,29 +824,68 @@ describe('Workspace', () => { describe('when an initialLine and initialColumn are specified', () => { it('moves the cursor to the indicated location', () => { - waitsForPromise(() => workspace.open('a', {initialLine: 1, initialColumn: 5})) + waitsForPromise(() => + workspace.open('a', { initialLine: 1, initialColumn: 5 }) + ) - runs(() => expect(workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([1, 5])) + runs(() => + expect( + workspace.getActiveTextEditor().getCursorBufferPosition() + ).toEqual([1, 5]) + ) - waitsForPromise(() => workspace.open('a', {initialLine: 2, initialColumn: 4})) + waitsForPromise(() => + workspace.open('a', { initialLine: 2, initialColumn: 4 }) + ) - runs(() => expect(workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([2, 4])) + runs(() => + expect( + workspace.getActiveTextEditor().getCursorBufferPosition() + ).toEqual([2, 4]) + ) - waitsForPromise(() => workspace.open('a', {initialLine: 0, initialColumn: 0})) + waitsForPromise(() => + workspace.open('a', { initialLine: 0, initialColumn: 0 }) + ) - runs(() => expect(workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 0])) + runs(() => + expect( + workspace.getActiveTextEditor().getCursorBufferPosition() + ).toEqual([0, 0]) + ) - waitsForPromise(() => workspace.open('a', {initialLine: NaN, initialColumn: 4})) + waitsForPromise(() => + workspace.open('a', { initialLine: NaN, initialColumn: 4 }) + ) - runs(() => expect(workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([0, 4])) + runs(() => + expect( + workspace.getActiveTextEditor().getCursorBufferPosition() + ).toEqual([0, 4]) + ) - waitsForPromise(() => workspace.open('a', {initialLine: 2, initialColumn: NaN})) + waitsForPromise(() => + workspace.open('a', { initialLine: 2, initialColumn: NaN }) + ) - runs(() => expect(workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([2, 0])) + runs(() => + expect( + workspace.getActiveTextEditor().getCursorBufferPosition() + ).toEqual([2, 0]) + ) - waitsForPromise(() => workspace.open('a', {initialLine: Infinity, initialColumn: Infinity})) + waitsForPromise(() => + workspace.open('a', { + initialLine: Infinity, + initialColumn: Infinity + }) + ) - runs(() => expect(workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual([2, 11])) + runs(() => + expect( + workspace.getActiveTextEditor().getCursorBufferPosition() + ).toEqual([2, 11]) + ) }) }) @@ -701,7 +894,9 @@ describe('Workspace', () => { spyOn(fs, 'getSizeSync').andReturn(size * 1048577) let selectedButtonIndex = 1 // cancel - atom.applicationDelegate.confirm.andCallFake((options, callback) => callback(selectedButtonIndex)) + atom.applicationDelegate.confirm.andCallFake((options, callback) => + callback(selectedButtonIndex) + ) let editor = await workspace.open('sample.js') if (shouldPrompt) { @@ -739,12 +934,12 @@ describe('Workspace', () => { it('returns the resource returned by the custom opener', () => { const fooOpener = (pathToOpen, options) => { if (pathToOpen != null ? pathToOpen.match(/\.foo/) : undefined) { - return {foo: pathToOpen, options} + return { foo: pathToOpen, options } } } - const barOpener = (pathToOpen) => { + const barOpener = pathToOpen => { if (pathToOpen != null ? pathToOpen.match(/^bar:\/\//) : undefined) { - return {bar: pathToOpen} + return { bar: pathToOpen } } } workspace.addOpener(fooOpener) @@ -752,29 +947,51 @@ describe('Workspace', () => { waitsForPromise(() => { const pathToOpen = atom.project.getDirectories()[0].resolve('a.foo') - return workspace.open(pathToOpen, {hey: 'there'}).then(item => expect(item).toEqual({foo: pathToOpen, options: {hey: 'there'}})) + return workspace.open(pathToOpen, { hey: 'there' }).then(item => + expect(item).toEqual({ + foo: pathToOpen, + options: { hey: 'there' } + }) + ) }) waitsForPromise(() => - workspace.open('bar://baz').then(item => expect(item).toEqual({bar: 'bar://baz'}))) + workspace + .open('bar://baz') + .then(item => expect(item).toEqual({ bar: 'bar://baz' })) + ) }) }) it("adds the file to the application's recent documents list", () => { - if (process.platform !== 'darwin') { return } // Feature only supported on macOS + if (process.platform !== 'darwin') { + return + } // Feature only supported on macOS spyOn(atom.applicationDelegate, 'addRecentDocument') waitsForPromise(() => workspace.open()) - runs(() => expect(atom.applicationDelegate.addRecentDocument).not.toHaveBeenCalled()) + runs(() => + expect( + atom.applicationDelegate.addRecentDocument + ).not.toHaveBeenCalled() + ) waitsForPromise(() => workspace.open('something://a/url')) - runs(() => expect(atom.applicationDelegate.addRecentDocument).not.toHaveBeenCalled()) + runs(() => + expect( + atom.applicationDelegate.addRecentDocument + ).not.toHaveBeenCalled() + ) waitsForPromise(() => workspace.open(__filename)) - runs(() => expect(atom.applicationDelegate.addRecentDocument).toHaveBeenCalledWith(__filename)) + runs(() => + expect(atom.applicationDelegate.addRecentDocument).toHaveBeenCalledWith( + __filename + ) + ) }) it('notifies ::onDidAddTextEditor observers', () => { @@ -783,14 +1000,24 @@ describe('Workspace', () => { workspace.onDidAddTextEditor(newEditorHandler) let editor = null - waitsForPromise(() => workspace.open(absolutePath).then(e => { editor = e })) + waitsForPromise(() => + workspace.open(absolutePath).then(e => { + editor = e + }) + ) - runs(() => expect(newEditorHandler.argsForCall[0][0].textEditor).toBe(editor)) + runs(() => + expect(newEditorHandler.argsForCall[0][0].textEditor).toBe(editor) + ) }) describe('when there is an error opening the file', () => { let notificationSpy = null - beforeEach(() => atom.notifications.onDidAddNotification(notificationSpy = jasmine.createSpy())) + beforeEach(() => + atom.notifications.onDidAddNotification( + (notificationSpy = jasmine.createSpy()) + ) + ) describe('when a file does not exist', () => { it('creates an empty buffer for the specified path', () => { @@ -881,9 +1108,11 @@ describe('Workspace', () => { ) it('rejects the promise', () => { - waitsFor((done) => { + waitsFor(done => { workspace.open('file1').catch(error => { - expect(error.message).toBe('I dont even know what is happening right now!!') + expect(error.message).toBe( + 'I dont even know what is happening right now!!' + ) done() }) }) @@ -897,7 +1126,7 @@ describe('Workspace', () => { let pane = null waitsForPromise(() => - atom.workspace.open('sample.js', {pending: true}).then(o => { + atom.workspace.open('sample.js', { pending: true }).then(o => { editor = o pane = atom.workspace.getActivePane() }) @@ -917,11 +1146,15 @@ describe('Workspace', () => { let editor2 = null waitsForPromise(() => - atom.workspace.open('sample.txt').then(o => { editor1 = o }) + atom.workspace.open('sample.txt').then(o => { + editor1 = o + }) ) waitsForPromise(() => - atom.workspace.open('sample2.txt', {pending: true}).then(o => { editor2 = o }) + atom.workspace.open('sample2.txt', { pending: true }).then(o => { + editor2 = o + }) ) runs(() => { @@ -942,11 +1175,13 @@ describe('Workspace', () => { let rightPane = null waitsForPromise(() => - atom.workspace.open('sample.js', {pending: true, split: 'right'}).then(o => { - editor1 = o - rightPane = atom.workspace.getActivePane() - spyOn(rightPane, 'destroy').andCallThrough() - }) + atom.workspace + .open('sample.js', { pending: true, split: 'right' }) + .then(o => { + editor1 = o + rightPane = atom.workspace.getActivePane() + spyOn(rightPane, 'destroy').andCallThrough() + }) ) runs(() => { @@ -957,7 +1192,9 @@ describe('Workspace', () => { }) waitsForPromise(() => - atom.workspace.open('sample.txt', {pending: true}).then(o => { editor2 = o }) + atom.workspace.open('sample.txt', { pending: true }).then(o => { + editor2 = o + }) ) runs(() => { @@ -967,15 +1204,19 @@ describe('Workspace', () => { }) }) - describe('when opening an editor with a buffer that isn\'t part of the project', () => { + describe("when opening an editor with a buffer that isn't part of the project", () => { it('adds the buffer to the project', async () => { const buffer = new TextBuffer() - const editor = new TextEditor({buffer}) + const editor = new TextEditor({ buffer }) await atom.workspace.open(editor) - expect(atom.project.getBuffers().map(buffer => buffer.id)).toContain(buffer.id) - expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar') + expect(atom.project.getBuffers().map(buffer => buffer.id)).toContain( + buffer.id + ) + expect(buffer.getLanguageMode().getLanguageId()).toBe( + 'text.plain.null-grammar' + ) }) }) }) @@ -985,21 +1226,42 @@ describe('Workspace', () => { const uri = 'atom://test-pane-for-item' const item = { element: document.createElement('div'), - getURI () { return uri } + getURI () { + return uri + } } atom.workspace.getActivePane().activateItem(item) - expect(atom.workspace.paneForItem(item)).toBe(atom.workspace.getCenter().getActivePane()) - expect(atom.workspace.paneContainerForItem(item)).toBe(atom.workspace.getCenter()) - expect(atom.workspace.paneForURI(uri)).toBe(atom.workspace.getCenter().getActivePane()) - expect(atom.workspace.paneContainerForURI(uri)).toBe(atom.workspace.getCenter()) + expect(atom.workspace.paneForItem(item)).toBe( + atom.workspace.getCenter().getActivePane() + ) + expect(atom.workspace.paneContainerForItem(item)).toBe( + atom.workspace.getCenter() + ) + expect(atom.workspace.paneForURI(uri)).toBe( + atom.workspace.getCenter().getActivePane() + ) + expect(atom.workspace.paneContainerForURI(uri)).toBe( + atom.workspace.getCenter() + ) atom.workspace.getActivePane().destroyActiveItem() - atom.workspace.getLeftDock().getActivePane().activateItem(item) - expect(atom.workspace.paneForItem(item)).toBe(atom.workspace.getLeftDock().getActivePane()) - expect(atom.workspace.paneContainerForItem(item)).toBe(atom.workspace.getLeftDock()) - expect(atom.workspace.paneForURI(uri)).toBe(atom.workspace.getLeftDock().getActivePane()) - expect(atom.workspace.paneContainerForURI(uri)).toBe(atom.workspace.getLeftDock()) + atom.workspace + .getLeftDock() + .getActivePane() + .activateItem(item) + expect(atom.workspace.paneForItem(item)).toBe( + atom.workspace.getLeftDock().getActivePane() + ) + expect(atom.workspace.paneContainerForItem(item)).toBe( + atom.workspace.getLeftDock() + ) + expect(atom.workspace.paneForURI(uri)).toBe( + atom.workspace.getLeftDock().getActivePane() + ) + expect(atom.workspace.paneContainerForURI(uri)).toBe( + atom.workspace.getLeftDock() + ) }) }) @@ -1061,13 +1323,21 @@ describe('Workspace', () => { describe('when the location resolves to a dock', () => { it('adds or shows the item and its dock if it is not currently visible, and otherwise hides the containing dock', async () => { const item1 = { - getDefaultLocation () { return 'left' }, - getElement () { return (this.element = document.createElement('div')) } + getDefaultLocation () { + return 'left' + }, + getElement () { + return (this.element = document.createElement('div')) + } } const item2 = { - getDefaultLocation () { return 'left' }, - getElement () { return (this.element = document.createElement('div')) } + getDefaultLocation () { + return 'left' + }, + getElement () { + return (this.element = document.createElement('div')) + } } const dock = workspace.getLeftDock() @@ -1098,13 +1368,21 @@ describe('Workspace', () => { describe('when the location resolves to the center', () => { it('adds or shows the item if it is not currently the active pane item, and otherwise removes the item', async () => { const item1 = { - getDefaultLocation () { return 'center' }, - getElement () { return (this.element = document.createElement('div')) } + getDefaultLocation () { + return 'center' + }, + getElement () { + return (this.element = document.createElement('div')) + } } const item2 = { - getDefaultLocation () { return 'center' }, - getElement () { return (this.element = document.createElement('div')) } + getDefaultLocation () { + return 'center' + }, + getElement () { + return (this.element = document.createElement('div')) + } } expect(workspace.getActivePaneItem()).toBeUndefined() @@ -1124,43 +1402,45 @@ describe('Workspace', () => { describe('active pane containers', () => { it('maintains the active pane and item globally across active pane containers', () => { const leftDock = workspace.getLeftDock() - const leftItem1 = {element: document.createElement('div')} - const leftItem2 = {element: document.createElement('div')} - const leftItem3 = {element: document.createElement('div')} + const leftItem1 = { element: document.createElement('div') } + const leftItem2 = { element: document.createElement('div') } + const leftItem3 = { element: document.createElement('div') } const leftPane1 = leftDock.getActivePane() leftPane1.addItems([leftItem1, leftItem2]) - const leftPane2 = leftPane1.splitDown({items: [leftItem3]}) + const leftPane2 = leftPane1.splitDown({ items: [leftItem3] }) const rightDock = workspace.getRightDock() - const rightItem1 = {element: document.createElement('div')} - const rightItem2 = {element: document.createElement('div')} - const rightItem3 = {element: document.createElement('div')} + const rightItem1 = { element: document.createElement('div') } + const rightItem2 = { element: document.createElement('div') } + const rightItem3 = { element: document.createElement('div') } const rightPane1 = rightDock.getActivePane() rightPane1.addItems([rightItem1, rightItem2]) - const rightPane2 = rightPane1.splitDown({items: [rightItem3]}) + const rightPane2 = rightPane1.splitDown({ items: [rightItem3] }) const bottomDock = workspace.getBottomDock() - const bottomItem1 = {element: document.createElement('div')} - const bottomItem2 = {element: document.createElement('div')} - const bottomItem3 = {element: document.createElement('div')} + const bottomItem1 = { element: document.createElement('div') } + const bottomItem2 = { element: document.createElement('div') } + const bottomItem3 = { element: document.createElement('div') } const bottomPane1 = bottomDock.getActivePane() bottomPane1.addItems([bottomItem1, bottomItem2]) - const bottomPane2 = bottomPane1.splitDown({items: [bottomItem3]}) + const bottomPane2 = bottomPane1.splitDown({ items: [bottomItem3] }) const center = workspace.getCenter() - const centerItem1 = {element: document.createElement('div')} - const centerItem2 = {element: document.createElement('div')} - const centerItem3 = {element: document.createElement('div')} + const centerItem1 = { element: document.createElement('div') } + const centerItem2 = { element: document.createElement('div') } + const centerItem3 = { element: document.createElement('div') } const centerPane1 = center.getActivePane() centerPane1.addItems([centerItem1, centerItem2]) - const centerPane2 = centerPane1.splitDown({items: [centerItem3]}) + const centerPane2 = centerPane1.splitDown({ items: [centerItem3] }) const activePaneContainers = [] const activePanes = [] const activeItems = [] - workspace.onDidChangeActivePaneContainer((container) => activePaneContainers.push(container)) - workspace.onDidChangeActivePane((pane) => activePanes.push(pane)) - workspace.onDidChangeActivePaneItem((item) => activeItems.push(item)) + workspace.onDidChangeActivePaneContainer(container => + activePaneContainers.push(container) + ) + workspace.onDidChangeActivePane(pane => activePanes.push(pane)) + workspace.onDidChangeActivePaneItem(item => activeItems.push(item)) function clearEvents () { activePaneContainers.length = 0 activePanes.length = 0 @@ -1255,11 +1535,18 @@ describe('Workspace', () => { describe('::onDidStopChangingActivePaneItem()', () => { it('invokes observers when the active item of the active pane stops changing', () => { const pane1 = atom.workspace.getCenter().getActivePane() - const pane2 = pane1.splitRight({items: [document.createElement('div'), document.createElement('div')]}); - atom.workspace.getLeftDock().getActivePane().addItem(document.createElement('div')) + const pane2 = pane1.splitRight({ + items: [document.createElement('div'), document.createElement('div')] + }) + atom.workspace + .getLeftDock() + .getActivePane() + .addItem(document.createElement('div')) - emittedItems = [] - atom.workspace.onDidStopChangingActivePaneItem(item => emittedItems.push(item)) + const emittedItems = [] + atom.workspace.onDidStopChangingActivePaneItem(item => + emittedItems.push(item) + ) pane2.activateNextItem() pane2.activateNextItem() @@ -1267,7 +1554,9 @@ describe('Workspace', () => { atom.workspace.getLeftDock().activate() advanceClock(100) - expect(emittedItems).toEqual([atom.workspace.getLeftDock().getActivePaneItem()]) + expect(emittedItems).toEqual([ + atom.workspace.getLeftDock().getActivePaneItem() + ]) }) }) @@ -1281,15 +1570,23 @@ describe('Workspace', () => { const coffeeScriptGrammarUsed = jasmine.createSpy('coffeescript') atom.packages.triggerDeferredActivationHooks() - atom.packages.onDidTriggerActivationHook('language-javascript:grammar-used', () => { - atom.workspace.observeTextEditors(observeTextEditorsSpy) - javascriptGrammarUsed() - }) - atom.packages.onDidTriggerActivationHook('language-coffee-script:grammar-used', coffeeScriptGrammarUsed) + atom.packages.onDidTriggerActivationHook( + 'language-javascript:grammar-used', + () => { + atom.workspace.observeTextEditors(observeTextEditorsSpy) + javascriptGrammarUsed() + } + ) + atom.packages.onDidTriggerActivationHook( + 'language-coffee-script:grammar-used', + coffeeScriptGrammarUsed + ) expect(javascriptGrammarUsed).not.toHaveBeenCalled() expect(observeTextEditorsSpy).not.toHaveBeenCalled() - const editor = await atom.workspace.open('sample.js', {autoIndent: false}) + const editor = await atom.workspace.open('sample.js', { + autoIndent: false + }) expect(javascriptGrammarUsed).toHaveBeenCalled() expect(observeTextEditorsSpy.callCount).toBe(1) @@ -1309,15 +1606,23 @@ describe('Workspace', () => { const coffeeScriptGrammarUsed = jasmine.createSpy('coffeescript') atom.packages.triggerDeferredActivationHooks() - atom.packages.onDidTriggerActivationHook('source.js:root-scope-used', () => { - atom.workspace.observeTextEditors(observeTextEditorsSpy) - javascriptGrammarUsed() - }) - atom.packages.onDidTriggerActivationHook('source.coffee:root-scope-used', coffeeScriptGrammarUsed) + atom.packages.onDidTriggerActivationHook( + 'source.js:root-scope-used', + () => { + atom.workspace.observeTextEditors(observeTextEditorsSpy) + javascriptGrammarUsed() + } + ) + atom.packages.onDidTriggerActivationHook( + 'source.coffee:root-scope-used', + coffeeScriptGrammarUsed + ) expect(javascriptGrammarUsed).not.toHaveBeenCalled() expect(observeTextEditorsSpy).not.toHaveBeenCalled() - const editor = await atom.workspace.open('sample.js', {autoIndent: false}) + const editor = await atom.workspace.open('sample.js', { + autoIndent: false + }) expect(javascriptGrammarUsed).toHaveBeenCalled() expect(observeTextEditorsSpy.callCount).toBe(1) @@ -1331,11 +1636,13 @@ describe('Workspace', () => { it("opens the uri associated with the last closed pane that isn't currently open", () => { const pane = workspace.getActivePane() waitsForPromise(() => - workspace.open('a').then(() => - workspace.open('b').then(() => - workspace.open('file1').then(() => workspace.open()) + workspace + .open('a') + .then(() => + workspace + .open('b') + .then(() => workspace.open('file1').then(() => workspace.open())) ) - ) ) runs(() => { @@ -1353,11 +1660,17 @@ describe('Workspace', () => { expect(workspace.getActivePaneItem().getURI()).not.toBeUndefined() // destroy all items - expect(workspace.getActivePaneItem().getURI()).toBe(firstDirectory.resolve('file1')) + expect(workspace.getActivePaneItem().getURI()).toBe( + firstDirectory.resolve('file1') + ) pane.destroyActiveItem() - expect(workspace.getActivePaneItem().getURI()).toBe(firstDirectory.resolve('b')) + expect(workspace.getActivePaneItem().getURI()).toBe( + firstDirectory.resolve('b') + ) pane.destroyActiveItem() - expect(workspace.getActivePaneItem().getURI()).toBe(firstDirectory.resolve('a')) + expect(workspace.getActivePaneItem().getURI()).toBe( + firstDirectory.resolve('a') + ) pane.destroyActiveItem() // reopens items with uris @@ -1366,16 +1679,28 @@ describe('Workspace', () => { waitsForPromise(() => workspace.reopenItem()) - runs(() => expect(workspace.getActivePaneItem().getURI()).toBe(firstDirectory.resolve('a'))) + runs(() => + expect(workspace.getActivePaneItem().getURI()).toBe( + firstDirectory.resolve('a') + ) + ) // does not reopen items that are already open waitsForPromise(() => workspace.open('b')) - runs(() => expect(workspace.getActivePaneItem().getURI()).toBe(firstDirectory.resolve('b'))) + runs(() => + expect(workspace.getActivePaneItem().getURI()).toBe( + firstDirectory.resolve('b') + ) + ) waitsForPromise(() => workspace.reopenItem()) - runs(() => expect(workspace.getActivePaneItem().getURI()).toBe(firstDirectory.resolve('file1'))) + runs(() => + expect(workspace.getActivePaneItem().getURI()).toBe( + firstDirectory.resolve('file1') + ) + ) }) }) @@ -1431,14 +1756,16 @@ describe('Workspace', () => { describe('::openLicense()', () => { it('opens the license as plain-text in a buffer', () => { waitsForPromise(() => workspace.openLicense()) - runs(() => expect(workspace.getActivePaneItem().getText()).toMatch(/Copyright/)) + runs(() => + expect(workspace.getActivePaneItem().getText()).toMatch(/Copyright/) + ) }) }) describe('::isTextEditor(obj)', () => { it('returns true when the passed object is an instance of `TextEditor`', () => { expect(workspace.isTextEditor(new TextEditor())).toBe(true) - expect(workspace.isTextEditor({getText: () => null})).toBe(false) + expect(workspace.isTextEditor({ getText: () => null })).toBe(false) expect(workspace.isTextEditor(null)).toBe(false) expect(workspace.isTextEditor(undefined)).toBe(false) }) @@ -1499,7 +1826,7 @@ describe('Workspace', () => { describe('::observeActiveTextEditor()', () => { it('invokes the observer with current active text editor and each time a different text editor becomes active', () => { const pane = workspace.getCenter().getActivePane() - observed = [] + const observed = [] const inactiveEditorBeforeRegisteringObserver = new TextEditor() const activeEditorBeforeRegisteringObserver = new TextEditor() @@ -1509,12 +1836,12 @@ describe('Workspace', () => { workspace.observeActiveTextEditor(editor => observed.push(editor)) const editorAddedAfterRegisteringObserver = new TextEditor() - const nonEditorItemAddedAfterRegisteringObserver = document.createElement('div') pane.activateItem(editorAddedAfterRegisteringObserver) - expect(observed).toEqual( - [activeEditorBeforeRegisteringObserver, editorAddedAfterRegisteringObserver] - ) + expect(observed).toEqual([ + activeEditorBeforeRegisteringObserver, + editorAddedAfterRegisteringObserver + ]) }) }) @@ -1566,7 +1893,7 @@ describe('Workspace', () => { const nonEditorItem1 = document.createElement('div') const nonEditorItem2 = document.createElement('div') pane.activateItem(nonEditorItem1) - pane.activateItem(nonEditorItem1) + pane.activateItem(nonEditorItem2) expect(observed).toEqual([]) }) @@ -1602,7 +1929,7 @@ describe('Workspace', () => { atom.grammars.assignLanguageMode(editor, 'source.js') expect(editor.getGrammar().name).toBe('JavaScript') - workspace.getActivePane().splitRight({copyActiveItem: true}) + workspace.getActivePane().splitRight({ copyActiveItem: true }) const newEditor = workspace.getActiveTextEditor() expect(newEditor).not.toBe(editor) expect(newEditor.getGrammar().name).toBe('JavaScript') @@ -1612,36 +1939,45 @@ describe('Workspace', () => { it('stores the active grammars used by all the open editors', () => { waitsForPromise(() => atom.packages.activatePackage('language-javascript')) - waitsForPromise(() => atom.packages.activatePackage('language-coffee-script')) + waitsForPromise(() => + atom.packages.activatePackage('language-coffee-script') + ) waitsForPromise(() => atom.packages.activatePackage('language-todo')) waitsForPromise(() => atom.workspace.open('sample.coffee')) runs(() => { - atom.workspace.getActiveTextEditor().setText(dedent ` + atom.workspace.getActiveTextEditor().setText(dedent` i = /test/; #FIXME\ `) - const atom2 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate}) + const atom2 = new AtomEnvironment({ + applicationDelegate: atom.applicationDelegate + }) atom2.initialize({ window: document.createElement('div'), - document: Object.assign( - document.createElement('div'), - { - body: document.createElement('div'), - head: document.createElement('div') - } - ) + document: Object.assign(document.createElement('div'), { + body: document.createElement('div'), + head: document.createElement('div') + }) }) atom2.packages.loadPackage('language-javascript') atom2.packages.loadPackage('language-coffee-script') atom2.packages.loadPackage('language-todo') atom2.project.deserialize(atom.project.serialize()) - atom2.workspace.deserialize(atom.workspace.serialize(), atom2.deserializers) + atom2.workspace.deserialize( + atom.workspace.serialize(), + atom2.deserializers + ) - expect(atom2.grammars.getGrammars().map(grammar => grammar.scopeName).sort()).toEqual([ + expect( + atom2.grammars + .getGrammars() + .map(grammar => grammar.scopeName) + .sort() + ).toEqual([ 'source.coffee', 'source.js', 'source.js.regexp', @@ -1658,7 +1994,10 @@ describe('Workspace', () => { describe('document.title', () => { describe('when there is no item open', () => { - it('sets the title to the project path', () => expect(document.title).toMatch(escapeStringRegex(fs.tildify(atom.project.getPaths()[0])))) + it('sets the title to the project path', () => + expect(document.title).toMatch( + escapeStringRegex(fs.tildify(atom.project.getPaths()[0])) + )) it("sets the title to 'untitled' if there is no project path", () => { atom.project.setPaths([]) @@ -1675,16 +2014,24 @@ describe('Workspace', () => { it("sets the title to the pane item's title plus the item's path", () => { const item = atom.workspace.getActivePaneItem() - const pathEscaped = fs.tildify(escapeStringRegex(path.dirname(item.getPath()))) - expect(document.title).toMatch(new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`)) + const pathEscaped = fs.tildify( + escapeStringRegex(path.dirname(item.getPath())) + ) + expect(document.title).toMatch( + new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`) + ) }) describe('when the title of the active pane item changes', () => { it("updates the window title based on the item's new title", () => { const editor = atom.workspace.getActivePaneItem() editor.buffer.setPath(path.join(temp.dir, 'hi')) - const pathEscaped = fs.tildify(escapeStringRegex(path.dirname(editor.getPath()))) - expect(document.title).toMatch(new RegExp(`^${editor.getTitle()} \\u2014 ${pathEscaped}`)) + const pathEscaped = fs.tildify( + escapeStringRegex(path.dirname(editor.getPath())) + ) + expect(document.title).toMatch( + new RegExp(`^${editor.getTitle()} \\u2014 ${pathEscaped}`) + ) }) }) @@ -1692,8 +2039,12 @@ describe('Workspace', () => { it("updates the title to the new item's title plus the project path", () => { atom.workspace.getActivePane().activateNextItem() const item = atom.workspace.getActivePaneItem() - const pathEscaped = fs.tildify(escapeStringRegex(path.dirname(item.getPath()))) - expect(document.title).toMatch(new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`)) + const pathEscaped = fs.tildify( + escapeStringRegex(path.dirname(item.getPath())) + ) + expect(document.title).toMatch( + new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`) + ) }) }) @@ -1709,15 +2060,17 @@ describe('Workspace', () => { }) describe('when the active pane item is inside a project path', () => { - beforeEach(() => - waitsForPromise(() => atom.workspace.open('b')) - ) + beforeEach(() => waitsForPromise(() => atom.workspace.open('b'))) describe('when there is an active pane item', () => { it("sets the title to the pane item's title plus the project path", () => { const item = atom.workspace.getActivePaneItem() - const pathEscaped = fs.tildify(escapeStringRegex(atom.project.getPaths()[0])) - expect(document.title).toMatch(new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`)) + const pathEscaped = fs.tildify( + escapeStringRegex(atom.project.getPaths()[0]) + ) + expect(document.title).toMatch( + new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`) + ) }) }) @@ -1725,8 +2078,12 @@ describe('Workspace', () => { it("updates the window title based on the item's new title", () => { const editor = atom.workspace.getActivePaneItem() editor.buffer.setPath(path.join(atom.project.getPaths()[0], 'hi')) - const pathEscaped = fs.tildify(escapeStringRegex(atom.project.getPaths()[0])) - expect(document.title).toMatch(new RegExp(`^${editor.getTitle()} \\u2014 ${pathEscaped}`)) + const pathEscaped = fs.tildify( + escapeStringRegex(atom.project.getPaths()[0]) + ) + expect(document.title).toMatch( + new RegExp(`^${editor.getTitle()} \\u2014 ${pathEscaped}`) + ) }) }) @@ -1734,8 +2091,12 @@ describe('Workspace', () => { it("updates the title to the new item's title plus the project path", () => { atom.workspace.getActivePane().activateNextItem() const item = atom.workspace.getActivePaneItem() - const pathEscaped = fs.tildify(escapeStringRegex(atom.project.getPaths()[0])) - expect(document.title).toMatch(new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`)) + const pathEscaped = fs.tildify( + escapeStringRegex(atom.project.getPaths()[0]) + ) + expect(document.title).toMatch( + new RegExp(`^${item.getTitle()} \\u2014 ${pathEscaped}`) + ) }) }) @@ -1743,7 +2104,9 @@ describe('Workspace', () => { it("updates the title to the project's first path", () => { atom.workspace.getActivePane().destroy() expect(atom.workspace.getActivePaneItem()).toBeUndefined() - expect(document.title).toMatch(escapeStringRegex(fs.tildify(atom.project.getPaths()[0]))) + expect(document.title).toMatch( + escapeStringRegex(fs.tildify(atom.project.getPaths()[0])) + ) }) }) @@ -1764,25 +2127,33 @@ describe('Workspace', () => { it("updates the title to contain the project's path", () => { document.title = null - const atom2 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate}) + const atom2 = new AtomEnvironment({ + applicationDelegate: atom.applicationDelegate + }) atom2.initialize({ window: document.createElement('div'), - document: Object.assign( - document.createElement('div'), - { - body: document.createElement('div'), - head: document.createElement('div') - } - ) + document: Object.assign(document.createElement('div'), { + body: document.createElement('div'), + head: document.createElement('div') + }) }) - waitsForPromise(() => atom2.project.deserialize(atom.project.serialize())) + waitsForPromise(() => + atom2.project.deserialize(atom.project.serialize()) + ) runs(() => { - atom2.workspace.deserialize(atom.workspace.serialize(), atom2.deserializers) + atom2.workspace.deserialize( + atom.workspace.serialize(), + atom2.deserializers + ) const item = atom2.workspace.getActivePaneItem() - const pathEscaped = fs.tildify(escapeStringRegex(atom.project.getPaths()[0])) - expect(document.title).toMatch(new RegExp(`^${item.getLongTitle()} \\u2014 ${pathEscaped}`)) + const pathEscaped = fs.tildify( + escapeStringRegex(atom.project.getPaths()[0]) + ) + expect(document.title).toMatch( + new RegExp(`^${item.getLongTitle()} \\u2014 ${pathEscaped}`) + ) atom2.destroy() }) @@ -1798,7 +2169,7 @@ describe('Workspace', () => { waitsForPromise(() => atom.workspace.open('a')) waitsForPromise(() => atom.workspace.open('b')) runs(() => { - [item1, item2] = atom.workspace.getPaneItems() + ;[item1, item2] = atom.workspace.getPaneItems() }) }) @@ -1832,31 +2203,46 @@ describe('Workspace', () => { // Don't use ES6 classes because then we'll have to call `super()` which we can't do with // HTMLElement - function TestItemElement () { this.constructor = TestItemElement } - function Ctor () { this.constructor = TestItemElement } + function TestItemElement () { + this.constructor = TestItemElement + } + function Ctor () { + this.constructor = TestItemElement + } Ctor.prototype = HTMLElement.prototype TestItemElement.prototype = new Ctor() TestItemElement.__super__ = HTMLElement.prototype - TestItemElement.prototype.initialize = function (model) { this.model = model; return this } - TestItemElement.prototype.getModel = function () { return this.model } + TestItemElement.prototype.initialize = function (model) { + this.model = model + return this + } + TestItemElement.prototype.getModel = function () { + return this.model + } beforeEach(() => - atom.views.addViewProvider(TestItem, model => new TestItemElement().initialize(model)) + atom.views.addViewProvider(TestItem, model => + new TestItemElement().initialize(model) + ) ) describe('::addLeftPanel(model)', () => { it('adds a panel to the correct panel container', () => { let addPanelSpy expect(atom.workspace.getLeftPanels().length).toBe(0) - atom.workspace.panelContainers.left.onDidAddPanel(addPanelSpy = jasmine.createSpy()) + atom.workspace.panelContainers.left.onDidAddPanel( + (addPanelSpy = jasmine.createSpy()) + ) const model = new TestItem() - const panel = atom.workspace.addLeftPanel({item: model}) + const panel = atom.workspace.addLeftPanel({ item: model }) expect(panel).toBeDefined() - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) - const itemView = atom.views.getView(atom.workspace.getLeftPanels()[0].getItem()) + const itemView = atom.views.getView( + atom.workspace.getLeftPanels()[0].getItem() + ) expect(itemView instanceof TestItemElement).toBe(true) expect(itemView.getModel()).toBe(model) }) @@ -1866,15 +2252,19 @@ describe('Workspace', () => { it('adds a panel to the correct panel container', () => { let addPanelSpy expect(atom.workspace.getRightPanels().length).toBe(0) - atom.workspace.panelContainers.right.onDidAddPanel(addPanelSpy = jasmine.createSpy()) + atom.workspace.panelContainers.right.onDidAddPanel( + (addPanelSpy = jasmine.createSpy()) + ) const model = new TestItem() - const panel = atom.workspace.addRightPanel({item: model}) + const panel = atom.workspace.addRightPanel({ item: model }) expect(panel).toBeDefined() - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) - const itemView = atom.views.getView(atom.workspace.getRightPanels()[0].getItem()) + const itemView = atom.views.getView( + atom.workspace.getRightPanels()[0].getItem() + ) expect(itemView instanceof TestItemElement).toBe(true) expect(itemView.getModel()).toBe(model) }) @@ -1884,15 +2274,19 @@ describe('Workspace', () => { it('adds a panel to the correct panel container', () => { let addPanelSpy expect(atom.workspace.getTopPanels().length).toBe(0) - atom.workspace.panelContainers.top.onDidAddPanel(addPanelSpy = jasmine.createSpy()) + atom.workspace.panelContainers.top.onDidAddPanel( + (addPanelSpy = jasmine.createSpy()) + ) const model = new TestItem() - const panel = atom.workspace.addTopPanel({item: model}) + const panel = atom.workspace.addTopPanel({ item: model }) expect(panel).toBeDefined() - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) - const itemView = atom.views.getView(atom.workspace.getTopPanels()[0].getItem()) + const itemView = atom.views.getView( + atom.workspace.getTopPanels()[0].getItem() + ) expect(itemView instanceof TestItemElement).toBe(true) expect(itemView.getModel()).toBe(model) }) @@ -1902,15 +2296,19 @@ describe('Workspace', () => { it('adds a panel to the correct panel container', () => { let addPanelSpy expect(atom.workspace.getBottomPanels().length).toBe(0) - atom.workspace.panelContainers.bottom.onDidAddPanel(addPanelSpy = jasmine.createSpy()) + atom.workspace.panelContainers.bottom.onDidAddPanel( + (addPanelSpy = jasmine.createSpy()) + ) const model = new TestItem() - const panel = atom.workspace.addBottomPanel({item: model}) + const panel = atom.workspace.addBottomPanel({ item: model }) expect(panel).toBeDefined() - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) - const itemView = atom.views.getView(atom.workspace.getBottomPanels()[0].getItem()) + const itemView = atom.views.getView( + atom.workspace.getBottomPanels()[0].getItem() + ) expect(itemView instanceof TestItemElement).toBe(true) expect(itemView.getModel()).toBe(model) }) @@ -1920,15 +2318,19 @@ describe('Workspace', () => { it('adds a panel to the correct panel container', () => { let addPanelSpy expect(atom.workspace.getHeaderPanels().length).toBe(0) - atom.workspace.panelContainers.header.onDidAddPanel(addPanelSpy = jasmine.createSpy()) + atom.workspace.panelContainers.header.onDidAddPanel( + (addPanelSpy = jasmine.createSpy()) + ) const model = new TestItem() - const panel = atom.workspace.addHeaderPanel({item: model}) + const panel = atom.workspace.addHeaderPanel({ item: model }) expect(panel).toBeDefined() - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) - const itemView = atom.views.getView(atom.workspace.getHeaderPanels()[0].getItem()) + const itemView = atom.views.getView( + atom.workspace.getHeaderPanels()[0].getItem() + ) expect(itemView instanceof TestItemElement).toBe(true) expect(itemView.getModel()).toBe(model) }) @@ -1938,15 +2340,19 @@ describe('Workspace', () => { it('adds a panel to the correct panel container', () => { let addPanelSpy expect(atom.workspace.getFooterPanels().length).toBe(0) - atom.workspace.panelContainers.footer.onDidAddPanel(addPanelSpy = jasmine.createSpy()) + atom.workspace.panelContainers.footer.onDidAddPanel( + (addPanelSpy = jasmine.createSpy()) + ) const model = new TestItem() - const panel = atom.workspace.addFooterPanel({item: model}) + const panel = atom.workspace.addFooterPanel({ item: model }) expect(panel).toBeDefined() - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) - const itemView = atom.views.getView(atom.workspace.getFooterPanels()[0].getItem()) + const itemView = atom.views.getView( + atom.workspace.getFooterPanels()[0].getItem() + ) expect(itemView instanceof TestItemElement).toBe(true) expect(itemView.getModel()).toBe(model) }) @@ -1956,15 +2362,19 @@ describe('Workspace', () => { it('adds a panel to the correct panel container', () => { let addPanelSpy expect(atom.workspace.getModalPanels().length).toBe(0) - atom.workspace.panelContainers.modal.onDidAddPanel(addPanelSpy = jasmine.createSpy()) + atom.workspace.panelContainers.modal.onDidAddPanel( + (addPanelSpy = jasmine.createSpy()) + ) const model = new TestItem() - const panel = atom.workspace.addModalPanel({item: model}) + const panel = atom.workspace.addModalPanel({ item: model }) expect(panel).toBeDefined() - expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0}) + expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 }) - const itemView = atom.views.getView(atom.workspace.getModalPanels()[0].getItem()) + const itemView = atom.views.getView( + atom.workspace.getModalPanels()[0].getItem() + ) expect(itemView instanceof TestItemElement).toBe(true) expect(itemView.getModel()).toBe(model) }) @@ -1973,7 +2383,7 @@ describe('Workspace', () => { describe('::panelForItem(item)', () => { it('returns the panel associated with the item', () => { const item = new TestItem() - const panel = atom.workspace.addLeftPanel({item}) + const panel = atom.workspace.addLeftPanel({ item }) const itemWithNoPanel = new TestItem() @@ -1989,13 +2399,17 @@ describe('Workspace', () => { const results = [] waitsForPromise(() => atom.workspace.scan( - /(a)+/, {leadingContextLineCount: 1, trailingContextLineCount: 1}, - result => results.push(result)) + /(a)+/, + { leadingContextLineCount: 1, trailingContextLineCount: 1 }, + result => results.push(result) + ) ) runs(() => { expect(results).toHaveLength(3) - expect(results[0].filePath).toBe(atom.project.getDirectories()[0].resolve('a')) + expect(results[0].filePath).toBe( + atom.project.getDirectories()[0].resolve('a') + ) expect(results[0].matches).toHaveLength(3) expect(results[0].matches[0]).toEqual({ matchText: 'aaa', @@ -2010,13 +2424,17 @@ describe('Workspace', () => { it('works with with escaped literals (like $ and ^)', () => { const results = [] - waitsForPromise(() => atom.workspace.scan( - /\$\w+/, {leadingContextLineCount: 1, trailingContextLineCount: 1}, - result => results.push(result))) + waitsForPromise(() => + atom.workspace.scan( + /\$\w+/, + { leadingContextLineCount: 1, trailingContextLineCount: 1 }, + result => results.push(result) + ) + ) runs(() => { expect(results.length).toBe(1) - const {filePath, matches} = results[0] + const { filePath, matches } = results[0] expect(filePath).toBe(atom.project.getDirectories()[0].resolve('a')) expect(matches).toHaveLength(1) expect(matches[0]).toEqual({ @@ -2064,7 +2482,9 @@ describe('Workspace', () => { it('ignores case if the regex includes the `i` flag', () => { const results = [] - waitsForPromise(() => atom.workspace.scan(/DOLLAR/i, result => results.push(result))) + waitsForPromise(() => + atom.workspace.scan(/DOLLAR/i, result => results.push(result)) + ) runs(() => expect(results).toHaveLength(1)) }) @@ -2074,7 +2494,12 @@ describe('Workspace', () => { let ignoredPath beforeEach(() => { - const sourceProjectPath = path.join(__dirname, 'fixtures', 'git', 'working-dir') + const sourceProjectPath = path.join( + __dirname, + 'fixtures', + 'git', + 'working-dir' + ) projectPath = path.join(temp.mkdirSync('atom')) const writerStream = fstream.Writer(projectPath) @@ -2086,7 +2511,10 @@ describe('Workspace', () => { }) runs(() => { - fs.renameSync(path.join(projectPath, 'git.git'), path.join(projectPath, '.git')) + fs.rename( + path.join(projectPath, 'git.git'), + path.join(projectPath, '.git') + ) ignoredPath = path.join(projectPath, 'ignored.txt') fs.writeFileSync(ignoredPath, 'this match should not be included') }) @@ -2119,10 +2547,14 @@ describe('Workspace', () => { const paths = [] let matches = [] waitsForPromise(() => - atom.workspace.scan(/aaa/, {paths: [`a-dir${path.sep}`]}, result => { - paths.push(result.filePath) - matches = matches.concat(result.matches) - }) + atom.workspace.scan( + /aaa/, + { paths: [`a-dir${path.sep}`] }, + result => { + paths.push(result.filePath) + matches = matches.concat(result.matches) + } + ) ) runs(() => { @@ -2177,11 +2609,16 @@ describe('Workspace', () => { }) ) - waitsForPromise(() => atom.workspace.scan(/a|Elephant/, result => results.push(result))) + waitsForPromise(() => + atom.workspace.scan(/a|Elephant/, result => results.push(result)) + ) runs(() => { expect(results).toHaveLength(3) - const resultForA = _.find(results, ({filePath}) => path.basename(filePath) === 'a') + const resultForA = _.find( + results, + ({ filePath }) => path.basename(filePath) === 'a' + ) expect(resultForA.matches).toHaveLength(1) expect(resultForA.matches[0].matchText).toBe('Elephant') }) @@ -2198,7 +2635,9 @@ describe('Workspace', () => { }) ) - waitsForPromise(() => atom.workspace.scan(/Elephant/, result => results.push(result))) + waitsForPromise(() => + atom.workspace.scan(/Elephant/, result => results.push(result)) + ) runs(() => expect(results).toHaveLength(0)) }) @@ -2225,7 +2664,9 @@ describe('Workspace', () => { it("searches matching files in all of the project's root directories", () => { const resultPaths = [] waitsForPromise(() => - atom.workspace.scan(/aaaa/, ({filePath}) => resultPaths.push(filePath)) + atom.workspace.scan(/aaaa/, ({ filePath }) => + resultPaths.push(filePath) + ) ) runs(() => expect(resultPaths.sort()).toEqual([file1, file2].sort())) @@ -2236,7 +2677,7 @@ describe('Workspace', () => { waitsForPromise(() => { const resultPaths = [] return atom.workspace - .scan(/aaaa/, {paths: ['dir']}, ({filePath}) => { + .scan(/aaaa/, { paths: ['dir'] }, ({ filePath }) => { if (!resultPaths.includes(filePath)) { resultPaths.push(filePath) } @@ -2247,33 +2688,45 @@ describe('Workspace', () => { waitsForPromise(() => { const resultPaths = [] return atom.workspace - .scan(/aaaa/, {paths: [path.join('dir', 'a-dir')]}, ({filePath}) => { - if (!resultPaths.includes(filePath)) { - resultPaths.push(filePath) + .scan( + /aaaa/, + { paths: [path.join('dir', 'a-dir')] }, + ({ filePath }) => { + if (!resultPaths.includes(filePath)) { + resultPaths.push(filePath) + } } - }) + ) .then(() => expect(resultPaths).toEqual([file1])) }) waitsForPromise(() => { const resultPaths = [] return atom.workspace - .scan(/aaaa/, {paths: [path.basename(dir2)]}, ({filePath}) => { - if (!resultPaths.includes(filePath)) { - resultPaths.push(filePath) + .scan( + /aaaa/, + { paths: [path.basename(dir2)] }, + ({ filePath }) => { + if (!resultPaths.includes(filePath)) { + resultPaths.push(filePath) + } } - }) + ) .then(() => expect(resultPaths).toEqual([file2])) }) waitsForPromise(() => { const resultPaths = [] return atom.workspace - .scan(/aaaa/, {paths: [path.join(path.basename(dir2), 'a-dir')]}, ({filePath}) => { - if (!resultPaths.includes(filePath)) { - resultPaths.push(filePath) + .scan( + /aaaa/, + { paths: [path.join(path.basename(dir2), 'a-dir')] }, + ({ filePath }) => { + if (!resultPaths.includes(filePath)) { + resultPaths.push(filePath) + } } - }) + ) .then(() => expect(resultPaths).toEqual([file2])) }) }) @@ -2310,13 +2763,19 @@ describe('Workspace', () => { beforeEach(() => { fakeSearch = null onFakeSearchCreated = null - atom.packages.serviceHub.provide('atom.directory-searcher', '0.1.0', { - canSearchDirectory (directory) { return directory.getPath() === dir1 }, - search (directory, regex, options) { - fakeSearch = new FakeSearch(options) - return fakeSearch + atom.packages.serviceHub.provide( + 'atom.directory-searcher', + '0.1.0', + { + canSearchDirectory (directory) { + return directory.getPath() === dir1 + }, + search (directory, regex, options) { + fakeSearch = new FakeSearch(options) + return fakeSearch + } } - }) + ) waitsFor(() => atom.workspace.directorySearchers.length > 0) }) @@ -2338,24 +2797,32 @@ describe('Workspace', () => { } onFakeSearchCreated = fakeSearch => { fakeSearch.options.didMatch(searchResult) - fakeSearch.options.didSearchPaths(numPathsToPretendToSearchInCustomDirectorySearcher) + fakeSearch.options.didSearchPaths( + numPathsToPretendToSearchInCustomDirectorySearcher + ) fakeSearch.hoistedResolve() } const resultPaths = [] const onPathsSearched = jasmine.createSpy('onPathsSearched') waitsForPromise(() => - atom.workspace.scan(/aaaa/, {onPathsSearched}, ({filePath}) => resultPaths.push(filePath)) + atom.workspace.scan(/aaaa/, { onPathsSearched }, ({ filePath }) => + resultPaths.push(filePath) + ) ) runs(() => { - expect(resultPaths.sort()).toEqual([foreignFilePath, file2].sort()) + expect(resultPaths.sort()).toEqual( + [foreignFilePath, file2].sort() + ) // onPathsSearched should be called once by each DirectorySearcher. The order is not // guaranteed, so we can only verify the total number of paths searched is correct // after the second call. expect(onPathsSearched.callCount).toBe(2) expect(onPathsSearched.mostRecentCall.args[0]).toBe( - numPathsToPretendToSearchInCustomDirectorySearcher + numPathsSearchedInDir2) + numPathsToPretendToSearchInCustomDirectorySearcher + + numPathsSearchedInDir2 + ) }) }) @@ -2371,7 +2838,11 @@ describe('Workspace', () => { expect(fakeSearch.cancelled).toBe(true) }) - waitsForPromise(() => thenable.then(promiseResult => { resultOfPromiseSearch = promiseResult })) + waitsForPromise(() => + thenable.then(promiseResult => { + resultOfPromiseSearch = promiseResult + }) + ) runs(() => expect(resultOfPromiseSearch).toBe('cancelled')) }) @@ -2380,21 +2851,34 @@ describe('Workspace', () => { // This provider's search should be cancelled when the first provider fails let cancelableSearch let fakeSearch2 = null - atom.packages.serviceHub.provide('atom.directory-searcher', '0.1.0', { - canSearchDirectory (directory) { return directory.getPath() === dir2 }, - search (directory, regex, options) { - fakeSearch2 = new FakeSearch(options) - return fakeSearch2 + atom.packages.serviceHub.provide( + 'atom.directory-searcher', + '0.1.0', + { + canSearchDirectory (directory) { + return directory.getPath() === dir2 + }, + search (directory, regex, options) { + fakeSearch2 = new FakeSearch(options) + return fakeSearch2 + } } - }) + ) let didReject = false - const promise = cancelableSearch = atom.workspace.scan(/aaaa/, () => {}) + const promise = (cancelableSearch = atom.workspace.scan( + /aaaa/, + () => {} + )) waitsFor('fakeSearch to be defined', () => fakeSearch != null) runs(() => fakeSearch.hoistedReject()) - waitsForPromise(() => cancelableSearch.catch(() => { didReject = true })) + waitsForPromise(() => + cancelableSearch.catch(() => { + didReject = true + }) + ) waitsFor(done => promise.then(null, done)) @@ -2424,7 +2908,12 @@ describe('Workspace', () => { expect(fs.existsSync(missingPath)).toBeFalsy() waitsForPromise(() => - atom.workspace.replace(/items/gi, 'items', [missingPath], (result, error) => errors.push(error)) + atom.workspace.replace( + /items/gi, + 'items', + [missingPath], + (result, error) => errors.push(error) + ) ) runs(() => { @@ -2441,7 +2930,9 @@ describe('Workspace', () => { const results = [] waitsForPromise(() => - atom.workspace.replace(/items/gi, 'items', [filePath], result => results.push(result)) + atom.workspace.replace(/items/gi, 'items', [filePath], result => + results.push(result) + ) ) runs(() => { @@ -2457,7 +2948,9 @@ describe('Workspace', () => { const results = [] waitsForPromise(() => - atom.workspace.replace(/;$/gmi, 'items', [filePath], result => results.push(result)) + atom.workspace.replace(/;$/gim, 'items', [filePath], result => + results.push(result) + ) ) runs(() => { @@ -2471,17 +2964,26 @@ describe('Workspace', () => { describe('when a buffer is already open', () => { it('replaces properly and saves when not modified', () => { const filePath = path.join(projectDir, 'sample.js') - fs.copyFileSync(path.join(fixturesDir, 'sample.js'), path.join(projectDir, 'sample.js')) + fs.copyFileSync( + path.join(fixturesDir, 'sample.js'), + path.join(projectDir, 'sample.js') + ) let editor = null const results = [] - waitsForPromise(() => atom.workspace.open('sample.js').then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open('sample.js').then(o => { + editor = o + }) + ) runs(() => expect(editor.isModified()).toBeFalsy()) waitsForPromise(() => - atom.workspace.replace(/items/gi, 'items', [filePath], result => results.push(result)) + atom.workspace.replace(/items/gi, 'items', [filePath], result => + results.push(result) + ) ) runs(() => { @@ -2497,13 +2999,21 @@ describe('Workspace', () => { const filePath = path.join(projectDir, 'sample.js') const commentFilePath = path.join(projectDir, 'sample-with-comments.js') fs.copyFileSync(path.join(fixturesDir, 'sample.js'), filePath) - fs.copyFileSync(path.join(fixturesDir, 'sample-with-comments.js'), path.join(projectDir, 'sample-with-comments.js')) + fs.copyFileSync( + path.join(fixturesDir, 'sample-with-comments.js'), + path.join(projectDir, 'sample-with-comments.js') + ) const results = [] waitsForPromise(() => atom.workspace.open('sample-with-comments.js')) waitsForPromise(() => - atom.workspace.replace(/items/gi, 'items', [commentFilePath], result => results.push(result)) + atom.workspace.replace( + /items/gi, + 'items', + [commentFilePath], + result => results.push(result) + ) ) runs(() => { @@ -2519,7 +3029,11 @@ describe('Workspace', () => { let editor = null const results = [] - waitsForPromise(() => atom.workspace.open('sample.js').then(o => { editor = o })) + waitsForPromise(() => + atom.workspace.open('sample.js').then(o => { + editor = o + }) + ) runs(() => { editor.buffer.setTextInRange([[0, 0], [0, 0]], 'omg') @@ -2527,7 +3041,9 @@ describe('Workspace', () => { }) waitsForPromise(() => - atom.workspace.replace(/items/gi, 'okthen', [filePath], result => results.push(result)) + atom.workspace.replace(/items/gi, 'okthen', [filePath], result => + results.push(result) + ) ) runs(() => { @@ -2545,9 +3061,11 @@ describe('Workspace', () => { let editor, notificationSpy beforeEach(() => { - waitsForPromise(() => atom.workspace.open('sample.js').then(o => { - editor = o - })) + waitsForPromise(() => + atom.workspace.open('sample.js').then(o => { + editor = o + }) + ) notificationSpy = jasmine.createSpy('did-add-notification') atom.notifications.onDidAddNotification(notificationSpy) @@ -2562,8 +3080,12 @@ describe('Workspace', () => { waitsForPromise(() => atom.workspace.saveActivePaneItem().then(() => { expect(notificationSpy).toHaveBeenCalled() - expect(notificationSpy.mostRecentCall.args[0].getType()).toBe('warning') - expect(notificationSpy.mostRecentCall.args[0].getMessage()).toContain('Unable to save') + expect(notificationSpy.mostRecentCall.args[0].getType()).toBe( + 'warning' + ) + expect( + notificationSpy.mostRecentCall.args[0].getMessage() + ).toContain('Unable to save') }) ) }) @@ -2576,15 +3098,21 @@ describe('Workspace', () => { waitsForPromise(() => atom.workspace.saveActivePaneItem().then(() => { expect(notificationSpy).toHaveBeenCalled() - expect(notificationSpy.mostRecentCall.args[0].getType()).toBe('warning') - expect(notificationSpy.mostRecentCall.args[0].getMessage()).toContain('Unable to save') + expect(notificationSpy.mostRecentCall.args[0].getType()).toBe( + 'warning' + ) + expect( + notificationSpy.mostRecentCall.args[0].getMessage() + ).toContain('Unable to save') }) ) }) it('emits a warning notification when the user does not have permission', () => { spyOn(editor, 'save').andCallFake(() => { - const error = new Error("EACCES, permission denied '/Some/dir/and-a-file.js'") + const error = new Error( + "EACCES, permission denied '/Some/dir/and-a-file.js'" + ) error.code = 'EACCES' error.path = '/Some/dir/and-a-file.js' throw error @@ -2593,15 +3121,21 @@ describe('Workspace', () => { waitsForPromise(() => atom.workspace.saveActivePaneItem().then(() => { expect(notificationSpy).toHaveBeenCalled() - expect(notificationSpy.mostRecentCall.args[0].getType()).toBe('warning') - expect(notificationSpy.mostRecentCall.args[0].getMessage()).toContain('Unable to save') + expect(notificationSpy.mostRecentCall.args[0].getType()).toBe( + 'warning' + ) + expect( + notificationSpy.mostRecentCall.args[0].getMessage() + ).toContain('Unable to save') }) ) }) it('emits a warning notification when the operation is not permitted', () => { spyOn(editor, 'save').andCallFake(() => { - const error = new Error("EPERM, operation not permitted '/Some/dir/and-a-file.js'") + const error = new Error( + "EPERM, operation not permitted '/Some/dir/and-a-file.js'" + ) error.code = 'EPERM' error.path = '/Some/dir/and-a-file.js' throw error @@ -2610,15 +3144,21 @@ describe('Workspace', () => { waitsForPromise(() => atom.workspace.saveActivePaneItem().then(() => { expect(notificationSpy).toHaveBeenCalled() - expect(notificationSpy.mostRecentCall.args[0].getType()).toBe('warning') - expect(notificationSpy.mostRecentCall.args[0].getMessage()).toContain('Unable to save') + expect(notificationSpy.mostRecentCall.args[0].getType()).toBe( + 'warning' + ) + expect( + notificationSpy.mostRecentCall.args[0].getMessage() + ).toContain('Unable to save') }) ) }) it('emits a warning notification when the file is already open by another app', () => { spyOn(editor, 'save').andCallFake(() => { - const error = new Error("EBUSY, resource busy or locked '/Some/dir/and-a-file.js'") + const error = new Error( + "EBUSY, resource busy or locked '/Some/dir/and-a-file.js'" + ) error.code = 'EBUSY' error.path = '/Some/dir/and-a-file.js' throw error @@ -2627,15 +3167,21 @@ describe('Workspace', () => { waitsForPromise(() => atom.workspace.saveActivePaneItem().then(() => { expect(notificationSpy).toHaveBeenCalled() - expect(notificationSpy.mostRecentCall.args[0].getType()).toBe('warning') - expect(notificationSpy.mostRecentCall.args[0].getMessage()).toContain('Unable to save') + expect(notificationSpy.mostRecentCall.args[0].getType()).toBe( + 'warning' + ) + expect( + notificationSpy.mostRecentCall.args[0].getMessage() + ).toContain('Unable to save') }) ) }) it('emits a warning notification when the file system is read-only', () => { spyOn(editor, 'save').andCallFake(() => { - const error = new Error("EROFS, read-only file system '/Some/dir/and-a-file.js'") + const error = new Error( + "EROFS, read-only file system '/Some/dir/and-a-file.js'" + ) error.code = 'EROFS' error.path = '/Some/dir/and-a-file.js' throw error @@ -2644,8 +3190,12 @@ describe('Workspace', () => { waitsForPromise(() => atom.workspace.saveActivePaneItem().then(() => { expect(notificationSpy).toHaveBeenCalled() - expect(notificationSpy.mostRecentCall.args[0].getType()).toBe('warning') - expect(notificationSpy.mostRecentCall.args[0].getMessage()).toContain('Unable to save') + expect(notificationSpy.mostRecentCall.args[0].getType()).toBe( + 'warning' + ) + expect( + notificationSpy.mostRecentCall.args[0].getMessage() + ).toContain('Unable to save') }) ) }) @@ -2655,7 +3205,7 @@ describe('Workspace', () => { throw new Error('no one knows') }) - waitsForPromise({shouldReject: true}, () => + waitsForPromise({ shouldReject: true }, () => atom.workspace.saveActivePaneItem() ) }) @@ -2672,7 +3222,7 @@ describe('Workspace', () => { atom.config.set('core.destroyEmptyPanes', false) const pane1 = atom.workspace.getActivePane() - const pane2 = pane1.splitRight({copyActiveItem: true}) + const pane2 = pane1.splitRight({ copyActiveItem: true }) expect(atom.workspace.getCenter().getPanes().length).toBe(2) expect(pane2.getItems().length).toBe(1) @@ -2819,13 +3369,25 @@ describe('Workspace', () => { expect(workspace.getVisiblePaneContainers()).toEqual([center]) leftDock.show() - expect(workspace.getVisiblePaneContainers().sort()).toEqual([center, leftDock]) + expect(workspace.getVisiblePaneContainers().sort()).toEqual([ + center, + leftDock + ]) rightDock.show() - expect(workspace.getVisiblePaneContainers().sort()).toEqual([center, leftDock, rightDock]) + expect(workspace.getVisiblePaneContainers().sort()).toEqual([ + center, + leftDock, + rightDock + ]) bottomDock.show() - expect(workspace.getVisiblePaneContainers().sort()).toEqual([center, leftDock, rightDock, bottomDock]) + expect(workspace.getVisiblePaneContainers().sort()).toEqual([ + center, + leftDock, + rightDock, + bottomDock + ]) }) }) @@ -2835,7 +3397,7 @@ describe('Workspace', () => { atom.config.set('core.allowPendingPaneItems', false) waitsForPromise(() => - atom.workspace.open('sample.js', {pending: true}).then(() => { + atom.workspace.open('sample.js', { pending: true }).then(() => { pane = atom.workspace.getActivePane() }) ) @@ -2852,9 +3414,18 @@ describe('Workspace', () => { const rubyGrammarUsed = jasmine.createSpy('ruby grammar used') const cGrammarUsed = jasmine.createSpy('c grammar used') - atom.packages.onDidTriggerActivationHook('language-javascript:grammar-used', javascriptGrammarUsed) - atom.packages.onDidTriggerActivationHook('language-ruby:grammar-used', rubyGrammarUsed) - atom.packages.onDidTriggerActivationHook('language-c:grammar-used', cGrammarUsed) + atom.packages.onDidTriggerActivationHook( + 'language-javascript:grammar-used', + javascriptGrammarUsed + ) + atom.packages.onDidTriggerActivationHook( + 'language-ruby:grammar-used', + rubyGrammarUsed + ) + atom.packages.onDidTriggerActivationHook( + 'language-c:grammar-used', + cGrammarUsed + ) await atom.packages.activatePackage('language-ruby') await atom.packages.activatePackage('language-javascript') @@ -2865,12 +3436,18 @@ describe('Workspace', () => { expect(javascriptGrammarUsed).toHaveBeenCalled() // Hooks are triggered when changing existing editors grammars - atom.grammars.assignLanguageMode(atom.workspace.getActiveTextEditor(), 'source.c') + atom.grammars.assignLanguageMode( + atom.workspace.getActiveTextEditor(), + 'source.c' + ) expect(cGrammarUsed).toHaveBeenCalled() // Hooks are triggered when editors are added in other ways. - atom.workspace.getActivePane().splitRight({copyActiveItem: true}) - atom.grammars.assignLanguageMode(atom.workspace.getActiveTextEditor(), 'source.ruby') + atom.workspace.getActivePane().splitRight({ copyActiveItem: true }) + atom.grammars.assignLanguageMode( + atom.workspace.getActiveTextEditor(), + 'source.ruby' + ) expect(rubyGrammarUsed).toHaveBeenCalled() }) }) @@ -2927,7 +3504,10 @@ describe('Workspace', () => { const dockPane = atom.workspace.getRightDock().getActivePane() spyOn(workspace.itemLocationStore, 'save') centerPane.moveItemToPane(item, dockPane) - expect(workspace.itemLocationStore.save).toHaveBeenCalledWith(ITEM_URI, 'right') + expect(workspace.itemLocationStore.save).toHaveBeenCalledWith( + ITEM_URI, + 'right' + ) }) it("clears the location if it's the default", () => { diff --git a/src/compile-cache.js b/src/compile-cache.js index f1f596e86..ea387a631 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -177,7 +177,7 @@ exports.install = function (resourcesPath, nodeRequire) { var rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1) try { - var sourceMap = JSON.parse(Buffer.from(rawData, 'base64')) + var sourceMap = JSON.parse(new Buffer(rawData, 'base64')) } catch (error) { console.warn('Error parsing source map', error.stack) return null diff --git a/src/file-system-blob-store.js b/src/file-system-blob-store.js index 81e4a6f39..67a959735 100644 --- a/src/file-system-blob-store.js +++ b/src/file-system-blob-store.js @@ -20,7 +20,7 @@ class FileSystemBlobStore { reset () { this.inMemoryBlobs = new Map() - this.storedBlob = Buffer.alloc(0) + this.storedBlob = new Buffer(0) this.storedBlobMap = {} this.usedKeys = new Set() } diff --git a/src/git-repository-provider.coffee b/src/git-repository-provider.coffee deleted file mode 100644 index 593324d0c..000000000 --- a/src/git-repository-provider.coffee +++ /dev/null @@ -1,84 +0,0 @@ -fs = require 'fs' -{Directory} = require 'pathwatcher' -GitRepository = require './git-repository' - -# Returns the .gitdir path in the agnostic Git symlink .git file given, or -# null if the path is not a valid gitfile. -# -# * `gitFile` {String} path of gitfile to parse -gitFileRegex = RegExp "^gitdir: (.+)" -pathFromGitFile = (gitFile) -> - try - gitFileBuff = fs.readFileSync(gitFile, 'utf8') - return gitFileBuff?.match(gitFileRegex)[1] - -# Checks whether a valid `.git` directory is contained within the given -# directory or one of its ancestors. If so, a Directory that corresponds to the -# `.git` folder will be returned. Otherwise, returns `null`. -# -# * `directory` {Directory} to explore whether it is part of a Git repository. -findGitDirectorySync = (directory) -> - # TODO: Fix node-pathwatcher/src/directory.coffee so the following methods - # can return cached values rather than always returning new objects: - # getParent(), getFile(), getSubdirectory(). - gitDir = directory.getSubdirectory('.git') - gitDirPath = pathFromGitFile(gitDir.getPath?()) - if gitDirPath - gitDir = new Directory(directory.resolve(gitDirPath)) - if gitDir.existsSync?() and isValidGitDirectorySync gitDir - gitDir - else if directory.isRoot() - return null - else - findGitDirectorySync directory.getParent() - -# Returns a boolean indicating whether the specified directory represents a Git -# repository. -# -# * `directory` {Directory} whose base name is `.git`. -isValidGitDirectorySync = (directory) -> - # To decide whether a directory has a valid .git folder, we use - # the heuristic adopted by the valid_repository_path() function defined in - # node_modules/git-utils/deps/libgit2/src/repository.c. - return directory.getSubdirectory('objects').existsSync() and - directory.getFile('HEAD').existsSync() and - directory.getSubdirectory('refs').existsSync() - -# Provider that conforms to the atom.repository-provider@0.1.0 service. -module.exports = -class GitRepositoryProvider - - constructor: (@project, @config) -> - # Keys are real paths that end in `.git`. - # Values are the corresponding GitRepository objects. - @pathToRepository = {} - - # Returns a {Promise} that resolves with either: - # * {GitRepository} if the given directory has a Git repository. - # * `null` if the given directory does not have a Git repository. - repositoryForDirectory: (directory) -> - # TODO: Currently, this method is designed to be async, but it relies on a - # synchronous API. It should be rewritten to be truly async. - Promise.resolve(@repositoryForDirectorySync(directory)) - - # Returns either: - # * {GitRepository} if the given directory has a Git repository. - # * `null` if the given directory does not have a Git repository. - repositoryForDirectorySync: (directory) -> - # Only one GitRepository should be created for each .git folder. Therefore, - # we must check directory and its parent directories to find the nearest - # .git folder. - gitDir = findGitDirectorySync(directory) - unless gitDir - return null - - gitDirPath = gitDir.getPath() - repo = @pathToRepository[gitDirPath] - unless repo - repo = GitRepository.open(gitDirPath, {@project, @config}) - return null unless repo - repo.onDidDestroy(=> delete @pathToRepository[gitDirPath]) - @pathToRepository[gitDirPath] = repo - repo.refreshIndex() - repo.refreshStatus() - repo diff --git a/src/git-repository-provider.js b/src/git-repository-provider.js new file mode 100644 index 000000000..9785a88ee --- /dev/null +++ b/src/git-repository-provider.js @@ -0,0 +1,180 @@ +const fs = require('fs') +const { Directory } = require('pathwatcher') +const GitRepository = require('./git-repository') + +const GIT_FILE_REGEX = RegExp('^gitdir: (.+)') + +// Returns the .gitdir path in the agnostic Git symlink .git file given, or +// null if the path is not a valid gitfile. +// +// * `gitFile` {String} path of gitfile to parse +function pathFromGitFileSync (gitFile) { + try { + const gitFileBuff = fs.readFileSync(gitFile, 'utf8') + return gitFileBuff != null ? gitFileBuff.match(GIT_FILE_REGEX)[1] : null + } catch (error) {} +} + +// Returns a {Promise} that resolves to the .gitdir path in the agnostic +// Git symlink .git file given, or null if the path is not a valid gitfile. +// +// * `gitFile` {String} path of gitfile to parse +function pathFromGitFile (gitFile) { + return new Promise(resolve => { + fs.readFile(gitFile, 'utf8', (err, gitFileBuff) => { + if (err == null && gitFileBuff != null) { + const result = gitFileBuff.toString().match(GIT_FILE_REGEX) + resolve(result != null ? result[1] : null) + } else { + resolve(null) + } + }) + }) +} + +// Checks whether a valid `.git` directory is contained within the given +// directory or one of its ancestors. If so, a Directory that corresponds to the +// `.git` folder will be returned. Otherwise, returns `null`. +// +// * `directory` {Directory} to explore whether it is part of a Git repository. +function findGitDirectorySync (directory) { + // TODO: Fix node-pathwatcher/src/directory.coffee so the following methods + // can return cached values rather than always returning new objects: + // getParent(), getFile(), getSubdirectory(). + let gitDir = directory.getSubdirectory('.git') + if (typeof gitDir.getPath === 'function') { + const gitDirPath = pathFromGitFileSync(gitDir.getPath()) + if (gitDirPath) { + gitDir = new Directory(directory.resolve(gitDirPath)) + } + } + if ( + typeof gitDir.existsSync === 'function' && + gitDir.existsSync() && + isValidGitDirectorySync(gitDir) + ) { + return gitDir + } else if (directory.isRoot()) { + return null + } else { + return findGitDirectorySync(directory.getParent()) + } +} + +// Checks whether a valid `.git` directory is contained within the given +// directory or one of its ancestors. If so, a Directory that corresponds to the +// `.git` folder will be returned. Otherwise, returns `null`. +// +// Returns a {Promise} that resolves to +// * `directory` {Directory} to explore whether it is part of a Git repository. +async function findGitDirectory (directory) { + // TODO: Fix node-pathwatcher/src/directory.coffee so the following methods + // can return cached values rather than always returning new objects: + // getParent(), getFile(), getSubdirectory(). + let gitDir = directory.getSubdirectory('.git') + if (typeof gitDir.getPath === 'function') { + const gitDirPath = await pathFromGitFile(gitDir.getPath()) + if (gitDirPath) { + gitDir = new Directory(directory.resolve(gitDirPath)) + } + } + if ( + typeof gitDir.exists === 'function' && + (await gitDir.exists()) && + isValidGitDirectory(gitDir) + ) { + return gitDir + } else if (directory.isRoot()) { + return null + } else { + return await findGitDirectory(directory.getParent()) + } +} + +// Returns a boolean indicating whether the specified directory represents a Git +// repository. +// +// * `directory` {Directory} whose base name is `.git`. +function isValidGitDirectorySync (directory) { + // To decide whether a directory has a valid .git folder, we use + // the heuristic adopted by the valid_repository_path() function defined in + // node_modules/git-utils/deps/libgit2/src/repository.c. + return ( + directory.getSubdirectory('objects').existsSync() && + directory.getFile('HEAD').existsSync() && + directory.getSubdirectory('refs').existsSync() + ) +} + +// Returns a {Promise} that resolves to a {Boolean} indicating whether the +// specified directory represents a Git repository. +// +// * `directory` {Directory} whose base name is `.git`. +async function isValidGitDirectory (directory) { + // To decide whether a directory has a valid .git folder, we use + // the heuristic adopted by the valid_repository_path() function defined in + // node_modules/git-utils/deps/libgit2/src/repository.c. + return ( + (await directory.getSubdirectory('objects').exists()) && + (await directory.getFile('HEAD').exists()) && + (await directory.getSubdirectory('refs').exists()) + ) +} + +// Provider that conforms to the atom.repository-provider@0.1.0 service. +class GitRepositoryProvider { + constructor (project, config) { + // Keys are real paths that end in `.git`. + // Values are the corresponding GitRepository objects. + this.project = project + this.config = config + this.pathToRepository = {} + } + + // Returns a {Promise} that resolves with either: + // * {GitRepository} if the given directory has a Git repository. + // * `null` if the given directory does not have a Git repository. + async repositoryForDirectory (directory) { + // Only one GitRepository should be created for each .git folder. Therefore, + // we must check directory and its parent directories to find the nearest + // .git folder. + const gitDir = await findGitDirectory(directory) + return this.repositoryForGitDirectory(gitDir) + } + + // Returns either: + // * {GitRepository} if the given directory has a Git repository. + // * `null` if the given directory does not have a Git repository. + repositoryForDirectorySync (directory) { + // Only one GitRepository should be created for each .git folder. Therefore, + // we must check directory and its parent directories to find the nearest + // .git folder. + const gitDir = findGitDirectorySync(directory) + return this.repositoryForGitDirectory(gitDir) + } + + // Returns either: + // * {GitRepository} if the given Git directory has a Git repository. + // * `null` if the given directory does not have a Git repository. + repositoryForGitDirectory (gitDir) { + if (!gitDir) { + return null + } + + const gitDirPath = gitDir.getPath() + let repo = this.pathToRepository[gitDirPath] + if (!repo) { + repo = GitRepository.open(gitDirPath, { project: this.project, config: this.config }) + if (!repo) { + return null + } + repo.onDidDestroy(() => delete this.pathToRepository[gitDirPath]) + this.pathToRepository[gitDirPath] = repo + repo.refreshIndex() + repo.refreshStatus() + } + return repo + } +} + +module.exports = GitRepositoryProvider diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index 4cbd02bfd..e80fc86a6 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -32,7 +32,17 @@ module.exports = ({blobStore}) -> {testRunnerPath, legacyTestRunnerPath, headless, logFile, testPaths, env} = getWindowLoadSettings() - unless headless + if headless + # Install console functions that output to stdout and stderr. + util = require 'util' + + Object.defineProperties process, + stdout: {value: remote.process.stdout} + stderr: {value: remote.process.stderr} + + console.log = (args...) -> process.stdout.write "#{util.format(args...)}\n" + console.error = (args...) -> process.stderr.write "#{util.format(args...)}\n" + else # Show window synchronously so a focusout doesn't fire on input elements # that are focused in the very first spec run. remote.getCurrentWindow().show() diff --git a/src/main-process/atom-application.js b/src/main-process/atom-application.js index 151845014..00fef2d6f 100644 --- a/src/main-process/atom-application.js +++ b/src/main-process/atom-application.js @@ -23,6 +23,17 @@ const ConfigSchema = require('../config-schema') const LocationSuffixRegExp = /(:\d+)(:\d+)?$/ +const getDefaultPath = () => { + const editor = atom.workspace.getActiveTextEditor() + if (!editor || !editor.getPath()) { + return + } + const paths = atom.project.getPaths() + if (paths) { + return paths[0] + } +} + // The application's singleton class. // // It's the entry point into the Atom application and maintains the global state @@ -392,6 +403,9 @@ class AtomApplication extends EventEmitter { this.on('application:check-for-update', () => this.autoUpdateManager.check()) if (process.platform === 'darwin') { + this.on('application:open', () => this.promptForPathToOpen('all', getLoadSettings(), getDefaultPath())) + this.on('application:open-file', () => this.promptForPathToOpen('file', getLoadSettings(), getDefaultPath())) + this.on('application:open-folder', () => this.promptForPathToOpen('folder', getLoadSettings(), getDefaultPath())) this.on('application:bring-all-windows-to-front', () => Menu.sendActionToFirstResponder('arrangeInFront:')) this.on('application:hide', () => Menu.sendActionToFirstResponder('hide:')) this.on('application:hide-other-applications', () => Menu.sendActionToFirstResponder('hideOtherApplications:')) diff --git a/src/native-compile-cache.js b/src/native-compile-cache.js index d8211b9f4..cc947e84b 100644 --- a/src/native-compile-cache.js +++ b/src/native-compile-cache.js @@ -1,7 +1,7 @@ const Module = require('module') const path = require('path') +const cachedVm = require('cached-run-in-this-context') const crypto = require('crypto') -const vm = require('vm') function computeHash (contents) { return crypto.createHash('sha1').update(contents, 'utf8').digest('hex') @@ -34,24 +34,6 @@ class NativeCompileCache { this.previousModuleCompile = Module.prototype._compile } - runInThisContext (code, filename) { - // produceCachedData is deprecated after Node 10.6, will need to update - // this for Electron 4.0 to use script.createCachedData() - const script = new vm.Script(code, {filename, produceCachedData: true}) - return { - result: script.runInThisContext(), - cacheBuffer: script.cachedData - } - } - - runInThisContextCached (code, filename, cachedData) { - const script = new vm.Script(code, {filename, cachedData}) - return { - result: script.runInThisContext(), - wasRejected: script.cachedDataRejected - } - } - overrideModuleCompile () { let self = this // Here we override Node's module.js @@ -82,7 +64,7 @@ class NativeCompileCache { let compiledWrapper = null if (self.cacheStore.has(cacheKey)) { let buffer = self.cacheStore.get(cacheKey) - let compilationResult = self.runInThisContextCached(wrapper, filename, buffer) + let compilationResult = cachedVm.runInThisContextCached(wrapper, filename, buffer) compiledWrapper = compilationResult.result if (compilationResult.wasRejected) { self.cacheStore.delete(cacheKey) @@ -90,12 +72,12 @@ class NativeCompileCache { } else { let compilationResult try { - compilationResult = self.runInThisContext(wrapper, filename) + compilationResult = cachedVm.runInThisContext(wrapper, filename) } catch (err) { console.error(`Error running script ${filename}`) throw err } - if (compilationResult.cacheBuffer !== null) { + if (compilationResult.cacheBuffer) { self.cacheStore.set(cacheKey, compilationResult.cacheBuffer) } compiledWrapper = compilationResult.result diff --git a/src/task.coffee b/src/task.coffee index 05d61f5a7..fa09c69f1 100644 --- a/src/task.coffee +++ b/src/task.coffee @@ -84,17 +84,17 @@ class Task # Routes messages from the child to the appropriate event. handleEvents: -> - @childProcess.removeAllListeners('message') + @childProcess.removeAllListeners() @childProcess.on 'message', ({event, args}) => @emitter.emit(event, args) if @childProcess? # Catch the errors that happened before task-bootstrap. if @childProcess.stdout? - @childProcess.stdout.removeAllListeners('data') + @childProcess.stdout.removeAllListeners() @childProcess.stdout.on 'data', (data) -> console.log data.toString() if @childProcess.stderr? - @childProcess.stderr.removeAllListeners('data') + @childProcess.stderr.removeAllListeners() @childProcess.stderr.on 'data', (data) -> console.error data.toString() # Public: Starts the task. @@ -147,9 +147,9 @@ class Task terminate: -> return false unless @childProcess? - @childProcess.removeAllListeners('message') - @childProcess.stdout?.removeAllListeners('data') - @childProcess.stderr?.removeAllListeners('data') + @childProcess.removeAllListeners() + @childProcess.stdout?.removeAllListeners() + @childProcess.stderr?.removeAllListeners() @childProcess.kill() @childProcess = null diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 9da3db137..2f24c0df5 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -4446,7 +4446,7 @@ class NodePool { if (element) { element.className = className || '' - element.attributeStyleMap.forEach((value, key) => { + element.styleMap.forEach((value, key) => { if (!style || style[key] == null) element.style[key] = '' }) if (style) Object.assign(element.style, style) diff --git a/static/index.js b/static/index.js index 40840f304..7a7513683 100644 --- a/static/index.js +++ b/static/index.js @@ -1,4 +1,8 @@ (function () { + // Eagerly require cached-run-in-this-context to prevent a circular require + // when using `NativeCompileCache` for the first time. + require('cached-run-in-this-context') + const electron = require('electron') const path = require('path') const Module = require('module')