diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2671f9a53e..6e6c873389 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -810,7 +810,13 @@ steps-tests: &steps-tests
- run:
name: Check test results existence
command: |
- MOCHA_FILE='src/junit/test-results.xml'
+ MOCHA_FILE='src/junit/test-results-remote.xml'
+ # Check if it exists and not empty.
+ if [ ! -s "$MOCHA_FILE" ]; then
+ exit 1
+ fi
+
+ MOCHA_FILE='src/junit/test-results-main.xml'
# Check if it exists and not empty.
if [ ! -s "$MOCHA_FILE" ]; then
exit 1
diff --git a/package-lock.json b/package-lock.json
index cf185f4ae1..91a8122a82 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -155,12 +155,52 @@
"any-observable": "^0.3.0"
}
},
+ "@types/chai": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz",
+ "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==",
+ "dev": true
+ },
+ "@types/chai-as-promised": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.0.tgz",
+ "integrity": "sha512-MFiW54UOSt+f2bRw8J7LgQeIvE/9b4oGvwU7XW30S9QGAiHGnU/fmiOprsyMkdmH2rl8xSPc0/yrQw8juXU6bQ==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "*"
+ }
+ },
+ "@types/mocha": {
+ "version": "5.2.6",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.6.tgz",
+ "integrity": "sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw==",
+ "dev": true
+ },
"@types/node": {
"version": "10.12.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.21.tgz",
"integrity": "sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==",
"dev": true
},
+ "@types/split": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/split/-/split-1.0.0.tgz",
+ "integrity": "sha512-pm9S1mkr+av0j7D6pFyqhBxXDbnbO9gqj4nb8DtGtCewvj0XhIv089SSwXrjrIizT1UquO8/h83hCut0pa3u8A==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/through": "*"
+ }
+ },
+ "@types/through": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.29.tgz",
+ "integrity": "sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@typescript-eslint/eslint-plugin": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.4.2.tgz",
@@ -4677,28 +4717,27 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true,
- "optional": true
+ "dev": true
},
"aproba": {
"version": "1.2.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"dev": true,
"optional": true,
@@ -4709,17 +4748,15 @@
},
"balanced-match": {
"version": "1.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true,
- "optional": true
+ "dev": true
},
"brace-expansion": {
"version": "1.1.11",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -4727,42 +4764,39 @@
},
"chownr": {
"version": "1.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
- "dev": true,
- "optional": true
+ "dev": true
},
"concat-map": {
"version": "0.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true,
- "optional": true
+ "dev": true
},
"console-control-strings": {
"version": "1.1.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
- "dev": true,
- "optional": true
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true,
"optional": true
},
"debug": {
"version": "2.6.9",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"optional": true,
@@ -4772,28 +4806,28 @@
},
"deep-extend": {
"version": "0.5.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
"dev": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"dev": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"dev": true,
"optional": true,
@@ -4803,14 +4837,14 @@
},
"fs.realpath": {
"version": "1.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"optional": true,
@@ -4827,7 +4861,7 @@
},
"glob": {
"version": "7.1.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"optional": true,
@@ -4842,14 +4876,14 @@
},
"has-unicode": {
"version": "2.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
"dev": true,
"optional": true,
@@ -4859,7 +4893,7 @@
},
"ignore-walk": {
"version": "3.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"dev": true,
"optional": true,
@@ -4869,7 +4903,7 @@
},
"inflight": {
"version": "1.0.6",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"optional": true,
@@ -4880,58 +4914,53 @@
},
"inherits": {
"version": "2.0.3",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
- "dev": true,
- "optional": true
+ "dev": true
},
"ini": {
"version": "1.3.5",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true,
- "optional": true
+ "dev": true
},
"minipass": {
"version": "2.2.4",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
"dev": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -4939,7 +4968,7 @@
},
"minizlib": {
"version": "1.1.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
"dev": true,
"optional": true,
@@ -4949,24 +4978,23 @@
},
"mkdirp": {
"version": "0.5.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true,
"optional": true
},
"needle": {
"version": "2.2.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
"dev": true,
"optional": true,
@@ -4978,7 +5006,7 @@
},
"node-pre-gyp": {
"version": "0.10.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
"dev": true,
"optional": true,
@@ -4997,7 +5025,7 @@
},
"nopt": {
"version": "4.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"dev": true,
"optional": true,
@@ -5008,14 +5036,14 @@
},
"npm-bundled": {
"version": "1.0.3",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
"dev": true,
"optional": true,
@@ -5026,7 +5054,7 @@
},
"npmlog": {
"version": "4.1.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"optional": true,
@@ -5039,45 +5067,43 @@
},
"number-is-nan": {
"version": "1.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
- "dev": true,
- "optional": true
+ "dev": true
},
"object-assign": {
"version": "4.1.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"optional": true,
@@ -5088,21 +5114,21 @@
},
"path-is-absolute": {
"version": "1.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.7",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
"dev": true,
"optional": true,
@@ -5115,7 +5141,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true,
"optional": true
@@ -5124,7 +5150,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"optional": true,
@@ -5140,7 +5166,7 @@
},
"rimraf": {
"version": "2.6.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"dev": true,
"optional": true,
@@ -5150,52 +5176,50 @@
},
"safe-buffer": {
"version": "5.1.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
- "dev": true,
- "optional": true
+ "dev": true
},
"safer-buffer": {
"version": "2.1.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"optional": true
},
"sax": {
"version": "1.2.4",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true,
"optional": true
},
"semver": {
"version": "5.5.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -5204,7 +5228,7 @@
},
"string_decoder": {
"version": "1.1.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"optional": true,
@@ -5214,24 +5238,23 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.1",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
"dev": true,
"optional": true,
@@ -5247,14 +5270,14 @@
},
"util-deprecate": {
"version": "1.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true,
"optional": true
},
"wide-align": {
"version": "1.1.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
"dev": true,
"optional": true,
@@ -5264,17 +5287,15 @@
},
"wrappy": {
"version": "1.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true,
- "optional": true
+ "dev": true
},
"yallist": {
"version": "3.0.2",
- "resolved": false,
+ "resolved": "",
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
- "dev": true,
- "optional": true
+ "dev": true
}
}
},
@@ -7787,6 +7808,12 @@
}
}
},
+ "make-error": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
+ "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
+ "dev": true
+ },
"map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@@ -11197,6 +11224,24 @@
"urix": "^0.1.0"
}
},
+ "source-map-support": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
+ "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
"source-map-url": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
@@ -12584,6 +12629,30 @@
"integrity": "sha512-FHkoUZvG6Egrv9XZAyYGKEyb1JMsFphgPjoczkZC2y6W93U1jswcVURB8MUvtsahEPEVACyxD47JAL63vF4JsQ==",
"dev": true
},
+ "ts-node": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-6.2.0.tgz",
+ "integrity": "sha512-ZNT+OEGfUNVMGkpIaDJJ44Zq3Yr0bkU/ugN1PHbU+/01Z7UV1fsELRiTx1KuQNvQ1A3pGh3y25iYF6jXgxV21A==",
+ "dev": true,
+ "requires": {
+ "arrify": "^1.0.0",
+ "buffer-from": "^1.1.0",
+ "diff": "^3.1.0",
+ "make-error": "^1.1.1",
+ "minimist": "^1.2.0",
+ "mkdirp": "^0.5.1",
+ "source-map-support": "^0.5.6",
+ "yn": "^2.0.0"
+ },
+ "dependencies": {
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ }
+ }
+ },
"tsconfig": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz",
@@ -13614,6 +13683,12 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
"dev": true
+ },
+ "yn": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
+ "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
+ "dev": true
}
}
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index b3fcaf17db..11b83448c7 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,11 @@
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
"@octokit/rest": "^16.3.2",
+ "@types/chai": "^4.1.7",
+ "@types/chai-as-promised": "^7.1.0",
+ "@types/mocha": "^5.2.6",
"@types/node": "^10.12.21",
+ "@types/split": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^1.4.2",
"@typescript-eslint/parser": "^1.4.2",
"aliasify": "^2.1.0",
@@ -42,6 +46,7 @@
"standard-markdown": "^5.0.0",
"sumchecker": "^2.0.2",
"temp": "^0.8.3",
+ "ts-node": "^6.0.3",
"tsify": "^4.0.1",
"typescript": "~3.3.3333"
},
@@ -71,7 +76,7 @@
"prepush": "check-for-leaks",
"repl": "node ./script/start.js --interactive",
"start": "node ./script/start.js",
- "test": "node ./script/spec-runner.js electron/spec",
+ "test": "node ./script/spec-runner.js",
"tsc": "tsc"
},
"license": "MIT",
@@ -115,4 +120,4 @@
"git add filenames.auto.gni"
]
}
-}
\ No newline at end of file
+}
diff --git a/script/spec-runner.js b/script/spec-runner.js
index d6750db499..50af2359c8 100755
--- a/script/spec-runner.js
+++ b/script/spec-runner.js
@@ -5,6 +5,12 @@ const crypto = require('crypto')
const fs = require('fs')
const { hashElement } = require('folder-hash')
const path = require('path')
+const unknownArgs = []
+
+const args = require('minimist')(process.argv, {
+ string: ['runners'],
+ unknown: arg => unknownArgs.push(arg)
+})
const utils = require('./lib/utils')
@@ -13,6 +19,14 @@ const NPM_CMD = process.platform === 'win32' ? 'npm.cmd' : 'npm'
const specHashPath = path.resolve(__dirname, '../spec/.hash')
+let runnersToRun = null
+if (args.runners) {
+ runnersToRun = args.runners.split(',')
+ console.log('Only running:', runnersToRun)
+} else {
+ console.log('Will trigger all spec runners')
+}
+
async function main () {
const [lastSpecHash, lastSpecInstallHash] = loadLastSpecHash()
const [currentSpecHash, currentSpecInstallHash] = await getSpecHash()
@@ -24,9 +38,24 @@ async function main () {
await getSpecHash().then(saveSpecHash)
}
+ if (!fs.existsSync(path.resolve(__dirname, '../electron.d.ts'))) {
+ console.log('Generating electron.d.ts as it is missing')
+ generateTypeDefinitions()
+ }
+
await runElectronTests()
}
+function generateTypeDefinitions () {
+ const { status } = childProcess.spawnSync('npm', ['run', 'create-typescript-definitions'], {
+ cwd: path.resolve(__dirname, '..'),
+ stdio: 'inherit'
+ })
+ if (status !== 0) {
+ throw new Error(`Electron typescript definition generation failed with exit code: ${status}.`)
+ }
+}
+
function loadLastSpecHash () {
return fs.existsSync(specHashPath)
? fs.readFileSync(specHashPath, 'utf8').split('\n')
@@ -38,14 +67,59 @@ function saveSpecHash ([newSpecHash, newSpecInstallHash]) {
}
async function runElectronTests () {
+ const errors = []
+ const runners = [
+ ['Remote based specs', 'remote', runRemoteBasedElectronTests],
+ ['Main process specs', 'main', runMainProcessElectronTests]
+ ]
+
+ const mochaFile = process.env.MOCHA_FILE
+ for (const runner of runners) {
+ if (runnersToRun && !runnersToRun.includes(runner[1])) {
+ console.info('\nSkipping:', runner[0])
+ continue
+ }
+ try {
+ console.info('\nRunning:', runner[0])
+ if (mochaFile) {
+ process.env.MOCHA_FILE = mochaFile.replace('.xml', `-${runner[1]}.xml`)
+ }
+ await runner[2]()
+ } catch (err) {
+ errors.push([runner[0], err])
+ }
+ }
+
+ if (errors.length !== 0) {
+ for (const err of errors) {
+ console.error('\n\nRunner Failed:', err[0])
+ console.error(err[1])
+ }
+ throw new Error('Electron test runners have failed')
+ }
+}
+
+async function runRemoteBasedElectronTests () {
let exe = path.resolve(BASE, utils.getElectronExec())
- const args = process.argv.slice(2)
+ const runnerArgs = ['electron/spec', ...unknownArgs.slice(2)]
if (process.platform === 'linux') {
- args.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe)
+ runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe)
exe = 'python'
}
- const { status } = childProcess.spawnSync(exe, args, {
+ const { status } = childProcess.spawnSync(exe, runnerArgs, {
+ cwd: path.resolve(__dirname, '../..'),
+ stdio: 'inherit'
+ })
+ if (status !== 0) {
+ throw new Error(`Electron tests failed with code ${status}.`)
+ }
+}
+
+async function runMainProcessElectronTests () {
+ const exe = path.resolve(BASE, utils.getElectronExec())
+
+ const { status } = childProcess.spawnSync(exe, ['electron/spec-main', ...unknownArgs.slice(2)], {
cwd: path.resolve(__dirname, '../..'),
stdio: 'inherit'
})
diff --git a/spec-main/.eslintrc b/spec-main/.eslintrc
new file mode 100644
index 0000000000..a26475e27d
--- /dev/null
+++ b/spec-main/.eslintrc
@@ -0,0 +1,20 @@
+{
+ "env": {
+ "browser": true,
+ "mocha": true,
+ "jquery": true,
+ "serviceworker": true
+ },
+ "globals": {
+ "Bindings": true,
+ "Components": true,
+ "UI": true,
+ "WebView": true
+ },
+ "plugins": [
+ "mocha"
+ ],
+ "rules": {
+ "mocha/no-exclusive-tests": "error"
+ }
+}
diff --git a/spec-main/ambient.d.ts b/spec-main/ambient.d.ts
new file mode 100644
index 0000000000..5e6e6eff8d
--- /dev/null
+++ b/spec-main/ambient.d.ts
@@ -0,0 +1 @@
+declare var isCI: boolean;
diff --git a/spec/api-app-spec.js b/spec-main/api-app-spec.ts
similarity index 76%
rename from spec/api-app-spec.js
rename to spec-main/api-app-spec.ts
index 999f2eca3f..710ec7ae86 100644
--- a/spec/api-app-spec.js
+++ b/spec-main/api-app-spec.ts
@@ -1,24 +1,20 @@
-const chai = require('chai')
-const chaiAsPromised = require('chai-as-promised')
-const dirtyChai = require('dirty-chai')
-const ChildProcess = require('child_process')
-const https = require('https')
-const net = require('net')
-const fs = require('fs')
-const path = require('path')
-const cp = require('child_process')
-const split = require('split')
-const { ipcRenderer, remote } = require('electron')
-const { emittedOnce } = require('./events-helpers')
-const { closeWindow } = require('./window-helpers')
+import * as chai from 'chai'
+import * as chaiAsPromised from 'chai-as-promised'
+import * as cp from 'child_process'
+import * as https from 'https'
+import * as net from 'net'
+import * as fs from 'fs'
+import * as path from 'path'
+import split = require('split')
+import { app, BrowserWindow, Menu } from 'electron'
+import { emittedOnce } from './events-helpers';
+import { closeWindow } from './window-helpers';
const { expect } = chai
-const { app, BrowserWindow, Menu, ipcMain } = remote
-
-const isCI = remote.getGlobal('isCi')
chai.use(chaiAsPromised)
-chai.use(dirtyChai)
+
+const fixturesPath = path.resolve(__dirname, '../spec/fixtures')
describe('electron module', () => {
it('does not expose internal modules to require', () => {
@@ -28,33 +24,16 @@ describe('electron module', () => {
})
describe('require("electron")', () => {
- let window = null
-
- beforeEach(() => {
- window = new BrowserWindow({
- show: false,
- width: 400,
- height: 400,
- webPreferences: {
- nodeIntegration: true
- }
- })
- })
-
- afterEach(() => {
- return closeWindow(window).then(() => { window = null })
- })
-
- it('always returns the internal electron module', (done) => {
- ipcMain.once('answer', () => done())
- window.loadFile(path.join(__dirname, 'fixtures', 'api', 'electron-module-app', 'index.html'))
+ it('always returns the internal electron module', () => {
+ require('electron')
})
})
})
describe('app module', () => {
- let server, secureUrl
- const certPath = path.join(__dirname, 'fixtures', 'certificates')
+ let server: https.Server
+ let secureUrl: string
+ const certPath = path.join(fixturesPath, 'certificates')
before((done) => {
const options = {
@@ -69,7 +48,7 @@ describe('app module', () => {
}
server = https.createServer(options, (req, res) => {
- if (req.client.authorized) {
+ if ((req as any).client.authorized) {
res.writeHead(200)
res.end('
authorized')
} else {
@@ -79,7 +58,7 @@ describe('app module', () => {
})
server.listen(0, '127.0.0.1', () => {
- const port = server.address().port
+ const port = (server.address() as net.AddressInfo).port
secureUrl = `https://127.0.0.1:${port}`
done()
})
@@ -107,13 +86,13 @@ describe('app module', () => {
describe('app.getName()', () => {
it('returns the name field of package.json', () => {
- expect(app.getName()).to.equal('Electron Test')
+ expect(app.getName()).to.equal('Electron Test Main')
})
})
describe('app.setName(name)', () => {
it('overrides the name', () => {
- expect(app.getName()).to.equal('Electron Test')
+ expect(app.getName()).to.equal('Electron Test Main')
app.setName('test-name')
expect(app.getName()).to.equal('test-name')
@@ -123,7 +102,7 @@ describe('app module', () => {
describe('app.getLocale()', () => {
it('should not be empty', () => {
- expect(app.getLocale()).to.not.be.empty()
+ expect(app.getLocale()).to.not.equal('')
})
})
@@ -140,7 +119,7 @@ describe('app module', () => {
describe('app.isPackaged', () => {
it('should be false durings tests', () => {
- expect(app.isPackaged).to.be.false()
+ expect(app.isPackaged).to.equal(false)
})
})
@@ -152,23 +131,23 @@ describe('app module', () => {
})
it('should be false during tests', () => {
- expect(app.isInApplicationsFolder()).to.be.false()
+ expect(app.isInApplicationsFolder()).to.equal(false)
})
})
describe('app.exit(exitCode)', () => {
- let appProcess = null
+ let appProcess: cp.ChildProcess | null = null
afterEach(() => {
- if (appProcess != null) appProcess.kill()
+ if (appProcess) appProcess.kill()
})
it('emits a process exit event with the code', async () => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app')
- const electronPath = remote.getGlobal('process').execPath
+ const appPath = path.join(fixturesPath, 'api', 'quit-app')
+ const electronPath = process.execPath
let output = ''
- appProcess = ChildProcess.spawn(electronPath, [appPath])
+ appProcess = cp.spawn(electronPath, [appPath])
appProcess.stdout.on('data', data => { output += data })
const [code] = await emittedOnce(appProcess, 'close')
@@ -179,10 +158,10 @@ describe('app module', () => {
})
it('closes all windows', async function () {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'exit-closes-all-windows-app')
- const electronPath = remote.getGlobal('process').execPath
+ const appPath = path.join(fixturesPath, 'api', 'exit-closes-all-windows-app')
+ const electronPath = process.execPath
- appProcess = ChildProcess.spawn(electronPath, [appPath])
+ appProcess = cp.spawn(electronPath, [appPath])
const [code, signal] = await emittedOnce(appProcess, 'close')
expect(signal).to.equal(null, 'exit signal should be null, if you see this please tag @MarshallOfSound')
@@ -195,32 +174,32 @@ describe('app module', () => {
return
}
- const electronPath = remote.getGlobal('process').execPath
- const appPath = path.join(__dirname, 'fixtures', 'api', 'singleton')
- appProcess = ChildProcess.spawn(electronPath, [appPath])
+ const electronPath = process.execPath
+ const appPath = path.join(fixturesPath, 'api', 'singleton')
+ appProcess = cp.spawn(electronPath, [appPath])
// Singleton will send us greeting data to let us know it's running.
// After that, ask it to exit gracefully and confirm that it does.
- appProcess.stdout.on('data', data => appProcess.kill())
+ appProcess.stdout.on('data', data => appProcess!.kill())
const [code, signal] = await emittedOnce(appProcess, 'close')
const message = `code:\n${code}\nsignal:\n${signal}`
expect(code).to.equal(0, message)
- expect(signal).to.be.null(message)
+ expect(signal).to.equal(null, message)
})
})
describe('app.requestSingleInstanceLock', () => {
it('prevents the second launch of app', function (done) {
this.timeout(120000)
- const appPath = path.join(__dirname, 'fixtures', 'api', 'singleton')
- const first = ChildProcess.spawn(remote.process.execPath, [appPath])
+ const appPath = path.join(fixturesPath, 'api', 'singleton')
+ const first = cp.spawn(process.execPath, [appPath])
first.once('exit', code => {
expect(code).to.equal(0)
})
// Start second app when received output.
first.stdout.once('data', () => {
- const second = ChildProcess.spawn(remote.process.execPath, [appPath])
+ const second = cp.spawn(process.execPath, [appPath])
second.once('exit', code => {
expect(code).to.equal(1)
done()
@@ -229,8 +208,8 @@ describe('app module', () => {
})
it('passes arguments to the second-instance event', async () => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'singleton')
- const first = ChildProcess.spawn(remote.process.execPath, [appPath])
+ const appPath = path.join(fixturesPath, 'api', 'singleton')
+ const first = cp.spawn(process.execPath, [appPath])
const firstExited = emittedOnce(first, 'exit')
// Wait for the first app to boot.
@@ -240,16 +219,16 @@ describe('app module', () => {
}
const data2Promise = emittedOnce(firstStdoutLines, 'data')
- const secondInstanceArgs = [remote.process.execPath, appPath, '--some-switch', 'some-arg']
- const second = ChildProcess.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1))
+ const secondInstanceArgs = [process.execPath, appPath, '--some-switch', 'some-arg']
+ const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1))
const [code2] = await emittedOnce(second, 'exit')
expect(code2).to.equal(1)
const [code1] = await firstExited
expect(code1).to.equal(0)
- const data2 = (await data2Promise).toString('ascii')
- const secondInstanceArgsReceived = JSON.parse(data2.toString('ascii'))
+ const data2 = (await data2Promise)[0].toString('ascii')
+ const secondInstanceArgsReceived: string[] = JSON.parse(data2.toString('ascii'))
const expected = process.platform === 'win32'
- ? [remote.process.execPath, '--some-switch', '--allow-file-access-from-files', secondInstanceArgsReceived.find(x => x.includes('original-process-start-time')), appPath, 'some-arg']
+ ? [process.execPath, '--some-switch', '--allow-file-access-from-files', secondInstanceArgsReceived.find(x => x.includes('original-process-start-time')), appPath, 'some-arg']
: secondInstanceArgs
expect(secondInstanceArgsReceived).to.eql(expected,
`expected ${JSON.stringify(expected)} but got ${data2.toString('ascii')}`)
@@ -257,7 +236,7 @@ describe('app module', () => {
})
describe('app.relaunch', () => {
- let server = null
+ let server: net.Server | null = null
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'
beforeEach(done => {
@@ -269,7 +248,7 @@ describe('app module', () => {
})
afterEach((done) => {
- server.close(() => {
+ server!.close(() => {
if (process.platform === 'win32') {
done()
} else {
@@ -282,8 +261,8 @@ describe('app module', () => {
this.timeout(120000)
let state = 'none'
- server.once('error', error => done(error))
- server.on('connection', client => {
+ server!.once('error', error => done(error))
+ server!.on('connection', client => {
client.once('data', data => {
if (String(data) === 'false' && state === 'none') {
state = 'first-launch'
@@ -295,8 +274,8 @@ describe('app module', () => {
})
})
- const appPath = path.join(__dirname, 'fixtures', 'api', 'relaunch')
- ChildProcess.spawn(remote.process.execPath, [appPath])
+ const appPath = path.join(fixturesPath, 'api', 'relaunch')
+ cp.spawn(process.execPath, [appPath])
})
})
@@ -313,61 +292,61 @@ describe('app module', () => {
})
})
- xdescribe('app.importCertificate', () => {
- let w = null
+ // xdescribe('app.importCertificate', () => {
+ // let w = null
- before(function () {
- if (process.platform !== 'linux') {
- this.skip()
- }
- })
+ // before(function () {
+ // if (process.platform !== 'linux') {
+ // this.skip()
+ // }
+ // })
- afterEach(() => closeWindow(w).then(() => { w = null }))
+ // afterEach(() => closeWindow(w).then(() => { w = null }))
- it('can import certificate into platform cert store', done => {
- const options = {
- certificate: path.join(certPath, 'client.p12'),
- password: 'electron'
- }
+ // it('can import certificate into platform cert store', done => {
+ // const options = {
+ // certificate: path.join(certPath, 'client.p12'),
+ // password: 'electron'
+ // }
- w = new BrowserWindow({
- show: false,
- webPreferences: {
- nodeIntegration: true
- }
- })
+ // w = new BrowserWindow({
+ // show: false,
+ // webPreferences: {
+ // nodeIntegration: true
+ // }
+ // })
- w.webContents.on('did-finish-load', () => {
- expect(w.webContents.getTitle()).to.equal('authorized')
- done()
- })
+ // w.webContents.on('did-finish-load', () => {
+ // expect(w.webContents.getTitle()).to.equal('authorized')
+ // done()
+ // })
- ipcRenderer.once('select-client-certificate', (event, webContentsId, list) => {
- expect(webContentsId).to.equal(w.webContents.id)
- expect(list).to.have.lengthOf(1)
+ // ipcRenderer.once('select-client-certificate', (event, webContentsId, list) => {
+ // expect(webContentsId).to.equal(w.webContents.id)
+ // expect(list).to.have.lengthOf(1)
- expect(list[0]).to.deep.equal({
- issuerName: 'Intermediate CA',
- subjectName: 'Client Cert',
- issuer: { commonName: 'Intermediate CA' },
- subject: { commonName: 'Client Cert' }
- })
+ // expect(list[0]).to.deep.equal({
+ // issuerName: 'Intermediate CA',
+ // subjectName: 'Client Cert',
+ // issuer: { commonName: 'Intermediate CA' },
+ // subject: { commonName: 'Client Cert' }
+ // })
- event.sender.send('client-certificate-response', list[0])
- })
+ // event.sender.send('client-certificate-response', list[0])
+ // })
- app.importCertificate(options, result => {
- expect(result).toNotExist()
- ipcRenderer.sendSync('set-client-certificate-option', false)
- w.loadURL(secureUrl)
- })
- })
- })
+ // app.importCertificate(options, result => {
+ // expect(result).toNotExist()
+ // ipcRenderer.sendSync('set-client-certificate-option', false)
+ // w.loadURL(secureUrl)
+ // })
+ // })
+ // })
describe('BrowserWindow events', () => {
- let w = null
+ let w: BrowserWindow = null as any
- afterEach(() => closeWindow(w).then(() => { w = null }))
+ afterEach(() => closeWindow(w).then(() => { w = null as any }))
it('should emit browser-window-focus event when window is focused', (done) => {
app.once('browser-window-focus', (e, window) => {
@@ -530,7 +509,7 @@ describe('app module', () => {
const platformIsSupported = !platformIsNotSupported
const expectedBadgeCount = 42
- let returnValue = null
+ let returnValue: boolean | null = null
beforeEach(() => { returnValue = app.setBadgeCount(expectedBadgeCount) })
@@ -547,7 +526,7 @@ describe('app module', () => {
})
it('returns true', () => {
- expect(returnValue).to.be.true()
+ expect(returnValue).to.equal(true)
})
it('sets a badge count', () => {
@@ -563,7 +542,7 @@ describe('app module', () => {
})
it('returns false', () => {
- expect(returnValue).to.be.false()
+ expect(returnValue).to.equal(false)
})
it('does not set a badge count', () => {
@@ -618,28 +597,28 @@ describe('app module', () => {
})
it('correctly sets and unsets the LoginItem', function () {
- expect(app.getLoginItemSettings().openAtLogin).to.be.false()
+ expect(app.getLoginItemSettings().openAtLogin).to.equal(false)
app.setLoginItemSettings({ openAtLogin: true })
- expect(app.getLoginItemSettings().openAtLogin).to.be.true()
+ expect(app.getLoginItemSettings().openAtLogin).to.equal(true)
app.setLoginItemSettings({ openAtLogin: false })
- expect(app.getLoginItemSettings().openAtLogin).to.be.false()
+ expect(app.getLoginItemSettings().openAtLogin).to.equal(false)
})
it('correctly sets and unsets the LoginItem as hidden', function () {
if (process.platform !== 'darwin') this.skip()
- expect(app.getLoginItemSettings().openAtLogin).to.be.false()
- expect(app.getLoginItemSettings().openAsHidden).to.be.false()
+ expect(app.getLoginItemSettings().openAtLogin).to.equal(false)
+ expect(app.getLoginItemSettings().openAsHidden).to.equal(false)
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true })
- expect(app.getLoginItemSettings().openAtLogin).to.be.true()
- expect(app.getLoginItemSettings().openAsHidden).to.be.true()
+ expect(app.getLoginItemSettings().openAtLogin).to.equal(true)
+ expect(app.getLoginItemSettings().openAsHidden).to.equal(true)
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false })
- expect(app.getLoginItemSettings().openAtLogin).to.be.true()
- expect(app.getLoginItemSettings().openAsHidden).to.be.false()
+ expect(app.getLoginItemSettings().openAtLogin).to.equal(true)
+ expect(app.getLoginItemSettings().openAsHidden).to.equal(false)
})
it('allows you to pass a custom executable and arguments', function () {
@@ -647,11 +626,11 @@ describe('app module', () => {
app.setLoginItemSettings({ openAtLogin: true, path: updateExe, args: processStartArgs })
- expect(app.getLoginItemSettings().openAtLogin).to.be.false()
+ expect(app.getLoginItemSettings().openAtLogin).to.equal(false)
expect(app.getLoginItemSettings({
path: updateExe,
args: processStartArgs
- }).openAtLogin).to.be.true()
+ }).openAtLogin).to.equal(true)
})
})
@@ -684,7 +663,7 @@ describe('app module', () => {
})
describe('select-client-certificate event', () => {
- let w = null
+ let w: BrowserWindow
before(function () {
if (process.platform === 'linux') {
@@ -702,16 +681,16 @@ describe('app module', () => {
})
})
- afterEach(() => closeWindow(w).then(() => { w = null }))
+ afterEach(() => closeWindow(w).then(() => { w = null as any }))
- it('can respond with empty certificate list', done => {
- w.webContents.on('did-finish-load', () => {
- expect(w.webContents.getTitle()).to.equal('denied')
- done()
+ it('can respond with empty certificate list', async () => {
+ app.once('select-client-certificate', function (event, webContents, url, list, callback) {
+ console.log('select-client-certificate emitted')
+ event.preventDefault()
+ callback()
})
-
- ipcRenderer.sendSync('set-client-certificate-option', true)
- w.webContents.loadURL(secureUrl)
+ await w.webContents.loadURL(secureUrl)
+ expect(w.webContents.getTitle()).to.equal('denied')
})
})
@@ -723,8 +702,8 @@ describe('app module', () => {
'--process-start-args', `"--hidden"`
]
- let Winreg
- let classesKey
+ let Winreg: any
+ let classesKey: any
before(function () {
if (process.platform !== 'win32') {
@@ -761,34 +740,34 @@ describe('app module', () => {
afterEach(() => {
app.removeAsDefaultProtocolClient(protocol)
- expect(app.isDefaultProtocolClient(protocol)).to.be.false()
+ expect(app.isDefaultProtocolClient(protocol)).to.equal(false)
app.removeAsDefaultProtocolClient(protocol, updateExe, processStartArgs)
- expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.be.false()
+ expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(false)
})
it('sets the app as the default protocol client', () => {
- expect(app.isDefaultProtocolClient(protocol)).to.be.false()
+ expect(app.isDefaultProtocolClient(protocol)).to.equal(false)
app.setAsDefaultProtocolClient(protocol)
- expect(app.isDefaultProtocolClient(protocol)).to.be.true()
+ expect(app.isDefaultProtocolClient(protocol)).to.equal(true)
})
it('allows a custom path and args to be specified', () => {
- expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.be.false()
+ expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(false)
app.setAsDefaultProtocolClient(protocol, updateExe, processStartArgs)
- expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.be.true()
- expect(app.isDefaultProtocolClient(protocol)).to.be.false()
+ expect(app.isDefaultProtocolClient(protocol, updateExe, processStartArgs)).to.equal(true)
+ expect(app.isDefaultProtocolClient(protocol)).to.equal(false)
})
it('creates a registry entry for the protocol class', (done) => {
app.setAsDefaultProtocolClient(protocol)
- classesKey.keys((error, keys) => {
+ classesKey.keys((error: Error, keys: any[]) => {
if (error) throw error
const exists = !!keys.find(key => key.key.includes(protocol))
- expect(exists).to.be.true()
+ expect(exists).to.equal(true)
done()
})
@@ -798,11 +777,11 @@ describe('app module', () => {
app.setAsDefaultProtocolClient(protocol)
app.removeAsDefaultProtocolClient(protocol)
- classesKey.keys((error, keys) => {
+ classesKey.keys((error: Error, keys: any[]) => {
if (error) throw error
const exists = !!keys.find(key => key.key.includes(protocol))
- expect(exists).to.be.false()
+ expect(exists).to.equal(false)
done()
})
@@ -819,11 +798,11 @@ describe('app module', () => {
protocolKey.set('test-value', 'REG_BINARY', '123', () => {
app.removeAsDefaultProtocolClient(protocol)
- classesKey.keys((error, keys) => {
+ classesKey.keys((error: Error, keys: any[]) => {
if (error) throw error
const exists = !!keys.find(key => key.key.includes(protocol))
- expect(exists).to.be.true()
+ expect(exists).to.equal(true)
done()
})
@@ -839,9 +818,9 @@ describe('app module', () => {
})
it('does not launch for argument following a URL', done => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app')
+ const appPath = path.join(fixturesPath, 'api', 'quit-app')
// App should exit with non 123 code.
- const first = ChildProcess.spawn(remote.process.execPath, [appPath, 'electron-test:?', 'abc'])
+ const first = cp.spawn(process.execPath, [appPath, 'electron-test:?', 'abc'])
first.once('exit', code => {
expect(code).to.not.equal(123)
done()
@@ -849,9 +828,9 @@ describe('app module', () => {
})
it('launches successfully for argument following a file path', done => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app')
+ const appPath = path.join(fixturesPath, 'api', 'quit-app')
// App should exit with code 123.
- const first = ChildProcess.spawn(remote.process.execPath, [appPath, 'e:\\abc', 'abc'])
+ const first = cp.spawn(process.execPath, [appPath, 'e:\\abc', 'abc'])
first.once('exit', code => {
expect(code).to.equal(123)
done()
@@ -859,9 +838,9 @@ describe('app module', () => {
})
it('launches successfully for multiple URIs following --', done => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'quit-app')
+ const appPath = path.join(fixturesPath, 'api', 'quit-app')
// App should exit with code 123.
- const first = ChildProcess.spawn(remote.process.execPath, [appPath, '--', 'http://electronjs.org', 'electron-test://testdata'])
+ const first = cp.spawn(process.execPath, [appPath, '--', 'http://electronjs.org', 'electron-test://testdata'])
first.once('exit', code => {
expect(code).to.equal(123)
done()
@@ -869,7 +848,7 @@ describe('app module', () => {
})
})
- describe('getFileIcon() API', (done) => {
+ describe('getFileIcon() API', () => {
const iconPath = path.join(__dirname, 'fixtures/assets/icon.ico')
const sizes = {
small: 16,
@@ -888,13 +867,14 @@ describe('app module', () => {
it('fetches a non-empty icon', async () => {
const icon = await app.getFileIcon(iconPath)
- expect(icon.isEmpty()).to.be.false()
+ expect(icon.isEmpty()).to.equal(false)
})
// TODO(codebytere): remove when promisification is complete
it('fetches a non-empty icon (callback)', (done) => {
- app.getFileIcon(iconPath, (icon) => {
- expect(icon.isEmpty()).to.be.false()
+ app.getFileIcon(iconPath, (error, icon) => {
+ expect(error).to.equal(null)
+ expect(icon.isEmpty()).to.equal(false)
done()
})
})
@@ -909,7 +889,8 @@ describe('app module', () => {
// TODO(codebytere): remove when promisification is complete
it('fetches normal icon size by default (callback)', (done) => {
- app.getFileIcon(iconPath, (icon) => {
+ app.getFileIcon(iconPath, (error, icon) => {
+ expect(error).to.equal(null)
const size = icon.getSize()
expect(size.height).to.equal(sizes.normal)
@@ -937,7 +918,8 @@ describe('app module', () => {
// TODO(codebytere): remove when promisification is complete
it('fetches a normal icon (callback)', (done) => {
- app.getFileIcon(iconPath, { size: 'normal' }, (icon) => {
+ app.getFileIcon(iconPath, { size: 'normal' }, (error, icon) => {
+ expect(error).to.equal(null)
const size = icon.getSize()
expect(size.height).to.equal(sizes.normal)
@@ -967,11 +949,11 @@ describe('app module', () => {
const types = []
for (const { pid, type, cpu } of appMetrics) {
expect(pid).to.be.above(0, 'pid is not > 0')
- expect(type).to.be.a('string').that.is.not.empty()
+ expect(type).to.be.a('string').that.does.not.equal('')
types.push(type)
- expect(cpu).to.have.own.property('percentCPUUsage').that.is.a('number')
- expect(cpu).to.have.own.property('idleWakeupsPerSecond').that.is.a('number')
+ expect(cpu).to.have.ownProperty('percentCPUUsage').that.is.a('number')
+ expect(cpu).to.have.ownProperty('idleWakeupsPerSecond').that.is.a('number')
}
if (process.platform === 'darwin') {
@@ -979,23 +961,22 @@ describe('app module', () => {
}
expect(types).to.include('Browser')
- expect(types).to.include('Tab')
})
})
describe('getGPUFeatureStatus() API', () => {
it('returns the graphic features statuses', () => {
const features = app.getGPUFeatureStatus()
- expect(features).to.have.own.property('webgl').that.is.a('string')
- expect(features).to.have.own.property('gpu_compositing').that.is.a('string')
+ expect(features).to.have.ownProperty('webgl').that.is.a('string')
+ expect(features).to.have.ownProperty('gpu_compositing').that.is.a('string')
})
})
describe('getGPUInfo() API', () => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'gpu-info.js')
+ const appPath = path.join(fixturesPath, 'api', 'gpu-info.js')
- const getGPUInfo = async (type) => {
- const appProcess = ChildProcess.spawn(remote.process.execPath, [appPath, type])
+ const getGPUInfo = async (type: string) => {
+ const appProcess = cp.spawn(process.execPath, [appPath, type])
let gpuInfoData = ''
let errorData = ''
appProcess.stdout.on('data', (data) => {
@@ -1013,11 +994,11 @@ describe('app module', () => {
return Promise.reject(new Error(errorData))
}
}
- const verifyBasicGPUInfo = async (gpuInfo) => {
+ const verifyBasicGPUInfo = async (gpuInfo: any) => {
// Devices information is always present in the available info.
- expect(gpuInfo).to.have.own.property('gpuDevice')
+ expect(gpuInfo).to.have.ownProperty('gpuDevice')
.that.is.an('array')
- .and.is.not.empty()
+ .and.does.not.equal([])
const device = gpuInfo.gpuDevice[0]
expect(device).to.be.an('object')
@@ -1040,11 +1021,11 @@ describe('app module', () => {
expect(completeInfo).to.deep.equal(basicInfo)
} else {
// Gl version is present in the complete info.
- expect(completeInfo).to.have.own.property('auxAttributes')
+ expect(completeInfo).to.have.ownProperty('auxAttributes')
.that.is.an('object')
- expect(completeInfo.auxAttributes).to.have.own.property('glVersion')
+ expect(completeInfo.auxAttributes).to.have.ownProperty('glVersion')
.that.is.a('string')
- .and.not.empty()
+ .and.does.not.equal([])
}
})
@@ -1056,8 +1037,8 @@ describe('app module', () => {
})
describe('sandbox options', () => {
- let appProcess = null
- let server = null
+ let appProcess: cp.ChildProcess = null as any
+ let server: net.Server = null as any
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'
beforeEach(function (done) {
@@ -1097,22 +1078,22 @@ describe('app module', () => {
describe('when app.enableSandbox() is called', () => {
it('adds --enable-sandbox to all renderer processes', done => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'mixed-sandbox-app')
- appProcess = ChildProcess.spawn(remote.process.execPath, [appPath, '--app-enable-sandbox'])
+ const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app')
+ appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox'])
server.once('error', error => { done(error) })
server.on('connection', client => {
- client.once('data', data => {
- const argv = JSON.parse(data)
+ client.once('data', (data) => {
+ const argv = JSON.parse(data.toString())
expect(argv.sandbox).to.include('--enable-sandbox')
expect(argv.sandbox).to.not.include('--no-sandbox')
expect(argv.noSandbox).to.include('--enable-sandbox')
expect(argv.noSandbox).to.not.include('--no-sandbox')
- expect(argv.noSandboxDevtools).to.be.true()
- expect(argv.sandboxDevtools).to.be.true()
+ expect(argv.noSandboxDevtools).to.equal(true)
+ expect(argv.sandboxDevtools).to.equal(true)
done()
})
@@ -1122,22 +1103,22 @@ describe('app module', () => {
describe('when the app is launched with --enable-sandbox', () => {
it('adds --enable-sandbox to all renderer processes', done => {
- const appPath = path.join(__dirname, 'fixtures', 'api', 'mixed-sandbox-app')
- appProcess = ChildProcess.spawn(remote.process.execPath, [appPath, '--enable-sandbox'])
+ const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app')
+ appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox'])
server.once('error', error => { done(error) })
server.on('connection', client => {
client.once('data', data => {
- const argv = JSON.parse(data)
+ const argv = JSON.parse(data.toString())
expect(argv.sandbox).to.include('--enable-sandbox')
expect(argv.sandbox).to.not.include('--no-sandbox')
expect(argv.noSandbox).to.include('--enable-sandbox')
expect(argv.noSandbox).to.not.include('--no-sandbox')
- expect(argv.noSandboxDevtools).to.be.true()
- expect(argv.sandboxDevtools).to.be.true()
+ expect(argv.noSandboxDevtools).to.equal(true)
+ expect(argv.sandboxDevtools).to.equal(true)
done()
})
@@ -1156,6 +1137,10 @@ describe('app module', () => {
const dockDescribe = process.platform === 'darwin' ? describe : describe.skip
dockDescribe('dock APIs', () => {
+ after(async () => {
+ await app.dock.show()
+ })
+
describe('dock.setMenu', () => {
it('can be retrieved via dock.getMenu', () => {
expect(app.dock.getMenu()).to.equal(null)
@@ -1173,7 +1158,7 @@ describe('app module', () => {
describe('dock.bounce', () => {
it('should return -1 for unknown bounce type', () => {
- expect(app.dock.bounce('bad type')).to.equal(-1)
+ expect(app.dock.bounce('bad type' as any)).to.equal(-1)
})
it('should return a positive number for informational type', () => {
@@ -1223,8 +1208,8 @@ describe('app module', () => {
expect(app.dock.show()).to.be.a('promise')
})
- it('eventually fulfills', () => {
- expect(app.dock.show()).to.be.eventually.fulfilled()
+ it('eventually fulfills', async () => {
+ await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined)
})
})
@@ -1241,32 +1226,32 @@ describe('app module', () => {
expect(app.whenReady()).to.be.a('promise')
})
- it('becomes fulfilled if the app is already ready', () => {
- expect(app.isReady()).to.be.true()
- expect(app.whenReady()).to.be.eventually.fulfilled()
+ it('becomes fulfilled if the app is already ready', async () => {
+ expect(app.isReady()).to.equal(true)
+ await expect(app.whenReady()).to.be.eventually.fulfilled.equal(undefined)
})
})
describe('commandLine.hasSwitch', () => {
it('returns true when present', () => {
app.commandLine.appendSwitch('foobar1')
- expect(app.commandLine.hasSwitch('foobar1')).to.be.true()
+ expect(app.commandLine.hasSwitch('foobar1')).to.equal(true)
})
it('returns false when not present', () => {
- expect(app.commandLine.hasSwitch('foobar2')).to.be.false()
+ expect(app.commandLine.hasSwitch('foobar2')).to.equal(false)
})
})
describe('commandLine.hasSwitch (existing argv)', () => {
it('returns true when present', async () => {
const { hasSwitch } = await runTestApp('command-line', '--foobar')
- expect(hasSwitch).to.be.true()
+ expect(hasSwitch).to.equal(true)
})
it('returns false when not present', async () => {
const { hasSwitch } = await runTestApp('command-line')
- expect(hasSwitch).to.be.false()
+ expect(hasSwitch).to.equal(false)
})
})
@@ -1335,9 +1320,9 @@ describe('default behavior', () => {
})
})
-async function runTestApp (name, ...args) {
- const appPath = path.join(__dirname, 'fixtures', 'api', name)
- const electronPath = remote.getGlobal('process').execPath
+async function runTestApp (name: string, ...args: any[]) {
+ const appPath = path.join(fixturesPath, 'api', name)
+ const electronPath = process.execPath
const appProcess = cp.spawn(electronPath, [appPath, ...args])
let output = ''
diff --git a/spec-main/events-helpers.ts b/spec-main/events-helpers.ts
new file mode 100644
index 0000000000..b4e493e101
--- /dev/null
+++ b/spec-main/events-helpers.ts
@@ -0,0 +1,40 @@
+import { EventEmitter } from "electron";
+
+/**
+ * @fileoverview A set of helper functions to make it easier to work
+ * with events in async/await manner.
+ */
+
+/**
+ * @param {!EventTarget} target
+ * @param {string} eventName
+ * @return {!Promise}
+ */
+export const waitForEvent = (target: EventTarget, eventName: string) => {
+ return new Promise(resolve => {
+ target.addEventListener(eventName, resolve, { once: true })
+ })
+}
+
+/**
+ * @param {!EventEmitter} emitter
+ * @param {string} eventName
+ * @return {!Promise} With Event as the first item.
+ */
+export const emittedOnce = (emitter: EventEmitter, eventName: string) => {
+ return emittedNTimes(emitter, eventName, 1).then(([result]) => result)
+}
+
+export const emittedNTimes = (emitter: EventEmitter, eventName: string, times: number) => {
+ const events: any[][] = []
+ return new Promise(resolve => {
+ const handler = (...args: any[]) => {
+ events.push(args)
+ if (events.length === times) {
+ emitter.removeListener(eventName, handler)
+ resolve(events)
+ }
+ }
+ emitter.on(eventName, handler)
+ })
+}
diff --git a/spec-main/index.js b/spec-main/index.js
new file mode 100644
index 0000000000..9e5b4f26ae
--- /dev/null
+++ b/spec-main/index.js
@@ -0,0 +1,90 @@
+const Module = require('module')
+const path = require('path')
+const v8 = require('v8')
+
+Module.globalPaths.push(path.resolve(__dirname, '../spec/node_modules'))
+
+// We want to terminate on errors, not throw up a dialog
+process.on('uncaughtException', (err) => {
+ console.error('Unhandled exception in main spec runner:', err)
+ process.exit(1)
+})
+
+// Tell ts-node which tsconfig to use
+process.env.TS_NODE_PROJECT = path.resolve(__dirname, '../tsconfig.spec.json')
+
+const { app } = require('electron')
+
+v8.setFlagsFromString('--expose_gc')
+app.commandLine.appendSwitch('js-flags', '--expose_gc')
+// Prevent the spec runner quiting when the first window closes
+app.on('window-all-closed', () => null)
+// TODO: This API should _probably_ only be enabled for the specific test that needs it
+// not the entire test suite
+app.commandLine.appendSwitch('ignore-certificate-errors')
+
+app.whenReady().then(() => {
+ require('ts-node/register')
+
+ const argv = require('yargs')
+ .boolean('ci')
+ .string('g').alias('g', 'grep')
+ .boolean('i').alias('i', 'invert')
+ .argv
+
+ const isCi = !!argv.ci
+ global.isCI = isCi
+
+ const Mocha = require('mocha')
+ const mochaOptions = {}
+ if (process.env.MOCHA_REPORTER) {
+ mochaOptions.reporter = process.env.MOCHA_REPORTER
+ }
+ if (process.env.MOCHA_MULTI_REPORTERS) {
+ mochaOptions.reporterOptions = {
+ reporterEnabled: process.env.MOCHA_MULTI_REPORTERS
+ }
+ }
+ const mocha = new Mocha(mochaOptions)
+
+ if (!process.env.MOCHA_REPORTER) {
+ mocha.ui('bdd').reporter('tap')
+ }
+ mocha.timeout(isCi ? 30000 : 10000)
+
+ if (argv.grep) mocha.grep(argv.grep)
+ if (argv.invert) mocha.invert()
+
+ // Read all test files.
+ const walker = require('walkdir').walk(__dirname, {
+ no_recurse: true
+ })
+
+ // This allows you to run specific modules only:
+ // npm run test -match=menu
+ const moduleMatch = process.env.npm_config_match
+ ? new RegExp(process.env.npm_config_match, 'g')
+ : null
+
+ walker.on('file', (file) => {
+ if (/-spec\.[tj]s$/.test(file) &&
+ (!moduleMatch || moduleMatch.test(file))) {
+ mocha.addFile(file)
+ }
+ })
+
+ walker.on('end', () => {
+ const runner = mocha.run(() => {
+ if (isCi && runner.hasOnly) {
+ try {
+ throw new Error('A spec contains a call to it.only or describe.only and should be reverted.')
+ } catch (error) {
+ console.error(error.stack || error)
+ }
+ process.exit(1)
+ }
+
+ process.exit(runner.failures)
+ })
+ })
+})
diff --git a/spec-main/package.json b/spec-main/package.json
new file mode 100644
index 0000000000..8a802f6bfd
--- /dev/null
+++ b/spec-main/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "electron-test-main",
+ "productName": "Electron Test Main",
+ "main": "index.js",
+ "version": "0.1.0"
+}
diff --git a/spec-main/window-helpers.ts b/spec-main/window-helpers.ts
new file mode 100644
index 0000000000..56345a1bbb
--- /dev/null
+++ b/spec-main/window-helpers.ts
@@ -0,0 +1,26 @@
+import { expect } from 'chai'
+import { BrowserWindow, WebContents } from 'electron'
+import { emittedOnce } from './events-helpers';
+
+export const closeWindow = async (
+ window: BrowserWindow | null = null,
+ { assertNotWindows } = { assertNotWindows: true }
+) => {
+ if (window && !window.isDestroyed()) {
+ const isClosed = emittedOnce(window, 'closed')
+ window.setClosable(true)
+ window.close()
+ await isClosed
+ }
+
+ if (assertNotWindows) {
+ expect(BrowserWindow.getAllWindows()).to.have.lengthOf(0)
+ }
+}
+
+exports.waitForWebContentsToLoad = async (webContents: WebContents) => {
+ const didFinishLoadPromise = emittedOnce(webContents, 'did-finish-load')
+ if (webContents.isLoadingMainFrame()) {
+ await didFinishLoadPromise
+ }
+}
diff --git a/spec/package-lock.json b/spec/package-lock.json
index 3ba22e9417..5f7208c3ad 100644
--- a/spec/package-lock.json
+++ b/spec/package-lock.json
@@ -5,9 +5,8 @@
"requires": true,
"dependencies": {
"abstract-socket": {
- "version": "github:nornagon/node-abstractsocket#7d9c770f9ffef14373349034f8820ff059879845",
- "from": "github:nornagon/node-abstractsocket#v8-compat",
- "dev": true,
+ "version": "2.0.0",
+ "resolved": "github:nornagon/node-abstractsocket#7d9c770f9ffef14373349034f8820ff059879845",
"optional": true,
"requires": {
"bindings": "^1.2.1",
@@ -66,7 +65,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.4.0.tgz",
"integrity": "sha512-7znEVX22Djn+nYjxCWKDne0RRloa9XfYa84yk3s+HkE3LpDYZmhArYr9O9huBoHY3/oXispx5LorIX7Sl2CgSQ==",
- "dev": true,
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
@@ -243,14 +241,12 @@
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
- "optional": true
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"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=",
- "optional": true
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cross-spawn": {
"version": "6.0.5",
@@ -285,6 +281,19 @@
"put": "0.0.6",
"safe-buffer": "^5.1.1",
"xml2js": "^0.4.17"
+ },
+ "dependencies": {
+ "abstract-socket": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/abstract-socket/-/abstract-socket-2.0.0.tgz",
+ "integrity": "sha1-2DyT598w0n4j8+gqdj5/XnjZFvk=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bindings": "^1.2.1",
+ "nan": "^2.0.9"
+ }
+ }
}
},
"debug": {
@@ -459,7 +468,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
- "dev": true,
"optional": true
},
"find-up": {
@@ -672,8 +680,7 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "optional": true
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"isexe": {
"version": "2.0.0",
@@ -778,9 +785,8 @@
},
"minimist": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
- "optional": true
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"mkdirp": {
"version": "0.5.1",
@@ -1100,8 +1106,7 @@
"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==",
- "optional": true
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"pump": {
"version": "2.0.1",
@@ -1151,9 +1156,8 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "optional": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@@ -1363,7 +1367,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "optional": true,
"requires": {
"safe-buffer": "~5.1.0"
}
@@ -1490,8 +1493,7 @@
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
- "optional": true
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"walkdir": {
"version": "0.3.2",
diff --git a/spec/package.json b/spec/package.json
index 38f6f32899..fb0ddcaa28 100644
--- a/spec/package.json
+++ b/spec/package.json
@@ -7,7 +7,6 @@
"postinstall": "node ../tools/run-if-exists.js node_modules/robotjs node-gyp rebuild"
},
"devDependencies": {
- "abstract-socket": "github:nornagon/node-abstractsocket#v8-compat",
"basic-auth": "^2.0.1",
"bluebird": "^3.5.3",
"chai": "^4.2.0",
@@ -33,6 +32,7 @@
"yargs": "^12.0.5"
},
"optionalDependencies": {
+ "abstract-socket": "github:nornagon/node-abstractsocket#v8-compat",
"robotjs": "github:nornagon/robotjs#node-12"
}
}
diff --git a/spec/static/index.html b/spec/static/index.html
index 059495c400..a6e12db00a 100644
--- a/spec/static/index.html
+++ b/spec/static/index.html
@@ -17,6 +17,12 @@
const electron = require('electron')
const { remote, ipcRenderer } = electron
+ // Set up chai-as-promised here first to avoid conflicts
+ // It must be loaded first or really strange things happen inside
+ // chai that cause test failures
+ // DO NOT MOVE, REMOVE OR EDIT THIS LINE
+ require('chai').use(require('chai-as-promised'))
+
// Check if we are running in CI.
const isCi = remote.getGlobal('isCi')
diff --git a/tsconfig.spec.json b/tsconfig.spec.json
new file mode 100644
index 0000000000..d08253b41f
--- /dev/null
+++ b/tsconfig.spec.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "spec-main"
+ },
+ "include": [
+ "spec-main",
+ "typings"
+ ]
+}
diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts
index 49886c3dbd..52255f14e5 100644
--- a/typings/internal-ambient.d.ts
+++ b/typings/internal-ambient.d.ts
@@ -13,6 +13,7 @@ declare namespace NodeJS {
interface V8UtilBinding {
getHiddenValue(obj: any, key: string): T;
setHiddenValue(obj: any, key: string, value: T): void;
+ requestGarbageCollectionForTesting(): void;
}
interface Process {
/**