]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
move away from sauce labs to browserstack
authorJohann-S <johann.servoire@gmail.com>
Wed, 1 Aug 2018 08:11:10 +0000 (10:11 +0200)
committerJohann-S <johann.servoire@gmail.com>
Thu, 30 Aug 2018 20:17:15 +0000 (22:17 +0200)
README.md
build/sauce_browsers.json [deleted file]
build/saucelabs-unit-test.js [deleted file]
js/tests/browsers.js [new file with mode: 0644]
js/tests/karma-browserstack.conf.js [new file with mode: 0644]
package-lock.json
package.json

index cd01c6609d5ee1665d20462ffd69081b102b2923..9f91a933664d809fd90e1aa1966dc8f621182914 100644 (file)
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@
 - [Community](#community)
 - [Versioning](#versioning)
 - [Creators](#creators)
+- [Thanks](#thanks)
 - [Copyright and license](#copyright-and-license)
 
 ## Quick start
@@ -65,8 +66,7 @@ Read the [Getting started page](https://getbootstrap.com/docs/4.1/getting-starte
 [![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/v4-dev.svg)](https://coveralls.io/github/twbs/bootstrap?branch=v4-dev)
 [![CSS gzip size](http://img.badgesize.io/twbs/bootstrap/v4-dev/dist/css/bootstrap.min.css?compression=gzip&label=CSS+gzip+size)](https://github.com/twbs/bootstrap/tree/v4-dev/dist/css/bootstrap.min.css)
 [![JS gzip size](http://img.badgesize.io/twbs/bootstrap/v4-dev/dist/js/bootstrap.min.js?compression=gzip&label=JS+gzip+size)](https://github.com/twbs/bootstrap/tree/v4-dev/dist/js/bootstrap.min.js)
-
-[![Sauce Labs Test Status](https://saucelabs.com/browser-matrix/bootstrap.svg)](https://saucelabs.com/u/bootstrap)
+[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)](https://www.browserstack.com/automate/public-build/SkxZcStBeExEdVJqQ2hWYnlWckpkNmNEY213SFp6WHFETWk2bGFuY3pCbz0tLXhqbHJsVlZhQnRBdEpod3NLSDMzaHc9PQ==--3d0b75245708616eb93113221beece33e680b229)
 
 ## What's included
 
@@ -172,6 +172,12 @@ See [the Releases section of our GitHub project](https://github.com/twbs/bootstr
 - <https://github.com/fat>
 
 
+## Thanks
+
+<img src="https://live.browserstack.com/images/opensource/browserstack-logo.svg" alt="BrowserStack Logo" width="490" height="106">
+
+Thanks to [BrowserStack](https://www.browserstack.com/) for providing the infrastructure that allows us to test in real browsers!
+
 ## Copyright and license
 
 Code and documentation copyright 2011-2018 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors) and [Twitter, Inc.](https://twitter.com) Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE).
diff --git a/build/sauce_browsers.json b/build/sauce_browsers.json
deleted file mode 100644 (file)
index b112d9c..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-[
-  {
-    "browserName": "safari",
-    "platform": "OS X 10.11",
-    "version": "latest"
-  },
-  {
-    "browserName": "chrome",
-    "platform": "OS X 10.11",
-    "version": "latest"
-  },
-  {
-    "browserName": "firefox",
-    "platform": "OS X 10.11",
-    "version": "latest"
-  },
-  {
-    "browserName": "MicrosoftEdge",
-    "platform": "Windows 10",
-    "version": "latest"
-  },
-  {
-    "browserName": "internet explorer",
-    "version": "11",
-    "platform": "Windows 8.1"
-  },
-  {
-    "browserName": "internet explorer",
-    "version": "10",
-    "platform": "Windows 8"
-  },
-  {
-    "browserName": "chrome",
-    "platform": "Windows 10",
-    "version": "latest"
-  },
-  {
-    "browserName": "firefox",
-    "platform": "Windows 10",
-    "version": "latest"
-  },
-  {
-    "browserName": "iphone",
-    "deviceName": "iPhone Simulator",
-    "platformName": "OS X 10.11",
-    "version": "9.3"
-  },
-  {
-    "browserName": "chrome",
-    "platform": "Linux",
-    "version": "latest"
-  },
-  {
-    "browserName": "firefox",
-    "platform": "Linux",
-    "version": "latest"
-  },
-  {
-    "platform": "Linux",
-    "browserName": "android",
-    "deviceName": "Android Emulator",
-    "version": "latest",
-    "deviceType": "phone"
-  }
-]
diff --git a/build/saucelabs-unit-test.js b/build/saucelabs-unit-test.js
deleted file mode 100644 (file)
index a84aa1d..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*!
- * Script to run our Sauce Labs tests.
- * Copyright 2017-2018 The Bootstrap Authors
- * Copyright 2017-2018 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
-
-/*
-Docs: https://wiki.saucelabs.com/display/DOCS/Platform+Configurator
-Mac Opera is not currently supported by Sauce Labs
-Win Opera 15+ is not currently supported by Sauce Labs
-iOS Chrome is not currently supported by Sauce Labs
-*/
-
-const path = require('path')
-const JSUnitSaucelabs = require('jsunitsaucelabs')
-
-const jsUnitSaucelabs = new JSUnitSaucelabs({
-  username: process.env.SAUCE_USERNAME,
-  password: process.env.SAUCE_ACCESS_KEY,
-  build: process.env.TRAVIS_JOB_ID
-})
-
-const testURL = 'http://localhost:3000/js/tests/index.html?hidepassed'
-const browsersFile = require(path.resolve(__dirname, './sauce_browsers.json'))
-const errorMessages = [
-  'Test exceeded maximum duration',
-  'Test exceeded maximum duration after 180 seconds'
-]
-let jobsDone = 0
-let jobsSucceeded = 0
-
-const waitingCallback = (error, body, id) => {
-  if (error) {
-    console.error(error)
-    process.exit(1)
-  }
-
-  if (typeof body !== 'undefined') {
-    if (!body.completed) {
-      setTimeout(() => {
-        jsUnitSaucelabs.getStatus(id, (error, body) => {
-          waitingCallback(error, body, id)
-        })
-      }, 2000)
-    } else {
-      const test = body['js tests'][0]
-      const platform = test.platform.join(', ')
-      let passed = false
-      let errorStr = false
-
-      if (test.result !== null) {
-        if (typeof test.result === 'string' && errorMessages.includes(test.result)) {
-          errorStr = test.result
-        } else {
-          passed = test.result.total === test.result.passed
-        }
-      }
-
-      console.log(`Tested ${testURL}`)
-      console.log(`Platform: ${platform}`)
-      console.log(`Passed: ${passed}`)
-      console.log(`URL: ${test.url}\n`)
-
-      if (errorStr) {
-        console.error(`${platform}: ${errorStr}`)
-      }
-
-      if (passed) {
-        jobsSucceeded++
-      }
-      jobsDone++
-
-      // Exit
-      if (jobsDone === browsersFile.length - 1) {
-        jsUnitSaucelabs.stop()
-        if (jobsDone > jobsSucceeded) {
-          const failedTests = jobsDone - jobsSucceeded
-          throw new Error(`${failedTests} test${failedTests > 1 ? 's' : ''} failed.`)
-        }
-
-        console.log('All tests passed')
-        process.exit(0)
-      }
-    }
-  }
-}
-
-jsUnitSaucelabs.on('tunnelCreated', () => {
-  browsersFile.forEach((tmpBrowser) => {
-    const browsersPlatform = typeof tmpBrowser.platform === 'undefined' ? tmpBrowser.platformName : tmpBrowser.platform
-    const browsersArray = [browsersPlatform, tmpBrowser.browserName, tmpBrowser.version]
-
-    jsUnitSaucelabs.start([browsersArray], testURL, 'qunit', (error, success) => {
-      if (typeof success !== 'undefined') {
-        const taskIds = success['js tests']
-
-        if (!taskIds || taskIds.length === 0) {
-          throw new Error('Error starting tests through Sauce Labs API')
-        }
-
-        taskIds.forEach((id) => {
-          jsUnitSaucelabs.getStatus(id, (error, body) => {
-            waitingCallback(error, body, id)
-          })
-        })
-      } else {
-        console.error(error)
-      }
-    })
-  })
-})
-
-jsUnitSaucelabs.initTunnel()
diff --git a/js/tests/browsers.js b/js/tests/browsers.js
new file mode 100644 (file)
index 0000000..da36e49
--- /dev/null
@@ -0,0 +1,82 @@
+/* eslint-env node */
+/* eslint-disable camelcase */
+
+const browsers = {
+  safariMac: {
+    base: 'BrowserStack',
+    os: 'OS X',
+    os_version: 'High Sierra',
+    browser: 'Safari',
+    browser_version: 'latest'
+  },
+  chromeMac: {
+    base: 'BrowserStack',
+    os: 'OS X',
+    os_version: 'High Sierra',
+    browser : 'Chrome',
+    browser_version : 'latest'
+  },
+  firefoxMac: {
+    base: 'BrowserStack',
+    os: 'OS X',
+    os_version: 'High Sierra',
+    browser: 'Firefox',
+    browser_version: 'latest'
+  },
+  edgeWin10: {
+    base: 'BrowserStack',
+    os: 'Windows',
+    os_version: '10',
+    browser: 'Edge',
+    browser_version: 'latest'
+  },
+  ie11Win10: {
+    base: 'BrowserStack',
+    os: 'Windows',
+    os_version: '10',
+    browser: 'IE',
+    browser_version: '11.0'
+  },
+  chromeWin10: {
+    base: 'BrowserStack',
+    os: 'Windows',
+    os_version: '10',
+    browser: 'Chrome',
+    browser_version: 'latest'
+  },
+  firefoxWin10: {
+    base: 'BrowserStack',
+    os: 'Windows',
+    os_version: '10',
+    browser: 'Firefox',
+    browser_version: 'latest'
+  },
+  ie10Win8: {
+    base: 'BrowserStack',
+    os: 'Windows',
+    os_version: '8',
+    browser: 'IE',
+    browser_version: '10.0'
+  },
+  iphoneX: {
+    base: 'BrowserStack',
+    os: 'ios',
+    os_version: '11.0',
+    device: 'iPhone X',
+    real_mobile: true
+  },
+  pixel2: {
+    base: 'BrowserStack',
+    os: 'android',
+    os_version: '8.0',
+    device: 'Google Pixel 2',
+    real_mobile: true
+  }
+}
+
+const browsersKeys = Object.keys(browsers)
+
+module.exports = {
+  browsers,
+  browsersKeys
+}
diff --git a/js/tests/karma-browserstack.conf.js b/js/tests/karma-browserstack.conf.js
new file mode 100644 (file)
index 0000000..a440818
--- /dev/null
@@ -0,0 +1,51 @@
+/* eslint-env node */
+/* eslint no-process-env: 0 */
+const ip = require('ip')
+const {
+  browsers,
+  browsersKeys
+} = require('./browsers')
+
+module.exports = (config) => {
+  config.set({
+    hostname: ip.address(),
+    browserStack: {
+      username: process.env.BROWSER_STACK_USERNAME,
+      accessKey: process.env.BROWSER_STACK_ACCESS_KEY,
+      build: `bootstrap-${new Date().toISOString()}`,
+      project: 'Bootstrap',
+      retryLimit: 2
+    },
+    basePath: '../..',
+    frameworks: ['qunit', 'sinon'],
+    plugins: [
+      'karma-qunit',
+      'karma-sinon',
+      'karma-browserstack-launcher'
+    ],
+    // list of files / patterns to load in the browser
+    files: [
+      'site/docs/4.1/assets/js/vendor/jquery-slim.min.js',
+      'site/docs/4.1/assets/js/vendor/popper.min.js',
+      'js/coverage/dist/util.js',
+      'js/coverage/dist/tooltip.js',
+      'js/coverage/dist/!(util|index|tooltip).js', // include all of our js/dist files except util.js, index.js and tooltip.js
+      'js/tests/unit/*.js'
+    ],
+    customLaunchers: browsers,
+    browsers: browsersKeys,
+    reporters: ['dots', 'BrowserStack'],
+    port: 9876,
+    colors: true,
+    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+    logLevel: config.LOG_ERROR || config.LOG_WARN,
+    autoWatch: false,
+    singleRun: true,
+    concurrency: Infinity,
+    client: {
+      qunit: {
+        showUI: true
+      }
+    }
+  })
+}
index 96b57a52db60fee62179499edcdc251c4a7a38e5..bcaa51a16faba00ec4cfdadc4dad04dd90e7f169 100644 (file)
       "dev": true
     },
     "@types/node": {
-      "version": "10.9.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.2.tgz",
-      "integrity": "sha512-pwZnkVyCGJ3LsQ0/3flQK5lCFao4esIzwUVzzk5NvL9vnkEyDhNf4fhHzUMHvyr56gNZywWTS2MR0euabMSz4A==",
+      "version": "10.5.5",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.5.tgz",
+      "integrity": "sha512-6Qnb1gXbp3g1JX9QVJj3A6ORzc9XCyhokxUKaoonHgNXcQhmk8adhotxfkeK8El9TnFeUuH72yI6jQ5nDJKS6w==",
       "dev": true
     },
     "abbrev": {
       "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
       "dev": true
     },
+    "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==",
+      "dev": true,
+      "requires": {
+        "es6-promisify": "^5.0.0"
+      }
+    },
     "ajv": {
       "version": "5.5.2",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
         "num2fraction": "^1.2.2",
         "postcss": "^7.0.2",
         "postcss-value-parser": "^3.2.3"
+      },
+      "dependencies": {
+        "browserslist": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.1.0.tgz",
+          "integrity": "sha512-kQBKB8hnq1SRfSpwHDpM1JNHAyk9fydW8hIDvndR2ijTFKIlBPEvkJkCt8JznOugdm12/YCaRgyq/sqDGz9PwA==",
+          "dev": true,
+          "requires": {
+            "caniuse-lite": "^1.0.30000878",
+            "electron-to-chromium": "^1.3.61",
+            "node-releases": "^1.0.0-alpha.11"
+          }
+        },
+        "caniuse-lite": {
+          "version": "1.0.30000880",
+          "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000880.tgz",
+          "integrity": "sha512-G2cDhHp0DshhwFJSurN7PByRTXgijs3eA3F9tGd5tf5vnTttDVuRI9bFna0WDMID4VYhGs2ob9U/K1A5+pm8pw==",
+          "dev": true
+        },
+        "electron-to-chromium": {
+          "version": "1.3.61",
+          "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.61.tgz",
+          "integrity": "sha512-XjTdsm6x71Y48lF9EEvGciwXD70b20g0t+3YbrE+0fPFutqV08DSNrZXkoXAp3QuzX7TpL/OW+/VsNoR9GkuNg==",
+          "dev": true
+        }
       }
     },
     "aws-sign2": {
         }
       }
     },
+    "binary": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+      "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
+      "dev": true,
+      "requires": {
+        "buffers": "~0.1.1",
+        "chainsaw": "~0.1.0"
+      }
+    },
     "binary-extensions": {
       "version": "1.11.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
         "caniuse-lite": "^1.0.30000878",
         "electron-to-chromium": "^1.3.61",
         "node-releases": "^1.0.0-alpha.11"
+      },
+      "dependencies": {
+        "electron-to-chromium": {
+          "version": "1.3.62",
+          "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.62.tgz",
+          "integrity": "sha512-x09ndL/Gjnuk3unlAyoGyUg3wbs4w/bXurgL7wL913vXHAOWmMhrLf1VNGRaMLngmadd5Q8gsV9BFuIr6rP+Xg==",
+          "dev": true
+        }
+      }
+    },
+    "browserstack": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.0.tgz",
+      "integrity": "sha1-tWVCWtYu1ywQgqHrl51TE8fUdU8=",
+      "dev": true,
+      "requires": {
+        "https-proxy-agent": "1.0.0"
+      },
+      "dependencies": {
+        "agent-base": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz",
+          "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=",
+          "dev": true,
+          "requires": {
+            "extend": "~3.0.0",
+            "semver": "~5.0.1"
+          }
+        },
+        "https-proxy-agent": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
+          "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=",
+          "dev": true,
+          "requires": {
+            "agent-base": "2",
+            "debug": "2",
+            "extend": "3"
+          }
+        },
+        "semver": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz",
+          "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=",
+          "dev": true
+        }
+      }
+    },
+    "browserstacktunnel-wrapper": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.3.tgz",
+      "integrity": "sha512-I3u/EOOPGBt34RN0PBhPDwee4Dsik/Y2URK7iJn/vj5sR8JIoNh5I9gA6bFyxhU9sCo/1ISN4p7URNXK4zD4AA==",
+      "dev": true,
+      "requires": {
+        "https-proxy-agent": "^2.2.1",
+        "unzip": "~0.1.11"
       }
     },
     "bser": {
       "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
       "dev": true
     },
+    "buffers": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+      "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=",
+      "dev": true
+    },
     "builtin-modules": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
         "lazy-cache": "^1.0.3"
       }
     },
+    "chainsaw": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+      "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
+      "dev": true,
+      "requires": {
+        "traverse": ">=0.3.0 <0.4"
+      }
+    },
     "chalk": {
       "version": "2.4.1",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
       "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
       "dev": true
     },
-    "electron-to-chromium": {
-      "version": "1.3.61",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.61.tgz",
-      "integrity": "sha512-XjTdsm6x71Y48lF9EEvGciwXD70b20g0t+3YbrE+0fPFutqV08DSNrZXkoXAp3QuzX7TpL/OW+/VsNoR9GkuNg==",
-      "dev": true
-    },
     "encodeurl": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
       "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=",
       "dev": true
     },
+    "es6-promise": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
+      "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==",
+      "dev": true
+    },
+    "es6-promisify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+      "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+      "dev": true,
+      "requires": {
+        "es6-promise": "^4.0.3"
+      }
+    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
     },
     "event-stream": {
       "version": "3.3.4",
-      "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
+      "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
       "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
       "dev": true,
       "requires": {
       }
     },
     "follow-redirects": {
-      "version": "1.5.7",
-      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.7.tgz",
-      "integrity": "sha512-NONJVIFiX7Z8k2WxfqBjtwqMifx7X42ORLFrOZ2LTKGj71G3C0kfdyTqGqr8fx5zSX6Foo/D95dgGWbPUiwnew==",
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.2.tgz",
+      "integrity": "sha512-kssLorP/9acIdpQ2udQVTiCS5LQmdEz9mvdIfDcl1gYX2tPKFADHSyFdvJS040XdFsPzemWtgI3q8mFVCxtX8A==",
       "dev": true,
       "requires": {
         "debug": "^3.1.0"
       }
     },
     "fstream": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
-      "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
+      "version": "0.1.31",
+      "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz",
+      "integrity": "sha1-czfwWPu7vvqMn1YaKMqwhJICyYg=",
       "dev": true,
       "requires": {
-        "graceful-fs": "^4.1.2",
+        "graceful-fs": "~3.0.2",
         "inherits": "~2.0.0",
-        "mkdirp": ">=0.5 0",
+        "mkdirp": "0.5",
         "rimraf": "2"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "3.0.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
+          "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
+          "dev": true,
+          "requires": {
+            "natives": "^1.1.0"
+          }
+        }
       }
     },
     "function-bind": {
         "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==",
+      "dev": true,
+      "requires": {
+        "agent-base": "^4.1.0",
+        "debug": "^3.1.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
+      }
+    },
     "humanize-duration": {
       "version": "3.15.1",
       "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.15.1.tgz",
       }
     },
     "ignore": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
-      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.3.tgz",
+      "integrity": "sha512-Z/vAH2GGIEATQnBVXMclE2IGV6i0GyVngKThcGZ5kHgHMxLo9Ow2+XHRq1aEKEej5vOF1TPJNbvX6J/anT0M7A==",
       "dev": true
     },
     "ignore-by-default": {
       "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
       "dev": true
     },
+    "ip": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
+      "dev": true
+    },
     "is-absolute": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
         "verror": "1.10.0"
       }
     },
-    "jsunitsaucelabs": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/jsunitsaucelabs/-/jsunitsaucelabs-1.3.2.tgz",
-      "integrity": "sha512-sjJS9KVyQtKnisQ1XW4xkJ9q2o42AIHD3dD5i52m7j0cMirkuoSd/NW6UaFSQTktvO0ATe8lK4ao796IC+6+DA==",
-      "dev": true,
-      "requires": {
-        "colors": "^1.2.1",
-        "sauce-tunnel": "^2.5.0"
-      },
-      "dependencies": {
-        "colors": {
-          "version": "1.3.2",
-          "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz",
-          "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==",
-          "dev": true
-        }
-      }
-    },
     "just-extend": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-3.0.0.tgz",
         }
       }
     },
+    "karma-browserstack-launcher": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/karma-browserstack-launcher/-/karma-browserstack-launcher-1.3.0.tgz",
+      "integrity": "sha512-LrPf5sU/GISkEElWyoy06J8x0c8BcOjjOwf61Wqu6M0aWQu0Eoqm9yh3xON64/ByST/CEr0GsWiREQ/EIEMd4Q==",
+      "dev": true,
+      "requires": {
+        "browserstack": "1.5.0",
+        "browserstacktunnel-wrapper": "~2.0.1",
+        "q": "~1.5.0"
+      }
+    },
     "karma-chrome-launcher": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
       "integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==",
       "dev": true
     },
+    "match-stream": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz",
+      "integrity": "sha1-mesFAJOzTf+t5CG5rAtBCpz6F88=",
+      "dev": true,
+      "requires": {
+        "buffers": "~0.1.1",
+        "readable-stream": "~1.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "dev": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
+        }
+      }
+    },
     "matcher-collection": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-1.0.5.tgz",
         "to-regex": "^3.0.1"
       }
     },
+    "natives": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.4.tgz",
+      "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==",
+      "dev": true
+    },
     "natural-compare": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
         "which": "1"
       },
       "dependencies": {
+        "fstream": {
+          "version": "1.0.11",
+          "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
+          "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "inherits": "~2.0.0",
+            "mkdirp": ">=0.5 0",
+            "rimraf": "2"
+          }
+        },
+        "request": {
+          "version": "2.88.0",
+          "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+          "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+          "dev": true,
+          "requires": {
+            "aws-sign2": "~0.7.0",
+            "aws4": "^1.8.0",
+            "caseless": "~0.12.0",
+            "combined-stream": "~1.0.6",
+            "extend": "~3.0.2",
+            "forever-agent": "~0.6.1",
+            "form-data": "~2.3.2",
+            "har-validator": "~5.1.0",
+            "http-signature": "~1.2.0",
+            "is-typedarray": "~1.0.0",
+            "isstream": "~0.1.2",
+            "json-stringify-safe": "~5.0.1",
+            "mime-types": "~2.1.19",
+            "oauth-sign": "~0.9.0",
+            "performance-now": "^2.1.0",
+            "qs": "~6.5.2",
+            "safe-buffer": "^5.1.2",
+            "tough-cookie": "~2.4.3",
+            "tunnel-agent": "^0.6.0",
+            "uuid": "^3.3.2"
+          }
+        },
         "semver": {
           "version": "5.3.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
           "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
           "dev": true
+        },
+        "uuid": {
+          "version": "3.3.2",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+          "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+          "dev": true
         }
       }
     },
         "mkdirp": "^0.5.1"
       }
     },
+    "over": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz",
+      "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg=",
+      "dev": true
+    },
     "p-finally": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
         "ps-tree": "^1.1.0"
       }
     },
+    "pullstream": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz",
+      "integrity": "sha1-1vs79a7Wl+gxFQ6xACwlo/iuExQ=",
+      "dev": true,
+      "requires": {
+        "over": ">= 0.0.5 < 1",
+        "readable-stream": "~1.0.31",
+        "setimmediate": ">= 1.0.2 < 2",
+        "slice-stream": ">= 1.0.0 < 2"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "dev": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
+        }
+      }
+    },
     "punycode": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
       "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
       "dev": true
     },
+    "q": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+      "dev": true
+    },
     "qjobs": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
         }
       }
     },
-    "sauce-tunnel": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/sauce-tunnel/-/sauce-tunnel-2.5.0.tgz",
-      "integrity": "sha1-DuTE/5tH4BPosHLL+sSVt/7Y6Os=",
-      "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "request": "^2.72.0",
-        "split": "^1.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "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"
-          }
-        },
-        "split": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
-          "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
-          "dev": true,
-          "requires": {
-            "through": "2"
-          }
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
     "scss-tokenizer": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
         }
       }
     },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+      "dev": true
+    },
     "setprototypeof": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
         "is-fullwidth-code-point": "^2.0.0"
       }
     },
+    "slice-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz",
+      "integrity": "sha1-WzO9ZvATsaf4ZGCwPUY97DmtPqA=",
+      "dev": true,
+      "requires": {
+        "readable-stream": "~1.0.31"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "dev": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
+        }
+      }
+    },
     "snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
         "block-stream": "*",
         "fstream": "^1.0.2",
         "inherits": "2"
+      },
+      "dependencies": {
+        "fstream": {
+          "version": "1.0.11",
+          "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
+          "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "inherits": "~2.0.0",
+            "mkdirp": ">=0.5 0",
+            "rimraf": "2"
+          }
+        }
       }
     },
     "term-size": {
         "punycode": "^1.4.1"
       }
     },
+    "traverse": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+      "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=",
+      "dev": true
+    },
     "trim": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz",
         }
       }
     },
+    "unzip": {
+      "version": "0.1.11",
+      "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.11.tgz",
+      "integrity": "sha1-iXScY7BY19kNYZ+GuYqhU107l/A=",
+      "dev": true,
+      "requires": {
+        "binary": ">= 0.3.0 < 1",
+        "fstream": ">= 0.1.30 < 1",
+        "match-stream": ">= 0.0.2 < 1",
+        "pullstream": ">= 0.4.1 < 1",
+        "readable-stream": "~1.0.31",
+        "setimmediate": ">= 1.0.1 < 2"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "dev": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+          "dev": true
+        }
+      }
+    },
     "unzip-response": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
index 5b06a03027e4c2f0b21b844d1a11af5d488ede22..9c5b05b7ac0f4ac9894d129cc308feee9103afdd 100644 (file)
@@ -55,7 +55,7 @@
     "js-test-karma-old": "cross-env USE_OLD_JQUERY=true npm run js-test-karma",
     "js-test-karma-bundle": "cross-env karma start js/tests/karma-bundle.conf.js",
     "js-test-karma-bundle-old": "cross-env USE_OLD_JQUERY=true npm run js-test-karma-bundle",
-    "js-test-cloud": "npm-run-all --parallel --race http-server saucelabs-test",
+    "js-test-cloud": "karma start js/tests/karma-browserstack.conf.js",
     "coveralls": "shx cat js/coverage/lcov.info | coveralls",
     "docs": "npm-run-all --parallel css-docs js-docs --sequential docs-compile docs-lint",
     "docs-compile": "bundle exec jekyll build",
@@ -71,7 +71,6 @@
     "release-sri": "node build/generate-sri.js",
     "release-version": "node build/change-version.js",
     "release-zip": "cd dist/ && zip -r9 bootstrap-$npm_package_version-dist.zip * && shx mv bootstrap-$npm_package_version-dist.zip ..",
-    "saucelabs-test": "node build/saucelabs-unit-test.js",
     "dist": "npm-run-all --parallel css js",
     "test": "npm-run-all dist js-test docs-compile docs-lint bundlesize",
     "watch": "npm-run-all --parallel watch-*",
     "glob": "^7.1.3",
     "htmllint-cli": "^0.0.7",
     "http-server": "^0.11.1",
-    "jsunitsaucelabs": "^1.3.2",
+    "ip": "^1.1.5",
     "karma": "^3.0.0",
+    "karma-browserstack-launcher": "^1.3.0",
     "karma-chrome-launcher": "^2.2.0",
     "karma-coverage-istanbul-reporter": "^2.0.2",
     "karma-detect-browsers": "^2.3.2",