├── .eslintrc-client ├── .eslintrc-client-test ├── .eslintrc-server ├── .eslintrc-server-test ├── .gitignore ├── .istanbul.func.yml ├── .npmignore ├── .travis.yml ├── CONTRIBUTING.md ├── HISTORY.md ├── LICENSE.txt ├── README.md ├── lib └── little-loader.js ├── package.json └── test ├── client ├── fixtures │ ├── advanced │ │ ├── four.js │ │ ├── one.js │ │ ├── three.amd.js │ │ ├── three.js │ │ └── two.js │ └── basic │ │ └── basic.js ├── requirejs │ ├── karma.conf.coverage.js │ ├── karma.conf.js │ ├── main.js │ └── spec │ │ ├── advanced.spec.js │ │ ├── basic.spec.js │ │ ├── config.spec.js │ │ └── error.spec.js └── webpack │ ├── karma.conf.coverage.js │ ├── karma.conf.js │ ├── main.js │ └── spec │ ├── advanced.spec.js │ ├── basic.spec.js │ ├── config.spec.js │ └── error.spec.js └── func ├── fixtures ├── advanced.html ├── advanced │ ├── first.js │ ├── fourth.js │ ├── second.js │ └── third.js ├── basic.html ├── basic │ └── basic.js ├── error-handler.js ├── error.html ├── jquery.html ├── jquery │ └── jquery.js └── origins.html ├── mocha-cov.opts ├── mocha.opts ├── setup-cov.js ├── setup.js └── spec ├── advanced.spec.js ├── base.spec.js ├── basic.spec.js ├── error.spec.js ├── jquery.spec.js └── origins.spec.js /.eslintrc-client: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - "defaults/configurations/walmart/es5-browser" 4 | -------------------------------------------------------------------------------- /.eslintrc-client-test: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - "defaults/configurations/walmart/es5-test" 4 | - "defaults/configurations/walmart/es5-browser" 5 | 6 | globals: 7 | expect: false 8 | define: false 9 | requirejs: false 10 | 11 | rules: 12 | no-unused-expressions: 0 13 | max-nested-callbacks: [2, 10] 14 | -------------------------------------------------------------------------------- /.eslintrc-server: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - "defaults/configurations/walmart/es5-node" 4 | -------------------------------------------------------------------------------- /.eslintrc-server-test: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - "defaults/configurations/walmart/es5-test" 4 | - "defaults/configurations/walmart/es5-node" 5 | 6 | globals: 7 | expect: false 8 | 9 | rules: 10 | no-unused-expressions: 0 11 | max-nested-callbacks: [2, 4] 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | \.git 2 | \.hg 3 | 4 | \.DS_Store 5 | \.project 6 | bower_components 7 | node_modules 8 | npm-debug\.log* 9 | phantomjsdriver\.log 10 | yarn.lock 11 | 12 | # Build 13 | dist 14 | coverage 15 | temp 16 | -------------------------------------------------------------------------------- /.istanbul.func.yml: -------------------------------------------------------------------------------- 1 | reporting: 2 | dir: coverage/func 3 | reports: 4 | - lcov 5 | - json 6 | - text-summary 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | \.git 2 | \.hg 3 | 4 | \.DS_Store 5 | \.project 6 | bower_components 7 | node_modules 8 | npm-debug\.log* 9 | phantomjsdriver\.log 10 | 11 | # Build 12 | coverage 13 | temp 14 | 15 | # NPM 16 | test 17 | .eslint* 18 | .istanbul* 19 | .travis* 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.10" 5 | 6 | # Use container-based Travis infrastructure. 7 | sudo: false 8 | 9 | branches: 10 | only: 11 | - master 12 | 13 | env: 14 | global: 15 | - TEST_FUNC_HOST=127.0.0.1 16 | # `sauce_connect` Travis add-on tunnel identifier _must_ be `TRAVIS_JOB_NUMBER` 17 | # https://docs.travis-ci.com/user/sauce-connect/ 18 | - SAUCE_CONNECT_TUNNEL_ID=$TRAVIS_JOB_NUMBER 19 | - SAUCE_USERNAME=wml-little-loader 20 | 21 | before_install: 22 | # GUI for real browsers. 23 | - export DISPLAY=:99.0 24 | - sh -e /etc/init.d/xvfb start 25 | # Upgrade to npm v2 26 | - npm install -g npm@2 27 | 28 | before_script: 29 | # Install dev. stuff (e.g., selenium drivers). 30 | - npm run install-dev 31 | # Start up processes used in parallel tests. 32 | # Note: We're playing a bit fast & loose here bceause selenium standalone needs to "just be ready" 33 | # by the time we start our tests. 34 | - nohup bash -c "node_modules/.bin/selenium-standalone start 2>&1 &" 35 | 36 | addons: 37 | jwt: 38 | secure: t9yalh16COfTgMWI58MKD5cq3/4t1jiiGmRz7xmi5T4QkRkxYzATF+C60z2nXbnsF4RLflcRBgJZ0PbR/L2JxQ3yALasougJjb0EbVr8XoJQiyhnUEQpRxtoxn9oV9Fnp3/DDQduPTZ1zPzd20N7mOpYoqNMaVnMFj353fVzI3vph29/uakvR/nNCq7BNjcOll5+JRifpXFgsACWUalU+IR/R1xmrciw7KoLlHfe8Zy349X0mv3Ez2uugqsQ5CZnRI9vffNvIWXhuVU0sCoANydYS+d5vBjO5txrxZSv14enXFKBLaXkxViYmzQMHI2FDjx71KsKeCoGoH9ooSOfwPSXSrUFTRDN0nhow3Z3uqo3EjP/uJ0I+Hoe7gJWvUDw90Q1IX2WuoSxWU+fdgEewZyvQboaC6CSaF2rdvCfl058uIbcGhhkkGAmpwvJKSsytWOJjeTA3hpYPSHGlvVLvXER1bfAIC/iBf5VGBExotNrPSpG+PsqzhpUsFSmkjY5gpKElhcNW9Ag3r5IJJJL1xVu8aXO55T5VrmTn/jINEAo91nJZhHrjX7x0kPWzlvU5/JAAOjpcrOVfpvqjkpvmxev/JtPZl2cx8lHadPXYJw98hIagpQdkfojNI5C4ACSrTVNe/IoEujgykbh9egpe8HGsG+U+g6qogmFPQFUP/8= 39 | sauce_connect: true 40 | 41 | script: 42 | # Run all base checks (with `local.phantomjs` browser for functional tests, 43 | # and PhantomJS+FF for Karma tests). 44 | - npm run check-ci 45 | 46 | # Sauce Labs 47 | # 48 | # Run all different environments in parallel for different `ROWDY_SETTINGS` 49 | # values. Flags key: 50 | # 51 | # - `buffer`: Save test output until _end_ of test run finishes (to make more 52 | # sense of concurrent output). 53 | # - `tries=2`: Try each separate test suite 2x before declaring a failures. 54 | # - `queue=NUM`: (Not Enabled) Limit concurrency to max of `NUM` tasks at the 55 | # same time. 56 | # 57 | - >- 58 | node_modules/.bin/builder envs test-func-ci --buffer --tries=2 59 | '[ { "TEST_FUNC_PORT": 3030, "ROWDY_SETTINGS":"sauceLabs.IE_8_Windows_2008_Desktop" }, 60 | { "TEST_FUNC_PORT": 3040, "ROWDY_SETTINGS":"sauceLabs.IE_9_Windows_2008_Desktop" }, 61 | { "TEST_FUNC_PORT": 3050, "ROWDY_SETTINGS":"sauceLabs.IE_10_Windows_2012_Desktop" }, 62 | { "TEST_FUNC_PORT": 3060, "ROWDY_SETTINGS":"sauceLabs.IE_11_Windows_2012_R2_Desktop" }, 63 | { "TEST_FUNC_PORT": 3070, "ROWDY_SETTINGS":"sauceLabs.safari_7_OS_X_10_9_Desktop" }, 64 | { "TEST_FUNC_PORT": 3080, "ROWDY_SETTINGS":"sauceLabs.chrome_latest_Windows_2012_R2_Desktop" }, 65 | { "TEST_FUNC_PORT": 3090, "ROWDY_SETTINGS":"sauceLabs.firefox_latest_Windows_2012_R2_Desktop" }, 66 | { "TEST_FUNC_PORT": 3100, "ROWDY_SETTINGS":"local.firefox" } ]' 67 | 68 | # Aggregate all functional coverage JSON objects to one report. 69 | - npm run cov-report-func 70 | 71 | # Manually send coverage reports to coveralls. 72 | - ls coverage/*/lcov.info coverage/client/*/*/lcov.info | cat 73 | - cat coverage/*/lcov.info coverage/client/*/*/lcov.info | ./node_modules/.bin/coveralls || echo "Coveralls upload failed" 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Community contributions are most welcome. Here are a couple of guidelines for 5 | contributions. 6 | 7 | * Use GitHub pull requests. 8 | * Reference existing open GitHub issues in commits where relevant. 9 | * Always run `npm run check` to run all style and test checks. 10 | 11 | And, thanks! 12 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | History 2 | ======= 3 | 4 | ## 0.2.0 5 | 6 | * Add options object argument for script mutation (`setup`) ( [@jknight12882][] ) 7 | [#48](https://github.com/walmartlabs/little-loader/issues/48) 8 | 9 | ## 0.1.1 10 | 11 | * Optimize for minified, gzipped size. ( [@pygy][] ) 12 | [#29](https://github.com/walmartlabs/little-loader/issues/29) 13 | 14 | ## 0.1.0 15 | 16 | * Add AMD, CommonJS Karma tests. 17 | * Add error capture for load callback. 18 | [#15](https://github.com/walmartlabs/little-loader/issues/15) 19 | 20 | ## 0.0.2 21 | 22 | * Add UMD wrapper. 23 | [#9](https://github.com/walmartlabs/little-loader/issues/9) 24 | * Tighten script code. 25 | * Add minified distribution for NPM. 26 | [#10](https://github.com/walmartlabs/little-loader/issues/10) 27 | * Add code coverage reporting. 28 | [#2](https://github.com/walmartlabs/little-loader/issues/2) 29 | 30 | ## 0.0.1 31 | 32 | * Initial implementation and functional tests. 33 | 34 | [@jknight12882]: https://github.com/jknight12882 35 | [@pygy]: https://github.com/pygy 36 | [@ryan-roemer]: https://github.com/ryan-roemer 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Brian Beck 2 | 3 | Copyright (C) 2015 Walmart Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *** 2 | # NOTICE: 3 | 4 | ## This repository has been archived and is not supported. 5 | 6 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 7 | *** 8 | NOTICE: SUPPORT FOR THIS PROJECT HAS ENDED 9 | 10 | This projected was owned and maintained by Walmart. This project has reached its end of life and Walmart no longer supports this project. 11 | 12 | We will no longer be monitoring the issues for this project or reviewing pull requests. You are free to continue using this project under the license terms or forks of this project at your own risk. This project is no longer subject to Walmart's bug bounty program or other security monitoring. 13 | 14 | 15 | ## Actions you can take 16 | 17 | We recommend you take the following action: 18 | 19 | * Review any configuration files used for build automation and make appropriate updates to remove or replace this project 20 | * Notify other members of your team and/or organization of this change 21 | * Notify your security team to help you evaluate alternative options 22 | 23 | ## Forking and transition of ownership 24 | 25 | For [security reasons](https://www.theregister.co.uk/2018/11/26/npm_repo_bitcoin_stealer/), Walmart does not transfer the ownership of our primary repos on Github or other platforms to other individuals/organizations. Further, we do not transfer ownership of packages for public package management systems. 26 | 27 | If you would like to fork this package and continue development, you should choose a new name for the project and create your own packages, build automation, etc. 28 | 29 | Please review the licensing terms of this project, which continue to be in effect even after decommission. 30 | 31 | Little Loader 32 | ============= 33 | 34 | A lightweight, IE8+ JavaScript loader that is **actually tested**... 35 | 36 | [![Travis Status][trav_img]][trav_site] 37 | [![Coverage Status][cov_img]][cov_site] 38 | ![size](http://badges.herokuapp.com/size/npm/little-loader/dist/little-loader.min.js) 39 | ![size (gz)](http://badges.herokuapp.com/size/npm/little-loader/dist/little-loader.min.js?gzip=true&label=size%20(gz)) 40 | 41 | [![Sauce Test Status][sauce_img]][sauce_site] 42 | 43 | ... with a very narrow set of objectives: 44 | 45 | * **Tested** all the way down to IE8 46 | * Reliably **calls back** after script loads 47 | * Captures script load **errors** down to IE8 48 | * Really, really **small** (clocking in at `~519` minified + gzipped bytes) 49 | * ... and **that's it**! 50 | 51 | We currently test: 52 | 53 | * Karma - _Travis_: PhantomJS, Firefox 54 | * Selenium - _Travis_: PhantomJS, Firefox 55 | * Selenium - _Sauce Labs_: 56 | * Windows: Firefox, Chrome, IE8-11 57 | * Mac: Safari 58 | 59 | ### Usage 60 | 61 | #### Integration 62 | 63 | Alone, little loader attaches to `window._lload` for loading your Javascript: 64 | 65 | ```html 66 | 72 | ``` 73 | 74 | If you use an AMD bundling tool (like RequireJS): 75 | 76 | ```js 77 | define(["little-loader"], function (load) { 78 | load("http://example.com/foo.js", function (err) { 79 | // ... your code ... 80 | }); 81 | }); 82 | ``` 83 | 84 | If you use a CommonJS bundling tool (like Webpack): 85 | 86 | ```js 87 | var load = require("little-loader"); 88 | 89 | load("http://example.com/foo.js", function (err) { 90 | // ... your code ... 91 | }); 92 | ``` 93 | 94 | #### Calling 95 | 96 | Little loader can be called in a number of ways: 97 | 98 | ```js 99 | // Load a script and don't worry about a callback 100 | load("http://foo.com/foo.js"); 101 | 102 | // Load, then callback (and optionally with context.) 103 | load("http://foo.com/foo.js", callback); 104 | load("http://foo.com/foo.js", callback, this); 105 | 106 | // Load, call `setup(script)` on the script tag before insertion, no callback 107 | load("http://foo.com/foo.js", { 108 | setup: setup, // setup(script) 109 | context: this // (optional) 110 | }); 111 | 112 | // Load, call `setup(script)` on the script tag before insertion, then 113 | // callback with context (two ways) 114 | load("http://foo.com/foo.js", { 115 | setup: setup, // setup(script) 116 | callback: callback, // callback(err) 117 | context: this 118 | }); 119 | load("http://foo.com/foo.js", { 120 | setup: setup, // setup(script) 121 | callback: callback // callback(err) 122 | }, this); 123 | ``` 124 | 125 | ### Installation 126 | 127 | #### CDN 128 | 129 | For the ready-to-use version from CDN, use 130 | 131 | ```html 132 | 133 | 134 | 135 | 136 | ``` 137 | 138 | #### NPM 139 | 140 | To include `little-loader` as part of your own build, first install from `npm`: 141 | 142 | ``` 143 | $ npm install --save little-loader 144 | ``` 145 | 146 | The library has a UMD wrapper and should work like any other AMD or CommonJS 147 | module with your favorite bundling tool (Webpack, RequireJS, etc.). 148 | 149 | If you do not use a CommonJS or AMD loader tool, then little loader will be 150 | exposed as the `window._lload` variable. 151 | 152 | ### Development 153 | 154 | Development requires two installation steps: 155 | 156 | ```sh 157 | $ npm install 158 | $ npm run install-dev 159 | ``` 160 | 161 | After that, run the full lint + tests: 162 | 163 | ```sh 164 | $ npm run check 165 | ``` 166 | 167 | You can try out the live functional tests fixtures with our static server: 168 | 169 | ```sh 170 | $ npm run server 171 | ``` 172 | 173 | and navigate to: http://127.0.0.1:3001/test/func/fixtures/ 174 | 175 | ### Tests 176 | 177 | We run both Karma (client-side) and Selenium (functional) tests. 178 | 179 | The **Karma** tests are faster and more flexible, but slightly "off" from 180 | real-world use because of their execution environment. We use Karma tests to 181 | kick the tires on our AMD and CommonJS abstractions and little, one-off use 182 | case scenarios. 183 | 184 | The **Selenium** tests are slower and klunky, but they are the "real deal" 185 | executing `little-loader` in exactly the same manner as would be used on a 186 | real web page. We use Selenium to test a core set of fundamental use cases 187 | across all browsers in our matrix. 188 | 189 | #### Parallel Local Tests 190 | 191 | Our CI is setup with a specific optimized parallel workflow. To run parallel 192 | functional tests in development, here are some helper tasks... 193 | 194 | **Local Browsers** 195 | 196 | ```sh 197 | $ TEST_PARALLEL=true \ 198 | builder envs test-func-local \ 199 | --setup=setup-local \ 200 | --buffer \ 201 | '[ { "TEST_FUNC_PORT": 3030, "ROWDY_SETTINGS":"local.phantomjs" }, 202 | { "TEST_FUNC_PORT": 3040, "ROWDY_SETTINGS":"local.firefox" }, 203 | { "TEST_FUNC_PORT": 3050, "ROWDY_SETTINGS":"local.chrome" } 204 | ]' 205 | ``` 206 | 207 | The `TEST_PARALLEL` flag indicates to not do in-test setup which would conflict 208 | with other test processes. We also rely on setting `TEST_FUNC_PORT` specifically 209 | to non-conflicting ports with at least 3 ports total from the starting number 210 | for the two separate static servers we run during tests. 211 | 212 | **Sauce Labs** 213 | 214 | To run Sauce Labs tests in parallel from a local machine, you'll need the `sc` 215 | binary, which can be force installed with: 216 | 217 | ```sh 218 | $ SAUCE_CONNECT_DOWNLOAD_ON_INSTALL=true npm install sauce-connect-launcher 219 | ``` 220 | 221 | After this, the module is available at: 222 | `node_modules/sauce-connect-launcher/sc/*/bin/sc` 223 | 224 | From there, you can invoke our helper local commands: 225 | 226 | ```sh 227 | $ TEST_PARALLEL=true \ 228 | SAUCE_USERNAME= \ 229 | SAUCE_ACCESS_KEY= \ 230 | builder envs test-func-sauce \ 231 | --setup=setup-sauce \ 232 | --buffer \ 233 | '[ { "TEST_FUNC_PORT": 3030, "ROWDY_SETTINGS":"sauceLabs.IE_8_Windows_2008_Desktop" }, 234 | { "TEST_FUNC_PORT": 3040, "ROWDY_SETTINGS":"sauceLabs.IE_9_Windows_2008_Desktop" }, 235 | { "TEST_FUNC_PORT": 3050, "ROWDY_SETTINGS":"sauceLabs.IE_10_Windows_2012_Desktop" } 236 | ]' 237 | ``` 238 | 239 | 240 | ### Releases 241 | 242 | **IMPORTANT - NPM**: To correctly run `preversion` your first step is to make 243 | sure that you have a very modern `npm` binary: 244 | 245 | ```sh 246 | $ npm install -g npm 247 | ``` 248 | 249 | First, you can optionally edit and commit the project history. 250 | 251 | ```sh 252 | $ vim HISTORY.md 253 | $ git add HISTORY.md 254 | $ git commit -m "Update history for VERSION" 255 | ``` 256 | 257 | Now we're ready to publish. Choose a semantic update for the new version. 258 | If you're unsure, read about semantic versioning at http://semver.org/ 259 | 260 | ```sh 261 | $ npm version VERSION|major|minor|patch -m "Version %s - INSERT_REASONS" 262 | ``` 263 | 264 | Now `postversion` will push to git and publish to NPM. 265 | 266 | [trav_img]: https://api.travis-ci.org/walmartlabs/little-loader.svg 267 | [trav_site]: https://travis-ci.org/walmartlabs/little-loader 268 | [sauce]: https://saucelabs.com 269 | [sauce_img]: http://badges.herokuapp.com/sauce/wml-little-loader 270 | 271 | [sauce_site]: https://saucelabs.com/u/wml-little-loader 272 | [cov]: https://coveralls.io 273 | [cov_img]: https://img.shields.io/coveralls/walmartlabs/little-loader.svg 274 | [cov_site]: https://coveralls.io/r/walmartlabs/little-loader 275 | -------------------------------------------------------------------------------- /lib/little-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Script loading is difficult thanks to IE. We need callbacks to fire 3 | * immediately following the script's execution, with no other scripts 4 | * running in between. If other scripts on the page are able to run 5 | * between our script and its callback, bad things can happen, such as 6 | * `jQuery.noConflict` not being called in time, resulting in plugins 7 | * latching onto our version of jQuery, etc. 8 | * 9 | * For IE<10 we use a relatively well-documented "preloading" strategy, 10 | * which ensures that the script is ready to execute *before* appending 11 | * it to the DOM. That way when it is finally appended, it is 12 | * executed immediately. 13 | * 14 | * References: 15 | * - http://www.html5rocks.com/en/tutorials/speed/script-loading/ 16 | * - http://blog.getify.com/ie11-please-bring-real-script-preloading-back/ 17 | * - https://github.com/jrburke/requirejs/issues/526 18 | * - https://connect.microsoft.com/IE/feedback/details/729164/ 19 | * ie10-dynamic-script-element-fires-loaded-readystate-prematurely 20 | */ 21 | (function () { 22 | 23 | // Global state. 24 | var pendingScripts = {}; 25 | var scriptCounter = 0; 26 | 27 | /** 28 | * Insert script into the DOM 29 | * 30 | * @param {Object} script Script DOM object 31 | * @returns {void} 32 | */ 33 | var _addScript = function (script) { 34 | // Get the first script element, we're just going to use it 35 | // as a reference for where to insert ours. Do NOT try to do 36 | // this just once at the top and then re-use the same script 37 | // as a reference later. Some weird loaders *remove* script 38 | // elements after the browser has executed their contents, 39 | // so the same reference might not have a parentNode later. 40 | var firstScript = document.getElementsByTagName("script")[0]; 41 | 42 | // Append the script to the DOM, triggering execution. 43 | firstScript.parentNode.insertBefore(script, firstScript); 44 | }; 45 | 46 | /** 47 | * Load Script. 48 | * 49 | * @param {String} src URI of script 50 | * @param {Function|Object} callback (Optional) Called on script load completion, 51 | * or options object 52 | * @param {Object} context (Optional) Callback context (`this`) 53 | * @returns {void} 54 | */ 55 | var _lload = function (src, callback, context) { 56 | /*eslint max-statements: [2, 32]*/ 57 | var setup; 58 | 59 | if (callback && typeof callback !== "function") { 60 | context = callback.context || context; 61 | setup = callback.setup; 62 | callback = callback.callback; 63 | } 64 | 65 | var script = document.createElement("script"); 66 | var done = false; 67 | var err; 68 | var _cleanup; // _must_ be set below. 69 | 70 | /** 71 | * Final handler for error or completion. 72 | * 73 | * **Note**: Will only be called _once_. 74 | * 75 | * @returns {void} 76 | */ 77 | var _finish = function () { 78 | // Only call once. 79 | if (done) { return; } 80 | done = true; 81 | 82 | // Internal cleanup. 83 | _cleanup(); 84 | 85 | // Callback. 86 | if (callback) { 87 | callback.call(context, err); 88 | } 89 | }; 90 | 91 | /** 92 | * Error handler 93 | * 94 | * @returns {void} 95 | */ 96 | var _error = function () { 97 | err = new Error(src || "EMPTY"); 98 | _finish(); 99 | }; 100 | 101 | if (script.readyState && !("async" in script)) { 102 | /*eslint-disable consistent-return*/ 103 | 104 | // This section is only for IE<10. Some other old browsers may 105 | // satisfy the above condition and enter this branch, but we don't 106 | // support those browsers anyway. 107 | 108 | var id = scriptCounter++; 109 | var isReady = { loaded: true, complete: true }; 110 | var inserted = false; 111 | 112 | // Clear out listeners, state. 113 | _cleanup = function () { 114 | script.onreadystatechange = script.onerror = null; 115 | pendingScripts[id] = void 0; 116 | }; 117 | 118 | // Attach the handler before setting src, otherwise we might 119 | // miss events (consider that IE could fire them synchronously 120 | // upon setting src, for example). 121 | script.onreadystatechange = function () { 122 | var firstState = script.readyState; 123 | 124 | // Protect against any errors from state change randomness. 125 | if (err) { return; } 126 | 127 | if (!inserted && isReady[firstState]) { 128 | inserted = true; 129 | 130 | // Append to DOM. 131 | _addScript(script); 132 | } 133 | 134 | // -------------------------------------------------------------------- 135 | // GLORIOUS IE8 HACKAGE!!! 136 | // -------------------------------------------------------------------- 137 | // 138 | // Oh IE8, how you disappoint. IE8 won't call `script.onerror`, so 139 | // we have to resort to drastic measures. 140 | // See, e.g. http://www.quirksmode.org/dom/events/error.html#t02 141 | // 142 | // As with all things development, there's a Stack Overflow comment that 143 | // asserts the following combinations of state changes in IE8 indicate a 144 | // script load error. And crazily, it seems to work! 145 | // 146 | // http://stackoverflow.com/a/18840568/741892 147 | // 148 | // The `script.readyState` transitions we're interested are: 149 | // 150 | // * If state starts as `loaded` 151 | // * Call `script.children`, which _should_ change state to `complete` 152 | // * If state is now `loading`, then **we have a load error** 153 | // 154 | // For the reader's amusement, here is HeadJS's catalog of various 155 | // `readyState` transitions in normal operation for IE: 156 | // https://github.com/headjs/headjs/blob/master/src/2.0.0/load.js#L379-L419 157 | if (firstState === "loaded") { 158 | // The act of accessing the property should change the script's 159 | // `readyState`. 160 | // 161 | // And, oh yeah, this hack is so hacky-ish we need the following 162 | // eslint disable... 163 | /*eslint-disable no-unused-expressions*/ 164 | script.children; 165 | /*eslint-enable no-unused-expressions*/ 166 | 167 | if (script.readyState === "loading") { 168 | // State transitions indicate we've hit the load error. 169 | // 170 | // **Note**: We are not intending to _return_ a value, just have 171 | // a shorter short-circuit code path here. 172 | return _error(); 173 | } 174 | } 175 | 176 | // It's possible for readyState to be "complete" immediately 177 | // after we insert (and execute) the script in the branch 178 | // above. So check readyState again here and react without 179 | // waiting for another onreadystatechange. 180 | if (script.readyState === "complete") { 181 | _finish(); 182 | } 183 | }; 184 | 185 | // Onerror handler _may_ work here. 186 | script.onerror = _error; 187 | 188 | // Since we're not appending the script to the DOM yet, the 189 | // reference to our script element might get garbage collected 190 | // when this function ends, without onreadystatechange ever being 191 | // fired. This has been witnessed to happen. Adding it to 192 | // `pendingScripts` ensures this can't happen. 193 | pendingScripts[id] = script; 194 | 195 | // call the setup callback to mutate the script tag 196 | if (setup) { 197 | setup.call(context, script); 198 | } 199 | 200 | // This triggers a request for the script, but its contents won't 201 | // be executed until we append it to the DOM. 202 | script.src = src; 203 | 204 | // In some cases, the readyState is already "loaded" immediately 205 | // after setting src. It's a lie! Don't append to the DOM until 206 | // the onreadystatechange event says so. 207 | 208 | } else { 209 | // This section is for modern browsers, including IE10+. 210 | 211 | // Clear out listeners. 212 | _cleanup = function () { 213 | script.onload = script.onerror = null; 214 | }; 215 | 216 | script.onerror = _error; 217 | script.onload = _finish; 218 | script.async = true; 219 | script.charset = "utf-8"; 220 | 221 | // call the setup callback to mutate the script tag 222 | if (setup) { 223 | setup.call(context, script); 224 | } 225 | 226 | script.src = src; 227 | 228 | // Append to DOM. 229 | _addScript(script); 230 | } 231 | }; 232 | 233 | // UMD wrapper. 234 | /*global define:false*/ 235 | if (typeof exports === "object" && typeof module === "object") { 236 | // CommonJS 237 | module.exports = _lload; 238 | 239 | } else if (typeof define === "function" && define.amd) { 240 | // AMD 241 | define([], function () { return _lload; }); 242 | 243 | } else { 244 | // VanillaJS 245 | window._lload = _lload; 246 | } 247 | }()); 248 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "little-loader", 3 | "version": "0.2.0", 4 | "description": "A lightweight, IE8+ JavaScript loader.", 5 | "main": "lib/little-loader.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/walmartlabs/little-loader.git" 9 | }, 10 | "contributors": [ 11 | { 12 | "name": "Brian Beck", 13 | "email": "brian.beck@formidable.com" 14 | }, 15 | { 16 | "name": "Ryan Roemer", 17 | "email": "ryan.roemer@formidable.com" 18 | } 19 | ], 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/walmartlabs/little-loader/issues" 23 | }, 24 | "dependencies": {}, 25 | "devDependencies": { 26 | "builder": "^2.3.1", 27 | "chai": "^3.4.1", 28 | "coveralls": "^2.11.4", 29 | "eslint": "^1.10.3", 30 | "eslint-config-defaults": "^9.0.0", 31 | "eslint-plugin-filenames": "^0.2.0", 32 | "guacamole": "^1.1.4", 33 | "http-server": "^0.8.5", 34 | "istanbul": "^0.4.0", 35 | "istanbul-instrumenter-loader": "^0.2.0", 36 | "karma": "^0.13.15", 37 | "karma-chrome-launcher": "^0.2.2", 38 | "karma-coverage": "^0.5.3", 39 | "karma-firefox-launcher": "^0.1.7", 40 | "karma-ie-launcher": "^0.2.0", 41 | "karma-mocha": "^0.2.1", 42 | "karma-phantomjs-launcher": "^1.0.0", 43 | "karma-phantomjs-shim": "^1.1.2", 44 | "karma-requirejs": "^0.2.2", 45 | "karma-safari-launcher": "^0.1.1", 46 | "karma-sauce-launcher": "^0.3.0", 47 | "karma-sinon": "^1.0.5", 48 | "karma-spec-reporter": "0.0.24", 49 | "karma-webpack": "^1.7.0", 50 | "mocha": "^2.3.4", 51 | "node-uuid": "^1.4.7", 52 | "phantomjs": "^2.1.3", 53 | "phantomjs-prebuilt": "^2.1.4", 54 | "requirejs": "^2.1.22", 55 | "rimraf": "^2.4.4", 56 | "rowdy": "^0.4.0", 57 | "sauce-connect-launcher": "^0.14.0", 58 | "saucelabs": "^1.0.1", 59 | "selenium-standalone": "^4.9.1", 60 | "server-destroy": "^1.0.1", 61 | "sinon": "^1.17.6", 62 | "sinon-chai": "^2.8.0", 63 | "uglify-js": "^2.6.1", 64 | "webdriverio": "^4.0.3", 65 | "webpack": "^1.12.9" 66 | }, 67 | "scripts": { 68 | "lint-client": "eslint -c .eslintrc-client lib test/func/fixtures", 69 | "lint-client-test": "eslint -c .eslintrc-client-test test/client/*/spec test/client/*/main.js", 70 | "lint-server": "eslint -c .eslintrc-server *.js", 71 | "lint-server-test": "eslint -c .eslintrc-server-test test/func/*.js test/func/spec", 72 | "lint": "builder concurrent lint-client lint-client-test lint-server lint-server-test", 73 | "clean-cov": "rimraf coverage/func", 74 | "test-client-rjs": "karma start test/client/requirejs/karma.conf.js", 75 | "test-client-rjs-ci": "karma start --browsers PhantomJS,Firefox test/client/requirejs/karma.conf.coverage.js", 76 | "test-client-rjs-cov": "karma start test/client/requirejs/karma.conf.coverage.js", 77 | "test-client-wp": "karma start test/client/webpack/karma.conf.js", 78 | "test-client-wp-ci": "karma start --browsers PhantomJS,Firefox test/client/webpack/karma.conf.coverage.js", 79 | "test-client-wp-cov": "karma start test/client/webpack/karma.conf.coverage.js", 80 | "test-client": "builder concurrent --buffer test-client-rjs test-client-wp", 81 | "test-client-ci": "builder concurrent --buffer test-client-rjs-ci test-client-wp-ci", 82 | "test-client-cov": "builder concurrent --buffer test-client-rjs-cov test-client-wp-cov", 83 | "test-func": "mocha --colors --opts test/func/mocha.opts test/func/spec/", 84 | "test-func-cov": "mocha --colors --opts test/func/mocha-cov.opts test/func/spec/", 85 | "test-func-ci": "builder run test-func-cov", 86 | "test": "builder run test-client && builder run test-func", 87 | "test-ci": "builder concurrent --buffer test-client-ci test-func-ci", 88 | "setup-local-selenium": "selenium-standalone start >/dev/null 2>&1", 89 | "setup-local": "builder run setup-local-selenium", 90 | "test-func-local": "sleep 5 && echo \"Starting ${ROWDY_SETTINGS}\" && builder run test-func-cov", 91 | "setup-sauce-connect": "node_modules/sauce-connect-launcher/sc/*/bin/sc", 92 | "setup-sauce": "builder run setup-sauce-connect", 93 | "test-func-sauce": "sleep 20 && echo \"Starting ${ROWDY_SETTINGS}\" && builder run test-func-cov", 94 | "cov-report-func": "istanbul report --config .istanbul.func.yml --include 'coverage/func/data/coverage-*.json'", 95 | "check": "builder run lint && builder run test", 96 | "check-ci": "builder run lint && builder run test-ci", 97 | "server": "http-server -p 3001 .", 98 | "install-dev": "selenium-standalone install", 99 | "build": "rimraf dist && mkdir dist && uglifyjs lib/little-loader.js --stats --compress --mangle --output=dist/little-loader.min.js", 100 | "preversion": "builder run check", 101 | "version": "builder run build", 102 | "postversion": "git push && git push --tags && npm publish" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/client/fixtures/advanced/four.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var test = window._LLOAD_TEST = window._LLOAD_TEST || {}; 3 | test.four = "four"; 4 | }()); 5 | -------------------------------------------------------------------------------- /test/client/fixtures/advanced/one.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | window._LLOAD_TEST = window._LLOAD_TEST || {}; 3 | window._LLOAD_TEST.one = "one"; 4 | }()); 5 | -------------------------------------------------------------------------------- /test/client/fixtures/advanced/three.amd.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var test = window._LLOAD_TEST = window._LLOAD_TEST || {}; 3 | test.three = "three"; 4 | test.getFour = function (callback) { 5 | require(["lib/little-loader"], function (load) { 6 | load("/base/test/client/fixtures/advanced/four.js", callback); 7 | }); 8 | }; 9 | }()); 10 | -------------------------------------------------------------------------------- /test/client/fixtures/advanced/three.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic script. 3 | */ 4 | (function () { 5 | var test = window._LLOAD_TEST = window._LLOAD_TEST || {}; 6 | test.three = "three"; 7 | }()); 8 | -------------------------------------------------------------------------------- /test/client/fixtures/advanced/two.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic script. 3 | */ 4 | (function () { 5 | window._LLOAD_TEST = window._LLOAD_TEST || {}; 6 | window._LLOAD_TEST.two = "two"; 7 | }()); 8 | -------------------------------------------------------------------------------- /test/client/fixtures/basic/basic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic script. 3 | */ 4 | (function () { 5 | window._LLOAD_TEST = window._LLOAD_TEST || {}; 6 | window._LLOAD_TEST.basic = "basic"; 7 | }()); 8 | -------------------------------------------------------------------------------- /test/client/requirejs/karma.conf.coverage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * Karma Configuration: "coverage" version. 4 | * 5 | * This configuration is the same as basic one-shot version, just with coverage. 6 | */ 7 | var path = require("path"); 8 | var ROOT = path.join(__dirname, "../../.."); 9 | 10 | module.exports = function (config) { 11 | /* eslint-disable global-require */ 12 | require("./karma.conf")(config); 13 | config.set({ 14 | reporters: ["spec", "coverage"], 15 | preprocessors: { 16 | "lib/little-loader.js": ["coverage"] 17 | }, 18 | coverageReporter: { 19 | reporters: [ 20 | { type: "json", file: "coverage.json" }, 21 | { type: "lcov" }, 22 | { type: "text-summary" } 23 | ], 24 | dir: path.join(ROOT, "coverage/client/requirejs") 25 | } 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /test/client/requirejs/karma.conf.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * Karma Configuration 4 | */ 5 | var path = require("path"); 6 | var ROOT = path.join(__dirname, "../../.."); 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | frameworks: ["mocha", "phantomjs-shim", "requirejs", "sinon"], 11 | reporters: ["spec"], 12 | browsers: ["PhantomJS"], 13 | basePath: ROOT, 14 | files: [ 15 | { pattern: "node_modules/chai/chai.js", included: false }, 16 | { pattern: "node_modules/sinon-chai/lib/sinon-chai.js", included: false }, 17 | { pattern: "lib/**/*.js", included: false }, 18 | { pattern: "test/client/fixtures/**/*.js", included: false }, 19 | { pattern: "test/client/requirejs/**/*.spec.js", included: false }, 20 | 21 | "test/client/requirejs/main.js" 22 | ], 23 | port: 9999, 24 | singleRun: true, 25 | client: { 26 | captureConsole: true, 27 | mocha: { 28 | ui: "bdd" 29 | } 30 | } 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /test/client/requirejs/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test setup for client-side tests. 3 | */ 4 | // TODO: Add expect.js + ie8 compat. 5 | // https://github.com/walmartlabs/little-loader/issues/17 6 | 7 | (function () { 8 | requirejs.config({ 9 | // Karma serves files from '/base' 10 | baseUrl: "/base", 11 | 12 | paths: { 13 | "chai": "node_modules/chai/chai", 14 | "sinon-chai": "node_modules/sinon-chai/lib/sinon-chai" 15 | } 16 | }); 17 | 18 | require(["chai", "sinon-chai"], function (chai, sinonChai) { 19 | /*globals window:false*/ 20 | 21 | // ------------------------------------------------------------------------ 22 | // Chai / Mocha configuration. 23 | // ------------------------------------------------------------------------ 24 | // Integration 25 | chai.use(sinonChai); 26 | 27 | // Exports 28 | window.expect = chai.expect; 29 | 30 | // Mocha (part of static include). 31 | window.mocha.setup({ 32 | ui: "bdd", 33 | bail: false 34 | }); 35 | 36 | // ------------------------------------------------------------------------ 37 | // Bootstrap 38 | // ------------------------------------------------------------------------ 39 | var tests = []; 40 | for (var file in window.__karma__.files) { 41 | if (/\.spec\.js$/.test(file)) { 42 | tests.push(file); 43 | } 44 | } 45 | 46 | require(tests, function () { 47 | window.__karma__.start(); 48 | }); 49 | }); 50 | }()); 51 | -------------------------------------------------------------------------------- /test/client/requirejs/spec/advanced.spec.js: -------------------------------------------------------------------------------- 1 | define(["lib/little-loader"], function (load) { 2 | describe("requirejs:advanced", function () { 3 | 4 | beforeEach(function () { 5 | window._LLOAD_TEST = {}; 6 | }); 7 | 8 | it("loads nested scripts", function (done) { 9 | load("/base/test/client/fixtures/advanced/one.js", function (err1) { 10 | expect(err1).to.not.be.ok; 11 | expect(window._LLOAD_TEST.one).to.equal("one"); 12 | 13 | load("/base/test/client/fixtures/advanced/two.js", function (err2) { 14 | expect(err2).to.not.be.ok; 15 | expect(window._LLOAD_TEST.two).to.equal("two"); 16 | 17 | load("/base/test/client/fixtures/advanced/three.js", function (err3) { 18 | expect(err3).to.not.be.ok; 19 | expect(window._LLOAD_TEST.three).to.equal("three"); 20 | 21 | load("/base/test/client/fixtures/advanced/four.js", function (err4) { 22 | expect(err4).to.not.be.ok; 23 | expect(window._LLOAD_TEST.four).to.equal("four"); 24 | done(); 25 | }); 26 | }); 27 | }); 28 | }); 29 | }); 30 | 31 | it("loads requirejs callback to load", function (done) { 32 | load("/base/test/client/fixtures/advanced/one.js", function (err1) { 33 | expect(err1).to.not.be.ok; 34 | expect(window._LLOAD_TEST.one).to.equal("one"); 35 | 36 | load("/base/test/client/fixtures/advanced/two.js", function (err2) { 37 | expect(err2).to.not.be.ok; 38 | expect(window._LLOAD_TEST.two).to.equal("two"); 39 | 40 | load("/base/test/client/fixtures/advanced/three.amd.js", function (err3) { 41 | expect(err3).to.not.be.ok; 42 | expect(window._LLOAD_TEST.three).to.equal("three"); 43 | 44 | window._LLOAD_TEST.getFour(function () { 45 | expect(window._LLOAD_TEST.four).to.equal("four"); 46 | done(); 47 | }); 48 | }); 49 | }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/client/requirejs/spec/basic.spec.js: -------------------------------------------------------------------------------- 1 | define(["lib/little-loader"], function (load) { 2 | describe("requirejs:basic", function () { 3 | 4 | it("load is a function", function () { 5 | expect(load).to.be.a("function"); 6 | }); 7 | 8 | it("loads basic", function (done) { 9 | load("/base/test/client/fixtures/basic/basic.js", function (err) { 10 | expect(err).to.not.be.ok; 11 | expect(window._LLOAD_TEST).to.be.ok; 12 | expect(window._LLOAD_TEST.basic).to.equal("basic"); 13 | done(); 14 | }); 15 | }); 16 | 17 | it("uses context", function (done) { 18 | var obj = { greeting: "hi" }; 19 | 20 | load("/base/test/client/fixtures/basic/basic.js", function (err) { 21 | expect(err).to.not.be.ok; 22 | expect(this.greeting).to.equal("hi"); 23 | done(); 24 | }, obj); 25 | }); 26 | 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/client/requirejs/spec/config.spec.js: -------------------------------------------------------------------------------- 1 | /*global sinon: false */ 2 | 3 | define(["lib/little-loader"], function (load) { 4 | describe("requirejs:config", function () { 5 | 6 | it("loads basic", function (done) { 7 | var config = { 8 | callback: function (err) { 9 | expect(err).to.not.be.ok; 10 | expect(window._LLOAD_TEST).to.be.ok; 11 | expect(window._LLOAD_TEST.basic).to.equal("basic"); 12 | done(); 13 | } 14 | }; 15 | 16 | load("/base/test/client/fixtures/basic/basic.js", config); 17 | }); 18 | 19 | it("uses context", function (done) { 20 | var config = { 21 | callback: function (err) { 22 | expect(err).to.not.be.ok; 23 | expect(this.greeting).to.equal("hi"); 24 | done(); 25 | }, 26 | 27 | context: { greeting: "hi" } 28 | }; 29 | 30 | load("/base/test/client/fixtures/basic/basic.js", config); 31 | }); 32 | 33 | it("calls setup", function (done) { 34 | var setup = sinon.spy(); 35 | var context = {}; 36 | 37 | var config = { 38 | callback: function (err) { 39 | expect(err).to.not.be.ok; 40 | expect(setup) 41 | .to.have.callCount(1).and 42 | .to.be.calledOn(context); 43 | 44 | done(); 45 | }, 46 | 47 | context: context, 48 | setup: setup 49 | }; 50 | 51 | load("/base/test/client/fixtures/basic/basic.js", config); 52 | }); 53 | 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/client/requirejs/spec/error.spec.js: -------------------------------------------------------------------------------- 1 | define(["lib/little-loader"], function (load) { 2 | describe("requirejs:error", function () { 3 | 4 | it("errs on 404", function (done) { 5 | load("DOESNT_EXIST.js", function (err) { 6 | expect(err).to.be.ok; 7 | expect(err.message || err.toString()).to.contain("DOESNT_EXIST.js"); 8 | done(); 9 | }); 10 | }); 11 | 12 | it("errs on undefined url", function (done) { 13 | load(undefined, function (err) { 14 | expect(err).to.be.ok; 15 | expect(err.message || err.toString()).to.contain("EMPTY"); 16 | done(); 17 | }); 18 | }); 19 | 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/client/webpack/karma.conf.coverage.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * Karma Configuration: "coverage" version. 4 | * 5 | * This configuration is the same as basic one-shot version, just with coverage. 6 | */ 7 | var path = require("path"); 8 | var ROOT = path.join(__dirname, "../../.."); 9 | 10 | module.exports = function (config) { 11 | /* eslint-disable global-require */ 12 | require("./karma.conf")(config); 13 | 14 | // Mutate and manually instrument. 15 | config.webpack.module = { 16 | preLoaders: [ 17 | { 18 | test: /lib\/.*\.js$/, 19 | exclude: /(test|node_modules)\//, 20 | loader: "istanbul-instrumenter" 21 | } 22 | ] 23 | }; 24 | 25 | config.set({ 26 | reporters: ["spec", "coverage"], 27 | preprocessors: { 28 | "test/client/webpack/main.js": ["webpack"] 29 | }, 30 | webpack: config.webpack, 31 | coverageReporter: { 32 | reporters: [ 33 | { type: "json", file: "coverage.json" }, 34 | { type: "lcov" }, 35 | { type: "text-summary" } 36 | ], 37 | dir: path.join(ROOT, "coverage/client/webpack") 38 | } 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /test/client/webpack/karma.conf.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | * Karma Configuration 4 | */ 5 | var path = require("path"); 6 | var ROOT = path.join(__dirname, "../../.."); 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | frameworks: ["mocha", "phantomjs-shim", "sinon"], 11 | reporters: ["spec"], 12 | browsers: ["PhantomJS"], 13 | basePath: ROOT, 14 | preprocessors: { 15 | "test/client/webpack/main.js": ["webpack"] 16 | }, 17 | files: [ 18 | { pattern: "test/client/fixtures/**/*.js", included: false }, 19 | 20 | "test/client/webpack/main.js" 21 | ], 22 | port: 9999, 23 | singleRun: true, 24 | client: { 25 | captureConsole: true, 26 | mocha: { 27 | ui: "bdd" 28 | } 29 | }, 30 | webpack: { 31 | cache: true, 32 | context: path.join(ROOT, "test/client/webpack"), 33 | entry: "./main", 34 | output: { 35 | filename: "main.js", 36 | publicPath: "/assets/" 37 | }, 38 | resolve: { 39 | alias: { 40 | // Allow root import of `lib/FOO` from ROOT/lib. 41 | lib: path.join(ROOT, "lib") 42 | } 43 | }, 44 | devtool: "source-map" 45 | }, 46 | webpackServer: { 47 | port: 3002, // Choose a non-conflicting port (3001 static serve) 48 | quiet: true, 49 | noInfo: true 50 | } 51 | }); 52 | }; 53 | -------------------------------------------------------------------------------- /test/client/webpack/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test setup for client-side tests. 3 | */ 4 | // TODO: Add expect.js + ie8 compat. 5 | // https://github.com/walmartlabs/little-loader/issues/17 6 | 7 | /*globals window:false*/ 8 | var chai = require("chai"); 9 | var sinonChai = require("sinon-chai"); 10 | 11 | // -------------------------------------------------------------------------- 12 | // Chai / Mocha configuration. 13 | // -------------------------------------------------------------------------- 14 | // Integration 15 | chai.use(sinonChai); 16 | 17 | // Exports 18 | window.expect = chai.expect; 19 | 20 | // Mocha (part of static include). 21 | window.mocha.setup({ 22 | ui: "bdd", 23 | bail: false 24 | }); 25 | 26 | // -------------------------------------------------------------------------- 27 | // Bootstrap 28 | // -------------------------------------------------------------------------- 29 | // Use webpack to infer and `require` tests automatically. 30 | var testsReq = require.context(".", true, /\.spec\.js$/); 31 | testsReq.keys().map(testsReq); 32 | -------------------------------------------------------------------------------- /test/client/webpack/spec/advanced.spec.js: -------------------------------------------------------------------------------- 1 | var load = require("lib/little-loader"); 2 | 3 | describe("webpack:advanced", function () { 4 | 5 | beforeEach(function () { 6 | window._LLOAD_TEST = {}; 7 | }); 8 | 9 | it("loads nested scripts", function (done) { 10 | load("/base/test/client/fixtures/advanced/one.js", function (err1) { 11 | expect(err1).to.not.be.ok; 12 | expect(window._LLOAD_TEST.one).to.equal("one"); 13 | 14 | load("/base/test/client/fixtures/advanced/two.js", function (err2) { 15 | expect(err2).to.not.be.ok; 16 | expect(window._LLOAD_TEST.two).to.equal("two"); 17 | 18 | load("/base/test/client/fixtures/advanced/three.js", function (err3) { 19 | expect(err3).to.not.be.ok; 20 | expect(window._LLOAD_TEST.three).to.equal("three"); 21 | 22 | load("/base/test/client/fixtures/advanced/four.js", function (err4) { 23 | expect(err4).to.not.be.ok; 24 | expect(window._LLOAD_TEST.four).to.equal("four"); 25 | done(); 26 | }); 27 | }); 28 | }); 29 | }); 30 | }); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /test/client/webpack/spec/basic.spec.js: -------------------------------------------------------------------------------- 1 | var load = require("lib/little-loader"); 2 | 3 | describe("webpack:basic", function () { 4 | 5 | it("load is a function", function () { 6 | expect(load).to.be.a("function"); 7 | }); 8 | 9 | it("loads basic", function (done) { 10 | load("/base/test/client/fixtures/basic/basic.js", function (err) { 11 | expect(err).to.not.be.ok; 12 | expect(window._LLOAD_TEST).to.be.ok; 13 | expect(window._LLOAD_TEST.basic).to.equal("basic"); 14 | done(); 15 | }); 16 | }); 17 | 18 | it("uses context", function (done) { 19 | var obj = { greeting: "hi" }; 20 | 21 | load("/base/test/client/fixtures/basic/basic.js", function (err) { 22 | expect(err).to.not.be.ok; 23 | expect(this.greeting).to.equal("hi"); 24 | done(); 25 | }, obj); 26 | }); 27 | 28 | }); 29 | -------------------------------------------------------------------------------- /test/client/webpack/spec/config.spec.js: -------------------------------------------------------------------------------- 1 | /*global sinon: false */ 2 | var load = require("lib/little-loader"); 3 | 4 | describe("webpack:config", function () { 5 | 6 | it("loads basic", function (done) { 7 | var config = { 8 | callback: function (err) { 9 | expect(err).to.not.be.ok; 10 | expect(window._LLOAD_TEST).to.be.ok; 11 | expect(window._LLOAD_TEST.basic).to.equal("basic"); 12 | done(); 13 | } 14 | }; 15 | 16 | load("/base/test/client/fixtures/basic/basic.js", config); 17 | }); 18 | 19 | it("uses context", function (done) { 20 | var config = { 21 | callback: function (err) { 22 | expect(err).to.not.be.ok; 23 | expect(this.greeting).to.equal("hi"); 24 | done(); 25 | }, 26 | 27 | context: { greeting: "hi" } 28 | }; 29 | 30 | load("/base/test/client/fixtures/basic/basic.js", config); 31 | }); 32 | 33 | it("calls setup", function (done) { 34 | var setup = sinon.spy(); 35 | var context = {}; 36 | 37 | var config = { 38 | callback: function (err) { 39 | expect(err).to.not.be.ok; 40 | expect(setup) 41 | .to.have.callCount(1).and 42 | .to.be.calledOn(context); 43 | 44 | done(); 45 | }, 46 | 47 | context: context, 48 | setup: setup 49 | }; 50 | 51 | load("/base/test/client/fixtures/basic/basic.js", config); 52 | }); 53 | 54 | }); 55 | -------------------------------------------------------------------------------- /test/client/webpack/spec/error.spec.js: -------------------------------------------------------------------------------- 1 | var load = require("lib/little-loader"); 2 | 3 | describe("webpack:error", function () { 4 | 5 | it("errs on 404", function (done) { 6 | load("DOESNT_EXIST.js", function (err) { 7 | expect(err).to.be.ok; 8 | expect(err.message || err.toString()).to.contain("DOESNT_EXIST.js"); 9 | done(); 10 | }); 11 | }); 12 | 13 | it("errs on undefined url", function (done) { 14 | load(undefined, function (err) { 15 | expect(err).to.be.ok; 16 | expect(err.message || err.toString()).to.contain("EMPTY"); 17 | done(); 18 | }); 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /test/func/fixtures/advanced.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fixture 5 | 6 | 7 |
8 |
9 | 10 | 11 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /test/func/fixtures/advanced/first.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var content = document.querySelector(".e2e-content"); 3 | content.innerHTML += "
First Script
"; 4 | }()); 5 | -------------------------------------------------------------------------------- /test/func/fixtures/advanced/fourth.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var content = document.querySelector(".e2e-content"); 3 | content.innerHTML += "
Fourth Script
"; 4 | }()); 5 | -------------------------------------------------------------------------------- /test/func/fixtures/advanced/second.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var content = document.querySelector(".e2e-content"); 3 | content.innerHTML += "
Second Script
"; 4 | 5 | window._lload("advanced/fourth.js", { 6 | callback: function () { 7 | content.innerHTML += "
After Load Fourth
"; 8 | }, 9 | setup: function () { 10 | content.innerHTML += "
After Setup Fourth
"; 11 | } 12 | }); 13 | }()); 14 | -------------------------------------------------------------------------------- /test/func/fixtures/advanced/third.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var content = document.querySelector(".e2e-content"); 3 | content.innerHTML += "
Third Script
"; 4 | }()); 5 | -------------------------------------------------------------------------------- /test/func/fixtures/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fixture 5 | 6 | 7 |
8 |
9 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/func/fixtures/basic/basic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic script. 3 | */ 4 | (function () { 5 | var content = document.querySelector(".e2e-content"); 6 | content.innerHTML += "
Basic Script
"; 7 | }()); 8 | -------------------------------------------------------------------------------- /test/func/fixtures/error-handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Error handler for all tests. 3 | */ 4 | (function () { 5 | /*eslint-disable max-params*/ 6 | window.onerror = function (msg, file, line, col, error) { 7 | var content = document.querySelector(".e2e-error"); 8 | content.innerHTML += "" 9 | + "msg: " + msg + "
\n" 10 | + "file: " + file + "
\n" 11 | + "line: " + line + "
\n" 12 | + "col: " + col + "
\n" 13 | + "error: " + error + "
\n" 14 | + "
"; 15 | }; 16 | }()); 17 | -------------------------------------------------------------------------------- /test/func/fixtures/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fixture 5 | 6 | 7 |
8 |
9 | 10 | 11 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/func/fixtures/jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fixture 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/func/fixtures/jquery/jquery.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // These are "live" tests meaning you need an internet connection. 3 | var load = window._lload; 4 | var content = document.querySelector(".e2e-content"); 5 | 6 | // DOM writing helper 7 | var output = function (name, err, result) { 8 | content.innerHTML += 9 | "
" + 10 | (err ? err.message || err.toString() : "") + 11 | "
" + 12 | 13 | "
" + 14 | (result || "") + 15 | "
"; 16 | }; 17 | 18 | // Append status information of jQuery noConflict state to DOM content. 19 | var check = function (host, version, extra) { 20 | var name = [host, version.replace(/\./g, "-"), extra].join("-"); 21 | var url = host === "google" ? 22 | "http://ajax.googleapis.com/ajax/libs/jquery/" + version + "/jquery.min.js" : 23 | "http://code.jquery.com/jquery-" + version + ".min.js"; 24 | 25 | load(url, function (err) { 26 | var results = ""; 27 | 28 | if (typeof window.jQuery !== "function") { 29 | results += "FAIL: jquery function, "; 30 | } 31 | 32 | // Remove the global. 33 | var jQuery = window.jQuery.noConflict(true); 34 | 35 | if (typeof jQuery._MARKED !== "undefined") { 36 | results += "FAIL: jquery marked, "; 37 | } 38 | 39 | if (jQuery.fn.jquery !== version) { 40 | results += "FAIL: jquery version ( " + 41 | jQuery.fn.jquery + " vs. " + version + "), "; 42 | } 43 | 44 | // Mark the instance with a flag. 45 | jQuery._MARKED = true; 46 | 47 | // Pass 48 | if (!results) { 49 | results = "PASS"; 50 | } 51 | 52 | output(name, err, results); 53 | }); 54 | }; 55 | 56 | // Run a whole bunch of loads. 57 | check("google", "1.11.3", "1"); 58 | check("jquery", "1.11.3", "1"); 59 | 60 | check("google", "1.11.2", "1"); 61 | check("jquery", "1.11.2", "1"); 62 | 63 | check("jquery", "1.7.2", "1"); 64 | 65 | check("google", "1.11.1", "1"); 66 | check("jquery", "1.11.1", "1"); 67 | 68 | check("google", "1.7.2", "1"); 69 | 70 | check("google", "1.11.3", "2"); 71 | check("jquery", "1.11.3", "2"); 72 | }()); 73 | -------------------------------------------------------------------------------- /test/func/fixtures/origins.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fixture 5 | 6 | 7 |
8 |
9 | 10 | 11 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/func/mocha-cov.opts: -------------------------------------------------------------------------------- 1 | --require test/func/setup-cov.js 2 | --reporter spec 3 | --ui bdd 4 | --timeout 60000 5 | -------------------------------------------------------------------------------- /test/func/mocha.opts: -------------------------------------------------------------------------------- 1 | --require test/func/setup.js 2 | --reporter spec 3 | --ui bdd 4 | --timeout 60000 5 | -------------------------------------------------------------------------------- /test/func/setup-cov.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Test setup for functional tests with code coverage. 4 | */ 5 | require("./setup.js"); 6 | 7 | // Indicate coverage. 8 | global.USE_COVERAGE = true; 9 | -------------------------------------------------------------------------------- /test/func/setup.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Test setup for functional tests. 4 | */ 5 | var chai = require("chai"); 6 | 7 | // Add test lib globals. 8 | global.expect = chai.expect; 9 | 10 | // Set test environment 11 | process.env.NODE_ENV = "func"; 12 | -------------------------------------------------------------------------------- /test/func/spec/advanced.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var base = require("./base.spec"); 4 | 5 | describe("advanced", function () { 6 | it("loads advanced scenario", function () { 7 | var url = base.appUrl + "test/func/fixtures/advanced.html"; 8 | 9 | return base.adapter.client 10 | .url(url) 11 | 12 | // Check errors 13 | .getText(".e2e-error").then(function (text) { 14 | expect(text).to.not.be.ok; 15 | }) 16 | 17 | // Verify load 18 | .getText(".e2e-script-first").then(function (text) { 19 | expect(text).to.equal("First Script"); 20 | }) 21 | .getText(".e2e-after-load-first").then(function (text) { 22 | expect(text).to.equal("After Load First"); 23 | }) 24 | .getText(".e2e-script-second").then(function (text) { 25 | expect(text).to.equal("Second Script"); 26 | }) 27 | .getText(".e2e-after-load-second").then(function (text) { 28 | expect(text).to.equal("After Load Second"); 29 | }) 30 | .getText(".e2e-script-third").then(function (text) { 31 | expect(text).to.equal("Third Script"); 32 | }) 33 | .getText(".e2e-after-load-third").then(function (text) { 34 | expect(text).to.equal("After Load Third"); 35 | }) 36 | .getText(".e2e-script-fourth").then(function (text) { 37 | expect(text).to.equal("Fourth Script"); 38 | }) 39 | .getText(".e2e-after-load-fourth").then(function (text) { 40 | expect(text).to.equal("After Load Fourth"); 41 | }) 42 | .getText(".e2e-after-setup-fourth").then(function (text) { 43 | expect(text).to.equal("After Setup Fourth"); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/func/spec/base.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Base setup, specs. 4 | * 5 | * This file _must_ be run in any spec file and thus must be manually 6 | * `require()`-ed in. 7 | */ 8 | // -------------------------------------------------------------------------- 9 | // Selenium (webdriverio/Rowdy) initialization 10 | // -------------------------------------------------------------------------- 11 | // We use webdriverio to get a client to Selenium, and Rowdy to help configure 12 | // our client, start a local selenium server if specified and provide a Mocha 13 | // adapter. 14 | // Enable Rowdy with webdriverio. 15 | var rowdy = require("rowdy"); 16 | var config = require("rowdy/config"); 17 | 18 | // Only start selenium on single-run local. 19 | var startSelenium = process.env.TEST_PARALLEL !== "true" && process.env.CI !== "true"; 20 | 21 | // Patch and re-configure. 22 | config.options.driverLib = "webdriverio"; 23 | config.options.server.start = startSelenium; 24 | rowdy(config); 25 | 26 | // Patch rowdy to force not started. 27 | // https://github.com/FormidableLabs/rowdy/issues/40 28 | if ((rowdy.setting.server || {}).start) { 29 | rowdy.setting.server.start = startSelenium; 30 | } 31 | 32 | // Mocha adapter. 33 | var Adapter = rowdy.adapters.mocha; 34 | var adapter = new Adapter(); 35 | 36 | adapter.before(); 37 | adapter.beforeEach(); 38 | adapter.afterEach(); 39 | adapter.after(); 40 | 41 | before(function () { 42 | var IMPLICIT_TIMEOUT = 200; 43 | 44 | // The `adapter.before();` call has the side effect of instantiating a 45 | // Selenium / webdriver client that we can extract here. 46 | // Set a global Selenium timeout that is _before_ our test timeout. 47 | return adapter.client 48 | .timeouts("implicit", IMPLICIT_TIMEOUT); 49 | }); 50 | 51 | // -------------------------------------------------------------------------- 52 | // Dev. Server 53 | // -------------------------------------------------------------------------- 54 | var APP_PORT_DEFAULT = 3030; 55 | var APP_PORT = process.env.TEST_FUNC_PORT || APP_PORT_DEFAULT; 56 | APP_PORT = parseInt(APP_PORT, 10); 57 | var APP_HOST = process.env.TEST_FUNC_HOST || "127.0.0.1"; 58 | var APP_URL = "http://" + APP_HOST + ":" + APP_PORT + "/"; 59 | 60 | // Go for "other" port of +2 if not specified 61 | var APP_PORT_OTHER = process.env.TEST_FUNC_PORT_OTHER || APP_PORT + 2; 62 | APP_PORT_OTHER = parseInt(APP_PORT_OTHER, 10); 63 | var APP_URL_OTHER = "http://" + APP_HOST + ":" + APP_PORT_OTHER + "/"; 64 | 65 | // Start up (and later stop) a single instance of the server so that we can 66 | // interact with the web application via our tests. 67 | // 68 | // An alternative to this approach is to hit a live running staging or 69 | // production server for "smoke" tests. 70 | // 71 | // For multi-file tests this setup should be extracted to a `base.spec.js` 72 | // file and executed **once** for the entire test suite. 73 | var httpServer = require("http-server"); 74 | var enableDestroy = require("server-destroy"); 75 | 76 | // To test multiple origins, we spawn two servers. 77 | var realServer1; 78 | var realServer2; 79 | 80 | // ---------------------------------------------------------------------------- 81 | // Code Coverage 82 | // ---------------------------------------------------------------------------- 83 | var path = require("path"); 84 | var fs = require("fs"); 85 | var uuid = require("node-uuid"); 86 | var istanbul = require("istanbul"); 87 | var collector = new istanbul.Collector(); 88 | 89 | var PROJECT_ROOT = path.resolve(__dirname, "../../.."); 90 | var middleware = []; 91 | 92 | // Instrument library for middleware insertion. 93 | var _covered = function (filePath) { 94 | var fileName = path.relative(PROJECT_ROOT, filePath); 95 | var code = fs.readFileSync(filePath); 96 | var instrumenter = new istanbul.Instrumenter(); 97 | return instrumenter.instrumentSync(code.toString(), fileName); 98 | }; 99 | 100 | if (global.USE_COVERAGE) { 101 | // Custom Instrumentation middleware. 102 | middleware.push(function (req, res) { 103 | var HTTP_OK = 200; 104 | 105 | if (/lib\/little-loader\.js/.test(req.url)) { 106 | var covered = _covered(path.resolve(PROJECT_ROOT, "lib/little-loader.js")); 107 | 108 | res.writeHead(HTTP_OK, { "Content-Type": "text/javascript" }); 109 | res.end(covered); 110 | return; 111 | } 112 | 113 | res.emit("next"); 114 | }); 115 | 116 | afterEach(function () { 117 | return adapter.client 118 | // Coverage. 119 | .execute(function () { 120 | // Client / browser code. 121 | /*globals window:false */ 122 | return JSON.stringify(window.__coverage__); 123 | }).then(function (ret) { 124 | // Gather data into collector. 125 | // Note: `JSON.parse` exception will get caught in `.finally()` 126 | var covObj = JSON.parse(ret.value); 127 | collector.add(covObj); 128 | }); 129 | }); 130 | 131 | after(function (done) { 132 | // Load configuration. 133 | // **Note**: We're tying to a known istanbul configuration file that in the 134 | // general should come from a shell flag. 135 | var cfg = istanbul.config.loadFile(".istanbul.func.yml"); 136 | 137 | // Patch reporter to output our GUID-driven incremental coverage files. 138 | cfg.reporting.reportConfig = function () { 139 | return { 140 | json: { 141 | file: "coverage-" + uuid.v4() + ".json" 142 | } 143 | }; 144 | }; 145 | 146 | // Create a `coverage/func/data` directory for outputs. 147 | var dir = path.join(cfg.reporting.config.dir, "data"); 148 | 149 | // Write out `data/coverage-GUID.json` object. 150 | var reporter = new istanbul.Reporter(cfg, dir); 151 | reporter.add("json"); 152 | reporter.write(collector, false, done); 153 | }); 154 | } 155 | 156 | // ---------------------------------------------------------------------------- 157 | // App server 158 | // ---------------------------------------------------------------------------- 159 | // Primary server 160 | before(function (done) { 161 | var server1 = httpServer.createServer({ 162 | before: middleware 163 | }); 164 | server1.listen(APP_PORT, APP_HOST, done); 165 | 166 | // `http-server` doesn't pass enough of the underlying server, so we capture it. 167 | realServer1 = server1.server; 168 | 169 | // Wrap the server with a "REALLY REALLY KILL IT!" `destroy` method. 170 | enableDestroy(realServer1); 171 | }); 172 | 173 | after(function (done) { 174 | if (!realServer1) { return done(); } 175 | 176 | // Take that server! 177 | realServer1.destroy(done); 178 | }); 179 | 180 | // Other server 181 | before(function (done) { 182 | var server2 = httpServer.createServer(); 183 | server2.listen(APP_PORT_OTHER, APP_HOST, done); 184 | realServer2 = server2.server; 185 | enableDestroy(realServer2); 186 | }); 187 | 188 | after(function (done) { 189 | if (!realServer2) { return done(); } 190 | realServer2.destroy(done); 191 | }); 192 | 193 | module.exports = { 194 | adapter: adapter, 195 | appUrl: APP_URL, 196 | appUrlOther: APP_URL_OTHER 197 | }; 198 | -------------------------------------------------------------------------------- /test/func/spec/basic.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var base = require("./base.spec"); 4 | 5 | describe("basic", function () { 6 | it("loads basic script", function () { 7 | var url = base.appUrl + "test/func/fixtures/basic.html"; 8 | 9 | return base.adapter.client 10 | .url(url) 11 | 12 | // Check errors 13 | .getText(".e2e-error").then(function (text) { 14 | expect(text).to.not.be.ok; 15 | }) 16 | 17 | // Verify load 18 | .getText(".e2e-basic-script").then(function (text) { 19 | expect(text).to.equal("Basic Script"); 20 | }) 21 | .getText(".e2e-after-load").then(function (text) { 22 | expect(text).to.equal("After Load"); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/func/spec/error.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var base = require("./base.spec"); 4 | 5 | describe("error", function () { 6 | it("capture script 404 error", function () { 7 | var url = base.appUrl + "test/func/fixtures/error.html"; 8 | 9 | return base.adapter.client 10 | .url(url) 11 | 12 | // Check errors 13 | .getText(".e2e-error").then(function (text) { 14 | expect(text).to.not.be.ok; 15 | }) 16 | 17 | // Verify error callback 18 | .getText(".e2e-after-load").then(function (text) { 19 | expect(text).to.equal("DOESNT_EXIST.js"); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/func/spec/jquery.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var base = require("./base.spec"); 4 | 5 | describe("error", function () { 6 | it("handles multiple jqueries", function () { 7 | var url = base.appUrl + "test/func/fixtures/jquery.html"; 8 | 9 | return base.adapter.client 10 | .url(url) 11 | 12 | // Check errors 13 | .getText(".e2e-error").then(function (text) { 14 | expect(text).to.not.be.ok; 15 | }) 16 | 17 | // Verify error and result callbacks 18 | .getText(".e2e-google-1-11-3-1-error").then(function (text) { 19 | expect(text).to.equal(""); 20 | }) 21 | .getText(".e2e-google-1-11-3-1-result").then(function (text) { 22 | expect(text).to.equal("PASS"); 23 | }) 24 | 25 | .getText(".e2e-jquery-1-11-3-1-error").then(function (text) { 26 | expect(text).to.equal(""); 27 | }) 28 | .getText(".e2e-jquery-1-11-3-1-result").then(function (text) { 29 | expect(text).to.equal("PASS"); 30 | }) 31 | 32 | .getText(".e2e-google-1-11-2-1-error").then(function (text) { 33 | expect(text).to.equal(""); 34 | }) 35 | .getText(".e2e-google-1-11-2-1-result").then(function (text) { 36 | expect(text).to.equal("PASS"); 37 | }) 38 | 39 | .getText(".e2e-jquery-1-11-2-1-error").then(function (text) { 40 | expect(text).to.equal(""); 41 | }) 42 | .getText(".e2e-jquery-1-11-2-1-result").then(function (text) { 43 | expect(text).to.equal("PASS"); 44 | }) 45 | 46 | .getText(".e2e-google-1-7-2-1-error").then(function (text) { 47 | expect(text).to.equal(""); 48 | }) 49 | .getText(".e2e-google-1-7-2-1-result").then(function (text) { 50 | expect(text).to.equal("PASS"); 51 | }) 52 | 53 | .getText(".e2e-google-1-11-1-1-error").then(function (text) { 54 | expect(text).to.equal(""); 55 | }) 56 | .getText(".e2e-google-1-11-1-1-result").then(function (text) { 57 | expect(text).to.equal("PASS"); 58 | }) 59 | 60 | .getText(".e2e-jquery-1-11-1-1-error").then(function (text) { 61 | expect(text).to.equal(""); 62 | }) 63 | .getText(".e2e-jquery-1-11-1-1-result").then(function (text) { 64 | expect(text).to.equal("PASS"); 65 | }) 66 | 67 | .getText(".e2e-jquery-1-7-2-1-error").then(function (text) { 68 | expect(text).to.equal(""); 69 | }) 70 | .getText(".e2e-jquery-1-7-2-1-result").then(function (text) { 71 | expect(text).to.equal("PASS"); 72 | }) 73 | 74 | .getText(".e2e-google-1-11-3-2-error").then(function (text) { 75 | expect(text).to.equal(""); 76 | }) 77 | .getText(".e2e-google-1-11-3-2-result").then(function (text) { 78 | expect(text).to.equal("PASS"); 79 | }) 80 | 81 | .getText(".e2e-jquery-1-11-3-2-error").then(function (text) { 82 | expect(text).to.equal(""); 83 | }) 84 | .getText(".e2e-jquery-1-11-3-2-result").then(function (text) { 85 | expect(text).to.equal("PASS"); 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/func/spec/origins.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Origins 4 | * 5 | * Reuses the `advanced` scripts to test 3/4 from altnerate origin (different port). 6 | */ 7 | var base = require("./base.spec"); 8 | 9 | describe("origins", function () { 10 | it("loads origins scenario", function () { 11 | var url = base.appUrl + "test/func/fixtures/origins.html"; 12 | 13 | return base.adapter.client 14 | .url(url) 15 | 16 | // Inject dynamic JS (easiest way to get `appUrlOther` information). 17 | .execute(function (appUrlOther) { 18 | // CLIENT CODE: This is stringified, sent to browser and executed. 19 | /*global window:false, document:false*/ 20 | var content = document.querySelector(".e2e-content"); 21 | 22 | // Load second (which loads fourth) and third from other orign. 23 | window._lload(appUrlOther + "test/func/fixtures/advanced/second.js", function () { 24 | content.innerHTML += "
After Load Second
"; 25 | }); 26 | window._lload(appUrlOther + "test/func/fixtures/advanced/third.js", function () { 27 | content.innerHTML += "
After Load Third
"; 28 | }); 29 | }, base.appUrlOther) 30 | 31 | // Check errors 32 | .getText(".e2e-error").then(function (text) { 33 | expect(text).to.not.be.ok; 34 | }) 35 | 36 | // Verify load from same origin. 37 | .getText(".e2e-script-first").then(function (text) { 38 | expect(text).to.equal("First Script"); 39 | }) 40 | .getText(".e2e-after-load-first").then(function (text) { 41 | expect(text).to.equal("After Load First"); 42 | }) 43 | 44 | // Verify load from different origin. 45 | .getText(".e2e-script-second").then(function (text) { 46 | expect(text).to.equal("Second Script"); 47 | }) 48 | .getText(".e2e-after-load-second").then(function (text) { 49 | expect(text).to.equal("After Load Second"); 50 | }) 51 | .getText(".e2e-script-third").then(function (text) { 52 | expect(text).to.equal("Third Script"); 53 | }) 54 | .getText(".e2e-after-load-third").then(function (text) { 55 | expect(text).to.equal("After Load Third"); 56 | }) 57 | .getText(".e2e-script-fourth").then(function (text) { 58 | expect(text).to.equal("Fourth Script"); 59 | }) 60 | .getText(".e2e-after-load-fourth").then(function (text) { 61 | expect(text).to.equal("After Load Fourth"); 62 | }) 63 | .getText(".e2e-after-setup-fourth").then(function (text) { 64 | expect(text).to.equal("After Setup Fourth"); 65 | }); 66 | }); 67 | }); 68 | --------------------------------------------------------------------------------