├── .eslintrc ├── .gitignore ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── bin └── sl-build.js ├── index.js ├── lib └── git.js ├── package.json ├── sl-build.txt └── test ├── build-example.js ├── fixtures ├── suite │ ├── .gitignore │ ├── .jshintignore │ ├── .jshintrc │ ├── .loopbackrc │ ├── LICENSE │ ├── README.cloud9.md │ ├── README.md │ ├── app.js │ ├── bin │ │ └── create-load.js │ ├── build │ │ └── readme.js │ ├── config.js │ ├── data-sources │ │ ├── db.js │ │ └── rest-geocode.js │ ├── models │ │ ├── car.js │ │ ├── car.json │ │ ├── customer.js │ │ ├── customer.json │ │ ├── inventory.js │ │ ├── inventory.json │ │ ├── location.js │ │ ├── location.json │ │ ├── note.js │ │ └── note.json │ ├── package.json │ ├── public │ │ ├── css │ │ │ ├── bootstrap.min.css │ │ │ ├── code.css │ │ │ └── main.css │ │ ├── fonts │ │ │ ├── 0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff │ │ │ ├── OsJ2DjdpjqFRVUSto6IffLO3LdcAZYWl9Si6vvxL-qU.woff │ │ │ └── _aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff │ │ ├── index.html │ │ └── js │ │ │ ├── bootstrap.min.js │ │ │ ├── jquery-scrollTo.js │ │ │ ├── jquery.min.js │ │ │ └── main.js │ ├── test-data │ │ ├── cars.json │ │ ├── customers.json │ │ ├── discover.js │ │ ├── generate-inventory.js │ │ ├── import.js │ │ ├── inventory.json │ │ ├── locations.json │ │ ├── oracle-tables.sql │ │ └── updatedb.js │ └── test │ │ ├── rest.js │ │ └── support.js └── zero-dependency │ ├── index.js │ └── package.json ├── test-basic.sh ├── test-build-bundle-scripts.js ├── test-build-install-nodeps.js ├── test-build-install.js ├── test-build-pack.js ├── test-build-scripts.js ├── test-script-only.js └── test-usage.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "strongloop", 3 | "env": { 4 | "node": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | sls-sample-app-2.0.3.tgz 4 | loopback-example-app*.tgz 5 | _suite 6 | _onto 7 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | 2016-05-06, Version 2.1.2 2 | ========================= 3 | 4 | * update copyright notices and license (Ryan Graham) 5 | 6 | 7 | 2016-04-11, Version 2.1.1 8 | ========================= 9 | 10 | * fix failing test on npm3 (Ryan Graham) 11 | 12 | 13 | 2015-12-21, Version 2.1.0 14 | ========================= 15 | 16 | * remove support for --bundle flag (Ryan Graham) 17 | 18 | * Refer to licenses with a link (Sam Roberts) 19 | 20 | 21 | 2015-10-01, Version 2.0.6 22 | ========================= 23 | 24 | * usage: document -s alias for --scripts (Ryan Graham) 25 | 26 | * lint: add spaces after keywords (Ryan Graham) 27 | 28 | * Use strongloop conventions for licensing (Sam Roberts) 29 | 30 | 31 | 2015-09-16, Version 2.0.5 32 | ========================= 33 | 34 | * switch to iconv for binary tests (Ryan Graham) 35 | 36 | 37 | 2015-08-06, Version 2.0.4 38 | ========================= 39 | 40 | * usage: clarify relationship between pack/bundle (Sam Roberts) 41 | 42 | * test: shelljs exec silent is required under io.js (Sam Roberts) 43 | 44 | * lintpocalypse (Ryan Graham) 45 | 46 | * package: add eslint and rules (Ryan Graham) 47 | 48 | * fix undefined test() error (Ryan Graham) 49 | 50 | * test: regression test for zero dependency apps (Ryan Graham) 51 | 52 | * build: detect/handle apps with no dependencies (Ryan Graham) 53 | 54 | 55 | 2015-07-07, Version 2.0.3 56 | ========================= 57 | 58 | * Construct packfile name from package (Sam Roberts) 59 | 60 | 61 | 2015-05-21, Version 2.0.2 62 | ========================= 63 | 64 | * Ignore test output (Sam Roberts) 65 | 66 | 67 | 2015-05-13, Version 2.0.1 68 | ========================= 69 | 70 | * test: flush disk after building test app (Ryan Graham) 71 | 72 | * test: improve debugability of test-script-only (Ryan Graham) 73 | 74 | * fix callback exception catching (Ryan Graham) 75 | 76 | * deps: upgrade tar (Ryan Graham) 77 | 78 | * deps: upgrade shelljs (Ryan Graham) 79 | 80 | * deps: upgrade lodash (Ryan Graham) 81 | 82 | * deps: upgrade json-file-plus (Ryan Graham) 83 | 84 | * deps: upgrade tap to 0.7 (Ryan Graham) 85 | 86 | * deps: alphabetize deps (Ryan Graham) 87 | 88 | 89 | 2015-04-30, Version 2.0.0 90 | ========================= 91 | 92 | * Fix #23, rename of bin/slb to bin/sl-build (Sam Roberts) 93 | 94 | * Add --npm and --git options (Sam Roberts) 95 | 96 | * Update README for strong-pm.io (Sam Roberts) 97 | 98 | * Factor usage message into sl-build.txt. (Sam Roberts) 99 | 100 | * Rename sl-build for consistency (Sam Roberts) 101 | 102 | 103 | 2015-01-12, Version 1.0.3 104 | ========================= 105 | 106 | * test: remove loopback-connector-oracle from tests (Ryan Graham) 107 | 108 | * Fix bad CLA URL in CONTRIBUTING.md (Ryan Graham) 109 | 110 | 111 | 2014-12-09, Version 1.0.2 112 | ========================= 113 | 114 | * test: use strong-fork-syslog for tests (Ryan Graham) 115 | 116 | * internal: add info for sync branch debugging (Ryan Graham) 117 | 118 | * Skip 'npm run build' if no build script (Ryan Graham) 119 | 120 | * package: use debug v2.x in all strongloop deps (Sam Roberts) 121 | 122 | 123 | 2014-11-03, Version 1.0.1 124 | ========================= 125 | 126 | * Ensure default steps run when using --script only (Krishna Raman) 127 | 128 | * git: don't rely on git-symbolic-ref --short (Ryan Graham) 129 | 130 | * Ignore .(git|npm)ignore when building packs (Krishna Raman) 131 | 132 | 133 | 2014-10-02, Version 1.0.0 134 | ========================= 135 | 136 | * Update contribution guidelines (Ryan Graham) 137 | 138 | 139 | 2014-09-02, Version 0.2.1 140 | ========================= 141 | 142 | * package: update json-file-plus to avoid deprecated (Sam Roberts) 143 | 144 | * package: update README (Sam Roberts) 145 | 146 | * package: add keywords for npm search (Sam Roberts) 147 | 148 | * package: rename changelog and fix date format (Sam Roberts) 149 | 150 | 151 | 2014-08-26, Version 0.2.0 152 | ========================= 153 | 154 | * test: replace slc dep with static fixture (Ryan Graham) 155 | 156 | * test: validate test setup (Ryan Graham) 157 | 158 | * test: ensure build can be run on HEAD (Ryan Graham) 159 | 160 | * Refactor common git ref lookup into utility (Ryan Graham) 161 | 162 | * Don't commit build results if unchanged (Ryan Graham) 163 | 164 | * Only merge HEAD into deploy if necessary (Ryan Graham) 165 | 166 | * Make --onto a modifier for --commit (Ryan Graham) 167 | 168 | * git: Override GIT_INDEX_FILE for all git commands (Ryan Graham) 169 | 170 | * Ensure create --onto branch if it doesn't exist (Ryan Graham) 171 | 172 | * Use git detection to inform defaults (Ryan Graham) 173 | 174 | * test: sls-sample-app loopback-example-app rename (Ryan Graham) 175 | 176 | * test: replace onto tests with shell script (Ryan Graham) 177 | 178 | * test: support mix of JS and shell based tests (Ryan Graham) 179 | 180 | * onto: fix typo in usage message (Sam Roberts) 181 | 182 | * Update package license to match LICENSE.md (Sam Roberts) 183 | 184 | 185 | 2014-07-22, Version 0.1.2 186 | ========================= 187 | 188 | * Allow building onto an identical branch (Sam Roberts) 189 | 190 | 191 | 2014-07-21, Version 0.1.1 192 | ========================= 193 | 194 | * docs: clarify typical git workflow (Sam Roberts) 195 | 196 | 197 | 2014-06-19, Version 0.1.0 198 | ========================= 199 | 200 | 201 | 202 | 2014-06-19, Version 0.0.1 203 | ========================= 204 | 205 | * docs: update README with usage (Sam Roberts) 206 | 207 | * usage: fix missing command name in usage message (Sam Roberts) 208 | 209 | * log: show git commands during --onto and --commit (Sam Roberts) 210 | 211 | * log: show commands during --install,bundle,pack (Sam Roberts) 212 | 213 | * install: prune dev dependencies after build script (Sam Roberts) 214 | 215 | * pack: remove npm debug messages from output (Sam Roberts) 216 | 217 | * bundle: accept packages with no dependencies (Sam Roberts) 218 | 219 | * log: log when beginning a long-running command (Sam Roberts) 220 | 221 | * doc: update README and other docs pre-publish (Sam Roberts) 222 | 223 | * readme: update with latest usage (Sam Roberts) 224 | 225 | * log: ensure each action logs on complete (Sam Roberts) 226 | 227 | * bundle,pack: log the actions taken (Sam Roberts) 228 | 229 | * git: remove unused $0 argument to git.onto() (Sam Roberts) 230 | 231 | * commit: commit build products into git (Sam Roberts) 232 | 233 | * bundle: bundle only dependencies and optional deps (Sam Roberts) 234 | 235 | * bundle: warn on problematic or incomplete config (Sam Roberts) 236 | 237 | * onto: commit source tree onto a deploy branch (Sam Roberts) 238 | 239 | * doc: add README.md and update --help output (Sam Roberts) 240 | 241 | * pack: place archive in parent directory (Sam Roberts) 242 | 243 | * test: use strong-cli as a dev dependency (Sam Roberts) 244 | 245 | * Implement --bundle, so dependencies are packed (Sam Roberts) 246 | 247 | * Implement --pack, to create an archive (Sam Roberts) 248 | 249 | * Implement --scripts, to build binary addons (Sam Roberts) 250 | 251 | * Refactor build test setup so it can be reused (Sam Roberts) 252 | 253 | * Use option parser, and test option parsing (Sam Roberts) 254 | 255 | * Implement running of custom package build script (Sam Roberts) 256 | 257 | * Implement usage and version options (Sam Roberts) 258 | 259 | * Implement npm install without build scripts (Sam Roberts) 260 | 261 | * Add tap test script (Sam Roberts) 262 | 263 | * Add package.json and slb stub (Sam Roberts) 264 | 265 | * License and contribution agreement (Sam Roberts) 266 | 267 | 268 | 2014-05-20, Version INITIAL 269 | =========================== 270 | 271 | * First release! 272 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contributing ### 2 | 3 | Thank you for your interest in `strong-build`, an open source project 4 | administered by StrongLoop. 5 | 6 | Contributing to `strong-build` is easy. In a few simple steps: 7 | 8 | * Ensure that your effort is aligned with the project's roadmap by 9 | talking to the maintainers, especially if you are going to spend a 10 | lot of time on it. 11 | 12 | * Make something better or fix a bug. 13 | 14 | * Adhere to code style outlined in the [Google C++ Style Guide][] and 15 | [Google Javascript Style Guide][]. 16 | 17 | * Sign the [Contributor License Agreement](https://cla.strongloop.com/agreements/strongloop/strong-build) 18 | 19 | * Submit a pull request through Github. 20 | 21 | 22 | ### Contributor License Agreement ### 23 | 24 | ``` 25 | Individual Contributor License Agreement 26 | 27 | By signing this Individual Contributor License Agreement 28 | ("Agreement"), and making a Contribution (as defined below) to 29 | StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and 30 | agree to the following terms and conditions for Your present and 31 | future Contributions submitted to StrongLoop. Except for the license 32 | granted in this Agreement to StrongLoop and recipients of software 33 | distributed by StrongLoop, You reserve all right, title, and interest 34 | in and to Your Contributions. 35 | 36 | 1. Definitions 37 | 38 | "You" or "Your" shall mean the copyright owner or the individual 39 | authorized by the copyright owner that is entering into this 40 | Agreement with StrongLoop. 41 | 42 | "Contribution" shall mean any original work of authorship, 43 | including any modifications or additions to an existing work, that 44 | is intentionally submitted by You to StrongLoop for inclusion in, 45 | or documentation of, any of the products owned or managed by 46 | StrongLoop ("Work"). For purposes of this definition, "submitted" 47 | means any form of electronic, verbal, or written communication 48 | sent to StrongLoop or its representatives, including but not 49 | limited to communication or electronic mailing lists, source code 50 | control systems, and issue tracking systems that are managed by, 51 | or on behalf of, StrongLoop for the purpose of discussing and 52 | improving the Work, but excluding communication that is 53 | conspicuously marked or otherwise designated in writing by You as 54 | "Not a Contribution." 55 | 56 | 2. You Grant a Copyright License to StrongLoop 57 | 58 | Subject to the terms and conditions of this Agreement, You hereby 59 | grant to StrongLoop and recipients of software distributed by 60 | StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, 61 | royalty-free, irrevocable copyright license to reproduce, prepare 62 | derivative works of, publicly display, publicly perform, 63 | sublicense, and distribute Your Contributions and such derivative 64 | works under any license and without any restrictions. 65 | 66 | 3. You Grant a Patent License to StrongLoop 67 | 68 | Subject to the terms and conditions of this Agreement, You hereby 69 | grant to StrongLoop and to recipients of software distributed by 70 | StrongLoop a perpetual, worldwide, non-exclusive, no-charge, 71 | royalty-free, irrevocable (except as stated in this Section) 72 | patent license to make, have made, use, offer to sell, sell, 73 | import, and otherwise transfer the Work under any license and 74 | without any restrictions. The patent license You grant to 75 | StrongLoop under this Section applies only to those patent claims 76 | licensable by You that are necessarily infringed by Your 77 | Contributions(s) alone or by combination of Your Contributions(s) 78 | with the Work to which such Contribution(s) was submitted. If any 79 | entity institutes a patent litigation against You or any other 80 | entity (including a cross-claim or counterclaim in a lawsuit) 81 | alleging that Your Contribution, or the Work to which You have 82 | contributed, constitutes direct or contributory patent 83 | infringement, any patent licenses granted to that entity under 84 | this Agreement for that Contribution or Work shall terminate as 85 | of the date such litigation is filed. 86 | 87 | 4. You Have the Right to Grant Licenses to StrongLoop 88 | 89 | You represent that You are legally entitled to grant the licenses 90 | in this Agreement. 91 | 92 | If Your employer(s) has rights to intellectual property that You 93 | create, You represent that You have received permission to make 94 | the Contributions on behalf of that employer, that Your employer 95 | has waived such rights for Your Contributions, or that Your 96 | employer has executed a separate Corporate Contributor License 97 | Agreement with StrongLoop. 98 | 99 | 5. The Contributions Are Your Original Work 100 | 101 | You represent that each of Your Contributions are Your original 102 | works of authorship (see Section 8 (Submissions on Behalf of 103 | Others) for submission on behalf of others). You represent that to 104 | Your knowledge, no other person claims, or has the right to claim, 105 | any right in any intellectual property right related to Your 106 | Contributions. 107 | 108 | You also represent that You are not legally obligated, whether by 109 | entering into an agreement or otherwise, in any way that conflicts 110 | with the terms of this Agreement. 111 | 112 | You represent that Your Contribution submissions include complete 113 | details of any third-party license or other restriction (including, 114 | but not limited to, related patents and trademarks) of which You 115 | are personally aware and which are associated with any part of 116 | Your Contributions. 117 | 118 | 6. You Don't Have an Obligation to Provide Support for Your Contributions 119 | 120 | You are not expected to provide support for Your Contributions, 121 | except to the extent You desire to provide support. You may provide 122 | support for free, for a fee, or not at all. 123 | 124 | 6. No Warranties or Conditions 125 | 126 | StrongLoop acknowledges that unless required by applicable law or 127 | agreed to in writing, You provide Your Contributions on an "AS IS" 128 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 129 | EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES 130 | OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR 131 | FITNESS FOR A PARTICULAR PURPOSE. 132 | 133 | 7. Submission on Behalf of Others 134 | 135 | If You wish to submit work that is not Your original creation, You 136 | may submit it to StrongLoop separately from any Contribution, 137 | identifying the complete details of its source and of any license 138 | or other restriction (including, but not limited to, related 139 | patents, trademarks, and license agreements) of which You are 140 | personally aware, and conspicuously marking the work as 141 | "Submitted on Behalf of a Third-Party: [named here]". 142 | 143 | 8. Agree to Notify of Change of Circumstances 144 | 145 | You agree to notify StrongLoop of any facts or circumstances of 146 | which You become aware that would make these representations 147 | inaccurate in any respect. Email us at callback@strongloop.com. 148 | ``` 149 | 150 | [Google C++ Style Guide]: https://google.github.io/styleguide/cppguide.html 151 | [Google Javascript Style Guide]: https://google.github.io/styleguide/javascriptguide.xml 152 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) IBM Corp. 2014,2016. All Rights Reserved. 2 | Node module: strong-build 3 | This project is licensed under the Artistic License 2.0, full text below. 4 | 5 | -------- 6 | 7 | The Artistic License 2.0 8 | 9 | Copyright (c) 2000-2006, The Perl Foundation. 10 | 11 | Everyone is permitted to copy and distribute verbatim copies 12 | of this license document, but changing it is not allowed. 13 | 14 | Preamble 15 | 16 | This license establishes the terms under which a given free software 17 | Package may be copied, modified, distributed, and/or redistributed. 18 | The intent is that the Copyright Holder maintains some artistic 19 | control over the development of that Package while still keeping the 20 | Package available as open source and free software. 21 | 22 | You are always permitted to make arrangements wholly outside of this 23 | license directly with the Copyright Holder of a given Package. If the 24 | terms of this license do not permit the full use that you propose to 25 | make of the Package, you should contact the Copyright Holder and seek 26 | a different licensing arrangement. 27 | 28 | Definitions 29 | 30 | "Copyright Holder" means the individual(s) or organization(s) 31 | named in the copyright notice for the entire Package. 32 | 33 | "Contributor" means any party that has contributed code or other 34 | material to the Package, in accordance with the Copyright Holder's 35 | procedures. 36 | 37 | "You" and "your" means any person who would like to copy, 38 | distribute, or modify the Package. 39 | 40 | "Package" means the collection of files distributed by the 41 | Copyright Holder, and derivatives of that collection and/or of 42 | those files. A given Package may consist of either the Standard 43 | Version, or a Modified Version. 44 | 45 | "Distribute" means providing a copy of the Package or making it 46 | accessible to anyone else, or in the case of a company or 47 | organization, to others outside of your company or organization. 48 | 49 | "Distributor Fee" means any fee that you charge for Distributing 50 | this Package or providing support for this Package to another 51 | party. It does not mean licensing fees. 52 | 53 | "Standard Version" refers to the Package if it has not been 54 | modified, or has been modified only in ways explicitly requested 55 | by the Copyright Holder. 56 | 57 | "Modified Version" means the Package, if it has been changed, and 58 | such changes were not explicitly requested by the Copyright 59 | Holder. 60 | 61 | "Original License" means this Artistic License as Distributed with 62 | the Standard Version of the Package, in its current version or as 63 | it may be modified by The Perl Foundation in the future. 64 | 65 | "Source" form means the source code, documentation source, and 66 | configuration files for the Package. 67 | 68 | "Compiled" form means the compiled bytecode, object code, binary, 69 | or any other form resulting from mechanical transformation or 70 | translation of the Source form. 71 | 72 | 73 | Permission for Use and Modification Without Distribution 74 | 75 | (1) You are permitted to use the Standard Version and create and use 76 | Modified Versions for any purpose without restriction, provided that 77 | you do not Distribute the Modified Version. 78 | 79 | 80 | Permissions for Redistribution of the Standard Version 81 | 82 | (2) You may Distribute verbatim copies of the Source form of the 83 | Standard Version of this Package in any medium without restriction, 84 | either gratis or for a Distributor Fee, provided that you duplicate 85 | all of the original copyright notices and associated disclaimers. At 86 | your discretion, such verbatim copies may or may not include a 87 | Compiled form of the Package. 88 | 89 | (3) You may apply any bug fixes, portability changes, and other 90 | modifications made available from the Copyright Holder. The resulting 91 | Package will still be considered the Standard Version, and as such 92 | will be subject to the Original License. 93 | 94 | 95 | Distribution of Modified Versions of the Package as Source 96 | 97 | (4) You may Distribute your Modified Version as Source (either gratis 98 | or for a Distributor Fee, and with or without a Compiled form of the 99 | Modified Version) provided that you clearly document how it differs 100 | from the Standard Version, including, but not limited to, documenting 101 | any non-standard features, executables, or modules, and provided that 102 | you do at least ONE of the following: 103 | 104 | (a) make the Modified Version available to the Copyright Holder 105 | of the Standard Version, under the Original License, so that the 106 | Copyright Holder may include your modifications in the Standard 107 | Version. 108 | 109 | (b) ensure that installation of your Modified Version does not 110 | prevent the user installing or running the Standard Version. In 111 | addition, the Modified Version must bear a name that is different 112 | from the name of the Standard Version. 113 | 114 | (c) allow anyone who receives a copy of the Modified Version to 115 | make the Source form of the Modified Version available to others 116 | under 117 | 118 | (i) the Original License or 119 | 120 | (ii) a license that permits the licensee to freely copy, 121 | modify and redistribute the Modified Version using the same 122 | licensing terms that apply to the copy that the licensee 123 | received, and requires that the Source form of the Modified 124 | Version, and of any works derived from it, be made freely 125 | available in that license fees are prohibited but Distributor 126 | Fees are allowed. 127 | 128 | 129 | Distribution of Compiled Forms of the Standard Version 130 | or Modified Versions without the Source 131 | 132 | (5) You may Distribute Compiled forms of the Standard Version without 133 | the Source, provided that you include complete instructions on how to 134 | get the Source of the Standard Version. Such instructions must be 135 | valid at the time of your distribution. If these instructions, at any 136 | time while you are carrying out such distribution, become invalid, you 137 | must provide new instructions on demand or cease further distribution. 138 | If you provide valid instructions or cease distribution within thirty 139 | days after you become aware that the instructions are invalid, then 140 | you do not forfeit any of your rights under this license. 141 | 142 | (6) You may Distribute a Modified Version in Compiled form without 143 | the Source, provided that you comply with Section 4 with respect to 144 | the Source of the Modified Version. 145 | 146 | 147 | Aggregating or Linking the Package 148 | 149 | (7) You may aggregate the Package (either the Standard Version or 150 | Modified Version) with other packages and Distribute the resulting 151 | aggregation provided that you do not charge a licensing fee for the 152 | Package. Distributor Fees are permitted, and licensing fees for other 153 | components in the aggregation are permitted. The terms of this license 154 | apply to the use and Distribution of the Standard or Modified Versions 155 | as included in the aggregation. 156 | 157 | (8) You are permitted to link Modified and Standard Versions with 158 | other works, to embed the Package in a larger work of your own, or to 159 | build stand-alone binary or bytecode versions of applications that 160 | include the Package, and Distribute the result without restriction, 161 | provided the result does not expose a direct interface to the Package. 162 | 163 | 164 | Items That are Not Considered Part of a Modified Version 165 | 166 | (9) Works (including, but not limited to, modules and scripts) that 167 | merely extend or make use of the Package, do not, by themselves, cause 168 | the Package to be a Modified Version. In addition, such works are not 169 | considered parts of the Package itself, and are not subject to the 170 | terms of this license. 171 | 172 | 173 | General Provisions 174 | 175 | (10) Any use, modification, and distribution of the Standard or 176 | Modified Versions is governed by this Artistic License. By using, 177 | modifying or distributing the Package, you accept this license. Do not 178 | use, modify, or distribute the Package, if you do not accept this 179 | license. 180 | 181 | (11) If your Modified Version has been derived from a Modified 182 | Version made by someone other than you, you are nevertheless required 183 | to ensure that your Modified Version complies with the requirements of 184 | this license. 185 | 186 | (12) This license does not grant you the right to use any trademark, 187 | service mark, tradename, or logo of the Copyright Holder. 188 | 189 | (13) This license includes the non-exclusive, worldwide, 190 | free-of-charge patent license to make, have made, use, offer to sell, 191 | sell, import and otherwise transfer the Package with respect to any 192 | patent claims licensable by the Copyright Holder that are necessarily 193 | infringed by the Package. If you institute patent litigation 194 | (including a cross-claim or counterclaim) against any party alleging 195 | that the Package constitutes direct or contributory patent 196 | infringement, then this Artistic License to you shall terminate on the 197 | date that such litigation is filed. 198 | 199 | (14) Disclaimer of Warranty: 200 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 201 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 202 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 203 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 204 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 205 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 206 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 207 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 208 | 209 | 210 | -------- 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # strong-build 2 | 3 | Build a node application package, preparing it for deploy to production. 4 | 5 | It is useful standalone, but is commonly used to build applications for 6 | deployment to the StrongLoop process manager, strong-pm. 7 | 8 | For more details, see http://strong-pm.io. 9 | 10 | 11 | ## Installation 12 | 13 | `sl-build` is made available through the 14 | [strongloop](https://github.com/strongloop/strongloop) tool as `slc build`, and 15 | works well with the StrongLoop Process Manager, 16 | [strong-pm](https://github.com/strongloop/strong-pm). 17 | 18 | `sl-build` can be installed standalone with: 19 | 20 | npm install -g strong-build 21 | 22 | 23 | ## Overview 24 | 25 | The purpose of building a node application is to bundle its dependencies so 26 | that there: 27 | 28 | - are no deploy-time dependencies on external services 29 | - it is in a deployable format 30 | 31 | The build process is implemented as four commands: 32 | 33 | - `sl-build --install`: the core of the build, it installs dependencies, runs custom 34 | build steps, and prunes development dependencies 35 | - `sl-build --bundle`: modify the npm `package.json` and `.npmignore` configuration files 36 | so dependencies will be packed 37 | - `sl-build --pack`: create an npm package of the build 38 | - `sl-build --commit`: commit the build onto a git branch 39 | 40 | The default behaviour of `sl-build` depends on whether the current directory is 41 | a git repository. 42 | 43 | - In a git repository: the default is `sl-build --install --commit`, to build 44 | onto a git branch. 45 | - Otherwise: the default is `sl-build --bundle --install --pack`, to build an 46 | npm package. 47 | 48 | Both npm packages and git branches can be deployed to the StrongLoop [process 49 | manager](http://github.com/strongloop/strong-pm) using the StrongLoop 50 | [deploy](http://github.com/strongloop/strong-deploy) tool. 51 | 52 | Specifying any command disables the default, and allows any mix of commands 53 | to be run, either singly, or all at once. 54 | 55 | Also, note that *builds should be done in a clean working copy*. You don't 56 | build deployment artifacts out of a possibly dirty working copy if you want 57 | reproducible builds. You can clean your working copy using `git clean -x -d 58 | -f`. This is too destructive for the build tool to do, but doing a build in an 59 | unclean working repository may trigger an error in a future version of the 60 | tool. 61 | 62 | 63 | ## install command 64 | 65 | Installation automates the common work flow for building application 66 | dependencies: 67 | 68 | - `npm install --ignore-scripts`: Install dependencies without running scripts. 69 | Scripts can be run optionally with `--scripts`. 70 | - `npm run build`: Custom build steps such as `grunt build` or `bower` can be 71 | specified in the package's `scripts.build` property, since front-end code 72 | served by node commonly requires some amount of preparation. 73 | - `npm prune --production`: Remove development-only tools (such as bower, or 74 | grunt) that may have been required by the package's build scripts, but should 75 | not be deployed. 76 | 77 | Note that compilation and install scripts should be run on the deployment 78 | server using: 79 | 80 | - `npm rebuild`: Compile add-ons for current system. 81 | - `npm install`: Run any install scripts (not typical, but if they exist they 82 | may be required to prepare the application for running). 83 | 84 | If builds are done on the same system architecture as the deploy, it is 85 | possible to compile and package the add-ons, and avoid the presence of a 86 | compiler on the deployment system. This is recommended when possible, but is 87 | not the default assumption of strong-build. 88 | 89 | 90 | ## bundle command 91 | 92 | Bundling configures the package.json and .npmignore so deployment (not 93 | development) dependencies as well as any 'build' script output will not be 94 | ignored by `npm pack`. 95 | 96 | This is unnecessary when using git to deploy, but mandatory when creating npm 97 | packages! 98 | 99 | ### package.bundleDependencies 100 | 101 | Bundling requires that the `bundleDependencies` property in the `package.json` 102 | file is configured to include all non-development dependencies, including 103 | optional dependencies, which are often overlooked. 104 | 105 | Its important that you remember to add every new production dependency to the 106 | `bundleDependencies` property, if you don't, npm will try and install them 107 | after deploy, creating unexpected and fragile dependencies on npmjs.org. 108 | 109 | Since keeping this up-to-date manually is likely to go wrong, we recommend 110 | allowing the bundle command to do this for you. However, the 111 | `bundleDependencies` property is not modified if present, so you are free to 112 | maintain it yourself, if you wish (or to not just the bundle command). 113 | 114 | ### .npmignore 115 | 116 | Setting bundle dependencies is insufficient to get the output of build tools. 117 | 118 | Both build output and project ephemera such as test output is usually ignored 119 | using `.gitignore`, as it should be. However, if npm does not find a 120 | `.npmignore` configuration file, it uses `.gitignore` as a fallback. This means 121 | that if you have custom build output, such as minimized JavaScript, it will be 122 | treated as project ephemera, and not be packed by npm. 123 | 124 | The bundle command will create an *empty* `.npmignore` file if there is a 125 | `.gitignore` file but there is no `.npmignore` file. This will work for clean 126 | repositories, but if you have any project ephemera, they will get packed. 127 | 128 | We recommend you write and maintain you own `.npmignore` file unless your build 129 | process guarantees a clean working repository. 130 | 131 | 132 | ## pack command 133 | 134 | Pack output is a tar file in the format produced by `npm pack` and 135 | accepted by `npm install` and 136 | [strong-deploy](https://github.com/strongloop/strong-deploy). 137 | 138 | The pack file is placed in the parent directory of the application being packed, 139 | to avoid the pack file itself getting packed by future builds, and to allow the 140 | working repository to be cleaned. 141 | 142 | If a `.npmignore` file was created by the bundle command, check the pack file 143 | contents carefully to ensure build products are packed, but project ephemera are 144 | not. 145 | 146 | 147 | ## commit command 148 | 149 | Committing build products into git provides the most robust tracking and 150 | storage, including versioning of deployments. 151 | 152 | This is often done by committing both build products and dependencies 153 | (`node_modules`) into git where they pollute the source branches, create massive 154 | git commits and huge churn on the development branches and repositories. 155 | 156 | The commit command does not do this. 157 | 158 | It commits an exact replica of current branch source and build products onto a 159 | deployment branch. After the commit, the deployment branch tip shows as a merge 160 | of the deployment and source branches. This allows a complete history of 161 | deployment builds to be kept in git, but separated from the development 162 | branches. Deployment branches can be pushed to the same repository as the 163 | development branches, or not. 164 | 165 | Note that branches prepared like this can also be pushed to platforms such 166 | as OpenShift and Heroku. 167 | 168 | The default name of the deployment branch is "deploy", but is configurable with 169 | the `--onto BRANCH` modifier to the commit command. 170 | 171 | 172 | ## Usage 173 | 174 | ``` 175 | usage: sl-build [options] 176 | 177 | Build a node application package. 178 | 179 | With no options, the default depends on whether a git repository is 180 | detected or not. 181 | 182 | If a git repository is detected the default is `--git`: install and commit the 183 | build results to the "deploy" branch, which will be created if it does not 184 | already exist. 185 | 186 | If no git repository is detected the default is `--npm`: bundle, install, and 187 | pack the build results into a `-.tgz` file. 188 | 189 | Options: 190 | -h,--help Print this message and exit. 191 | -v,--version Print version and exit. 192 | -n,--npm Same as `--install --bundle --pack`. 193 | -g,--git Same as `--install --commit`. 194 | -i,--install Install dependencies (without scripts, by default). 195 | --scripts If installing, run scripts (to build addons). 196 | -b,--bundle Modify package to bundle deployment dependencies. 197 | -p,--pack Pack into a publishable archive (with dependencies). 198 | 199 | Git specific options: 200 | -c,--commit Commit build output (branch specified by --onto). 201 | --onto BRANCH Branch to commit build results to, created if 202 | necessary ("deploy", by default). 203 | ``` 204 | 205 | ## License 206 | 207 | strong-build uses a dual license model. 208 | 209 | You may use this library under the terms of the [Artistic 2.0 license][], 210 | or under the terms of the [StrongLoop Subscription Agreement][]. 211 | 212 | [Artistic 2.0 license]: http://opensource.org/licenses/Artistic-2.0 213 | [StrongLoop Subscription Agreement]: http://strongloop.com/license 214 | -------------------------------------------------------------------------------- /bin/sl-build.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 3 | // Node module: strong-build 4 | // This file is licensed under the Artistic License 2.0. 5 | // License text available at https://opensource.org/licenses/Artistic-2.0 6 | 7 | 'use strict'; 8 | 9 | require('../').build(process.argv, function(er) { 10 | if (!er) { 11 | process.exit(0); 12 | } 13 | process.exit(1); 14 | }); 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var Parser = require('posix-getopt').BasicParser; 9 | var debug = require('debug')('strong-build'); 10 | var fmt = require('util').format; 11 | var fs = require('fs'); 12 | var git = require('./lib/git'); 13 | var path = require('path'); 14 | var pump = require('pump'); 15 | var shell = require('shelljs'); 16 | var strongPack = require('strong-pack'); 17 | var vasync = require('vasync'); 18 | var zlib = require('zlib'); 19 | 20 | function printHelp($0, prn) { 21 | var USAGE = fs.readFileSync(require.resolve('./sl-build.txt'), 'utf-8') 22 | .replace(/%MAIN%/g, $0) 23 | .trim(); 24 | 25 | prn(USAGE); 26 | } 27 | 28 | function runCommand(cmd, callback) { 29 | debug('run command: %s', cmd); 30 | shell.exec(cmd, {silent: true}, function(code, output) { 31 | debug('code %d: <<<\n%s>>>', code, output); 32 | if (code !== 0) { 33 | var er = Error(cmd); 34 | } 35 | return callback(er, output, code); 36 | }); 37 | } 38 | 39 | function runWait(cmd, callback) { 40 | console.log('Running `%s`', cmd); 41 | runCommand(cmd, function(er, output) { 42 | if (er) { 43 | console.error('Error on `%s`:', cmd); 44 | reportRunError(er, output); 45 | return callback(er); 46 | } 47 | return callback(null, output); 48 | }); 49 | } 50 | 51 | function runStep(cmd) { 52 | return function(_, callback) { 53 | runWait(cmd, function(er) { 54 | return callback(er); // do not return output of runWait() 55 | }); 56 | }; 57 | } 58 | 59 | function reportRunError(er, output) { 60 | if (!er) return; 61 | 62 | console.error('Failed to run `%s`:', er.message); 63 | if (output && output !== '') { 64 | process.stderr.write(output); 65 | } 66 | } 67 | 68 | exports.build = function build(argv, callback) { 69 | var $0 = process.env.SLC_COMMAND ? 70 | 'slc ' + process.env.SLC_COMMAND : 71 | path.basename(argv[1]); 72 | var parser = new Parser([ 73 | ':v(version)', 74 | 'h(help)', 75 | 'n(npm)', 76 | 'g(git)', 77 | 's(scripts)', 78 | 'i(install)', 79 | 'b(bundle)', 80 | 'p(pack)', 81 | 'O:(onto)', 82 | 'c(commit)', 83 | 'N(no-commit)', 84 | ].join(''), argv); 85 | var option; 86 | var onto = 'deploy'; 87 | var install; 88 | var scripts; 89 | var pack; 90 | var commit; 91 | 92 | while ((option = parser.getopt()) !== undefined) { 93 | switch (option.option) { 94 | case 'v': 95 | console.log(require('./package.json').version); 96 | return callback(); 97 | case 'h': 98 | printHelp($0, console.log); 99 | return callback(); 100 | case 'n': 101 | install = true; 102 | commit = false; 103 | pack = true; 104 | break; 105 | case 'g': 106 | install = true; 107 | commit = true; 108 | pack = false; 109 | break; 110 | case 's': 111 | scripts = true; 112 | break; 113 | case 'i': 114 | install = true; 115 | break; 116 | case 'b': 117 | console.error('Warning: the --bundle option now does nothing and ' + 118 | 'should not be used'); 119 | break; 120 | case 'p': 121 | pack = true; 122 | break; 123 | case 'O': 124 | onto = option.optarg; 125 | break; 126 | case 'c': 127 | commit = true; 128 | break; 129 | case 'N': 130 | commit = false; 131 | break; 132 | default: 133 | console.error('Invalid usage (near option \'%s\'), try `%s --help`.', 134 | option.optopt, $0); 135 | return callback(Error('usage')); 136 | } 137 | } 138 | 139 | if (parser.optind() !== argv.length) { 140 | console.error('Invalid usage (extra arguments), try `%s --help`.', $0); 141 | return callback(Error('usage')); 142 | } 143 | 144 | // With no actions selected, do everything we can (onto requires an argument, 145 | // so we can't do it automatically). 146 | if (!(install || pack || commit)) { 147 | install = true; 148 | if (git.isGit()) { 149 | commit = true; 150 | pack = false; 151 | } else { 152 | commit = false; 153 | pack = true; 154 | } 155 | } 156 | 157 | if (commit && !git.isGit()) { 158 | console.error('Cannot perform commit on non-git working directory'); 159 | return callback(Error('usage')); 160 | } 161 | 162 | var steps = []; 163 | 164 | if (commit) { 165 | steps.push(doEnsureGitBranch); 166 | steps.push(doGitSyncBranch); 167 | } 168 | 169 | if (install) { 170 | steps.push(doNpmInstall); 171 | } 172 | 173 | if (pack) { 174 | steps.push(doNpmPack); 175 | } 176 | 177 | if (commit) { 178 | steps.push(doGitCommit); 179 | } 180 | 181 | vasync.pipeline({funcs: steps}, callback); 182 | 183 | function doEnsureGitBranch(_, callback) { 184 | try { 185 | git.ensureBranch(onto); 186 | } catch (er) { 187 | console.error('%s', er.message); 188 | return callback(er); 189 | } 190 | return callback(); 191 | } 192 | 193 | function doGitSyncBranch(_, callback) { 194 | try { 195 | var info = git.syncBranch(onto); 196 | if (info.srcBranch && info.dstBranch) { 197 | console.log('Merged source tree of `%s` onto `%s`', 198 | info.srcBranch, info.dstBranch); 199 | } else { 200 | console.log('Not merging HEAD into `%s`, already up to date.', onto); 201 | } 202 | } catch (er) { 203 | console.error('%s', er.message); 204 | return callback(er); 205 | } 206 | return callback(); 207 | } 208 | 209 | function doNpmInstall(_, callback) { 210 | var pkg = require(path.resolve('package.json')); 211 | var install = 'npm install'; 212 | if (!scripts) { 213 | install += ' --ignore-scripts'; 214 | } 215 | var steps = [runStep(install)]; 216 | if (pkg.scripts && pkg.scripts.build) { 217 | steps.push(runStep('npm run build')); 218 | } 219 | steps.push(runStep('npm prune --production')); 220 | vasync.pipeline({funcs: steps}, function(er) { 221 | return callback(er); 222 | }); 223 | } 224 | 225 | function doNpmPack(_, callback) { 226 | var pkg = JSON.parse(fs.readFileSync('package.json')); 227 | var tgzName = fmt('%s-%s.tgz', pkg.name, pkg.version); 228 | var dst = path.join('..', tgzName); 229 | var tarStream = strongPack(process.cwd()); 230 | var dstFile = fs.createWriteStream(dst, 'binary'); 231 | var gz = zlib.createGzip(); 232 | console.log('Packing application in to %s', dst); 233 | pump(tarStream, gz, dstFile, callback); 234 | } 235 | 236 | function doGitCommit(_, callback) { 237 | try { 238 | var info = git.commitAll(onto); 239 | if (info.branch) { 240 | console.log('Committed build products onto `%s`', info.branch); 241 | } else { 242 | console.log('Build products already up to date on `%s`', onto); 243 | } 244 | } catch (er) { 245 | console.error('%s', er.message); 246 | return callback(er); 247 | } 248 | return callback(); 249 | } 250 | }; 251 | -------------------------------------------------------------------------------- /lib/git.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var debug = require('debug')('strong-build'); 9 | var lodash = require('lodash'); 10 | var path = require('path'); 11 | var shell = require('shelljs'); 12 | var util = require('util'); 13 | 14 | function fmt() { 15 | return util.format.apply(util, arguments); 16 | } 17 | 18 | function execSync(cmd, error, noOutput) { 19 | debug('exec `%s`: ...', cmd); 20 | 21 | if (gitDir(true)) 22 | process.env.GIT_INDEX_FILE = path.join(gitDir(), 'strong-build-index'); 23 | 24 | var exec = shell.exec(cmd, {silent: true}); 25 | 26 | exec.output = exec.output.trim(); 27 | 28 | debug('exec `%s`: (code %s) %s', cmd, exec.code, exec.output); 29 | 30 | if (exec.code !== 0) { 31 | var output = exec.output.replace('fatal: ', ''); 32 | console.log('Error on `%s`:\n %s', cmd, output); 33 | throw Error(error); 34 | } 35 | 36 | console.log('Running `%s`', cmd); 37 | 38 | // git on success may still write messages to stderr, and shelljs combines 39 | // stdout and stderr (sucky). This effect commit-tree, for example, when dest 40 | // branch and HEAD are the same, the first output line is 'error: duplicate 41 | // parent ... ignored\n', but the command succeeds, and the SHA of the tree is 42 | // the next line. So, remove any error lines from output if the command 43 | // exited with success. 44 | if (!noOutput && exec.output.length > 0) { 45 | exec.output = exec.output.replace(/error: .*\n/, '', 'g'); 46 | 47 | console.log(' => %s', exec.output); 48 | } 49 | 50 | return exec.output; 51 | } 52 | 53 | function resolveBranch(name) { 54 | // Resolve symbolic name "HEAD" if given 55 | if (name === 'HEAD') { 56 | name = shell.exec('git symbolic-ref HEAD', {silent: true}) 57 | .output 58 | .trim() 59 | .replace(/^refs\/heads\//, ''); 60 | } 61 | 62 | // $ git show-ref --heads master 63 | // 5855ad5bf1d38fc94059a1ef917c61866ca4d3d4 refs/heads/master 64 | var showRef = fmt('git show-ref --heads "%s"', name); 65 | var info = shell.exec(showRef, {silent: true}) 66 | .output 67 | .trim() 68 | .split(' '); 69 | 70 | return { 71 | name: name, 72 | sha: info[0], 73 | ref: info[1], 74 | }; 75 | } 76 | 77 | exports.isGit = function gitIsGit() { 78 | return !!gitDir(); 79 | }; 80 | 81 | var gitDir = exports.gitDir = lodash.memoize(function gitDir() { 82 | var exec = shell.exec('git rev-parse --git-dir', {silent: true}); 83 | return exec.code === 0 && exec.output.trim(); 84 | }); 85 | 86 | exports.ensureBranch = function ensureBranch(name) { 87 | var info = resolveBranch(name); 88 | if (!info.ref) 89 | execSync( 90 | fmt('git branch "%s"', name), 91 | fmt('Failed to create branch "%s"', name) 92 | ); 93 | }; 94 | 95 | exports.syncBranch = function gitSyncBranch(gitDstBranch) { 96 | // Get name of current branch, and the tree SHA of its HEAD commit 97 | var src = resolveBranch('HEAD'); 98 | var dst = resolveBranch(gitDstBranch); 99 | 100 | debug('syncing dst(%o) with src(%o)', src, dst); 101 | var mergeBaseCmd = fmt('git merge-base "%s" "%s"', src.sha, dst.sha); 102 | var mergeBase = shell.exec(mergeBaseCmd, {silent: true}) 103 | .output 104 | .trim(); 105 | 106 | if (mergeBase === src.sha) { 107 | return {}; 108 | } 109 | 110 | var gitSrcTreeSha = execSync( 111 | 'git log -1 --pretty=format:"%t" HEAD', 112 | 'Failed to parse branch `HEAD`, is this a git repository?' 113 | ); 114 | 115 | // Commit the source tree to the head of the destination branch, and update 116 | // the destination branch ref. 117 | var message = fmt('Commit tree of \'%s\' onto \'%s\'', src.ref, gitDstBranch); 118 | var commitSha = execSync( 119 | fmt('git commit-tree -p "%s" -p "%s" -m "%s" %s', 120 | dst.ref, src.ref, message, gitSrcTreeSha), 121 | fmt('Failed to commit `%s` onto `%s`', src.ref, gitDstBranch) 122 | ); 123 | 124 | execSync( 125 | fmt('git update-ref "%s" %s', dst.ref, commitSha), 126 | fmt('Failed to merge `%s` onto `%s`', src.ref, gitDstBranch) 127 | ); 128 | 129 | return { 130 | dstBranch: gitDstBranch, 131 | srcBranch: src.ref, 132 | srcTreeSha: gitSrcTreeSha, 133 | commitSha: commitSha, 134 | }; 135 | }; 136 | 137 | exports.commitAll = function gitCommitAll(gitDstBranch) { 138 | execSync( 139 | fmt('git add --force --all .'), 140 | fmt('Failed to add build products') 141 | ); 142 | var treeSha = execSync( 143 | fmt('git write-tree'), 144 | fmt('Failed to stage build products for commit') 145 | ); 146 | var dst = resolveBranch(gitDstBranch); 147 | var commitSha = execSync( 148 | fmt('git commit-tree -p "%s" -m "Commit build products" %s', 149 | dst.ref, treeSha), 150 | fmt('Failed to create build products commit for `%s`', gitDstBranch), 151 | true 152 | ); 153 | 154 | var diff = shell.exec(fmt('git diff --quiet "%s" "%s"', commitSha, dst.ref), 155 | {silent: true}); 156 | if (diff.code === 0) 157 | return {}; 158 | 159 | execSync( 160 | fmt('git update-ref "%s" %s', dst.ref, commitSha), 161 | fmt('Failed to commit build products to `%s`', gitDstBranch) 162 | ); 163 | 164 | return { 165 | branch: gitDstBranch, 166 | dstRef: dst.ref, 167 | tree: treeSha, 168 | commit: commitSha, 169 | }; 170 | }; 171 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strong-build", 3 | "description": "Prepare a node package for deployment", 4 | "version": "2.1.2", 5 | "keywords": [ 6 | "StrongLoop", 7 | "build", 8 | "bundle", 9 | "dependencies", 10 | "git", 11 | "heroku", 12 | "npm", 13 | "openshift", 14 | "slc" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/strongloop/strong-build.git" 19 | }, 20 | "author": "engineering@strongloop.com", 21 | "license": "Artistic-2.0", 22 | "readmeFilename": "README.md", 23 | "main": "index.js", 24 | "scripts": { 25 | "pretest": "eslint *.js bin/*.js lib/*.js test/*.js", 26 | "test": "tap --timeout=300 ./test/test-*" 27 | }, 28 | "bin": { 29 | "sl-build": "./bin/sl-build.js" 30 | }, 31 | "dependencies": { 32 | "debug": "^2.0.0", 33 | "lodash": "^4.15.0", 34 | "posix-getopt": "^1.0.0", 35 | "pump": "^1.0.1", 36 | "shelljs": "^0.4.0", 37 | "strong-pack": "^1.0.0", 38 | "vasync": "^1.4.3" 39 | }, 40 | "devDependencies": { 41 | "async": "^2.0.1", 42 | "eslint": "^2.13.1", 43 | "eslint-config-strongloop": "^2.1.0", 44 | "tap": "^0.7.1", 45 | "tar": "^2.1.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sl-build.txt: -------------------------------------------------------------------------------- 1 | usage: %MAIN% [options] 2 | 3 | Build a node application package. 4 | 5 | With no options, the default depends on whether a git repository is 6 | detected or not. 7 | 8 | If a git repository is detected the default is `--git`: install and commit the 9 | build results to the "deploy" branch, which will be created if it does not 10 | already exist. 11 | 12 | If no git repository is detected the default is `--npm`: install and 13 | pack the build results into a `-.tgz` file. 14 | 15 | Options: 16 | -h,--help Print this message and exit. 17 | -v,--version Print version and exit. 18 | -n,--npm Same as `--install --pack`. 19 | -g,--git Same as `--install --commit`. 20 | -i,--install Install dependencies (without scripts, by default). 21 | -s,--scripts If installing, run scripts (to build addons). 22 | -p,--pack Pack into a publishable archive. 23 | 24 | Git specific options: 25 | -c,--commit Commit build output (branch specified by --onto). 26 | --onto BRANCH Branch to commit build results to, created if 27 | necessary ("deploy", by default). 28 | -------------------------------------------------------------------------------- /test/build-example.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var debug = require('debug')('strong-build:test'); 10 | var fmt = require('util').format; 11 | var fs = require('fs'); 12 | var sh = require('shelljs'); 13 | var tar = require('tar'); 14 | var zlib = require('zlib'); 15 | 16 | module.exports = function buildExample(fixture, args, callback) { 17 | tar.list = function list(tarfile, callback) { 18 | var paths = []; 19 | 20 | fs.createReadStream(tarfile) 21 | .on('error', callback) 22 | .pipe(zlib.Unzip()) 23 | .pipe(tar.Parse()) 24 | .on('entry', function(entry) { 25 | paths.push(entry.path); 26 | }) 27 | .on('end', function() { 28 | debug('tarfile %s list:', tarfile, paths); 29 | return callback(null, paths); 30 | }); 31 | }; 32 | 33 | fs.readJsonSync = function readJsonSync(file) { 34 | return JSON.parse(fs.readFileSync(file)); 35 | }; 36 | 37 | var build = require('../'); 38 | 39 | // Check for node silently exiting with code 0 when tests have not passed. 40 | var ok = false; 41 | 42 | process.on('exit', function(code) { 43 | if (code === 0) { 44 | assert(ok); 45 | } 46 | }); 47 | 48 | sh.rm('-rf', '_suite'); 49 | sh.cp('-Rf', fmt('fixtures/%s/*', fixture), '_suite'); 50 | sh.cd('_suite'); 51 | assert(sh.test('-f', 'package.json')); 52 | assert(!sh.test('-d', 'node_modules')); 53 | 54 | var argv = ['node', 'slb'].concat(args); 55 | 56 | debug('build with argv:', argv); 57 | 58 | build.build(argv, function(er) { 59 | ok = true; 60 | if (process.env.CI && process.platform !== 'win32') { 61 | debug('committing writes before continuing with test...'); 62 | sh.exec('sync'); 63 | return setTimeout(function() { 64 | callback(er); 65 | }, 1000); 66 | } 67 | return callback(er); 68 | }); 69 | 70 | debug('waiting for build...'); 71 | }; 72 | -------------------------------------------------------------------------------- /test/fixtures/suite/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .project 3 | *.sublime-* 4 | .DS_Store 5 | *.seed 6 | *.log 7 | *.csv 8 | *.dat 9 | *.out 10 | *.pid 11 | *.swp 12 | *.swo 13 | node_modules 14 | coverage 15 | *.tgz 16 | *.xml 17 | -------------------------------------------------------------------------------- /test/fixtures/suite/.jshintignore: -------------------------------------------------------------------------------- 1 | public/explorer/lib/underscore-min.js 2 | node_modules 3 | -------------------------------------------------------------------------------- /test/fixtures/suite/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase": true 3 | , "quotmark": "single" 4 | , "eqnull": true 5 | , "node": true 6 | , "laxcomma": true 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/suite/.loopbackrc: -------------------------------------------------------------------------------- 1 | { 2 | "demo": { 3 | "memory": {}, 4 | "oracle": { 5 | "host": "demo.strongloop.com", 6 | "port": 1521, 7 | "database": "XE", 8 | "username": "demo", 9 | "password": "L00pBack" 10 | }, 11 | "mongodb": { 12 | "host": "demo.strongloop.com", 13 | "database": "demo", 14 | "username": "demo", 15 | "password": "L00pBack", 16 | "port": 27017 17 | }, 18 | "mysql": { 19 | "host": "demo.strongloop.com", 20 | "port": 3306, 21 | "database": "demo", 22 | "username": "demo", 23 | "password": "L00pBack" 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/fixtures/suite/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 StrongLoop, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /test/fixtures/suite/README.cloud9.md: -------------------------------------------------------------------------------- 1 | # Welcome to the StrongLoop Cloud Experience! 2 | 3 | This Cloud 9 Workspace contains several StrongLoop products including slc (CLI), 4 | LoopBack (API Server) and the StrongOps agent (DevOps and Performance Monitoring) 5 | for you to try in the cloud (Cloud 9, to be exact) before [downloading and 6 | installing](http://www.strongloop.com/get-started) them on your own. 7 | 8 | ## Before You Begin 9 | 10 | Before we really get into the StrongLoop Suite, take a minute to familiarize 11 | yourself with the Cloud 9 IDE. 12 | 13 | * The left edge of the screen is a full view of the projects, with all the 14 | wonderful files and folders therein. Feel free to have a look around, but we 15 | recommend finishing this README before exploring _too_ much. 16 | * On the top of the screen is the main menu, the toolbar, and the tabs. Boring, 17 | but necessary. The most necessary element is the "Run" button, which we'll come 18 | to shortly. 19 | * At the bottom is a two-tab interface: Terminal and Output. The Terminal 20 | allows you to (as you might expect) run commands and interact with your current 21 | application/project. The Output tab contains the logs for the running instance 22 | of your application. 23 | * When you run the application it'll be necessary to open multiple 24 | terminal windows to run multiple processes at the same time, to open 25 | a new terminal window go to the "View" menu and choose 26 | "Terminals"->New Terminal 27 | * The panel you're currently reading from is the main code editor; as we walk 28 | through Node examples, this panel will host all of the code being discussed. 29 | * The panel on the right is a full, working browser window. No browser? Let's 30 | fix that: 31 | 32 | ## Running and Viewing the SLS Sample Application 33 | 34 | Remember that friendly, green "Run" button in the main menu? Go ahead and give 35 | 'er a click. The LoopBack Sample App should begin running, and the bottom Output 36 | tab should become visible. If not, select Output in the bottom pane and take 37 | note of a few things: 38 | 39 | ``` 40 | Your code is running at 'http://strongloop.soandso.cloud9beta.com'. 41 | Important: use 'process.env.PORT' as the port and 'process.env.IP' as the host in your scripts! 42 | Using the memory connector. 43 | To specify another connector: 44 | DB=oracle node app 45 | ``` 46 | 47 | The first is that the application has run successfully. If that's no the case 48 | (for example, if the word Exception and a big, scary stack trace have been 49 | produced, then something's wrong!), then please [reach out](http://wwww.strongloop.com/strongloop-suite/strongsupport) and we'll 50 | gladly give you a hand. 51 | 52 | Second, click the blue link in that first message. If the right browser tab was 53 | hidden, it'll reveal itself ... along with the home page of the SLS Sample. 54 | 55 | Third, it's MUCH better if you view the home page of the sample 56 | application in a browser on your local machine. To do so, click the 57 | detach icon on the browser pane, next to the close icon. This will 58 | open a new browser tab or window on your local browser with the home 59 | page. 60 | 61 | Lastly, note the URL of your sample app in the browser pane. It's 62 | the same URL that should be displayed in the Output tab after "Your 63 | code is running at...". 64 | 65 | ## Where do I go from here? 66 | 67 | Now that you have the Sample App running, embiggen (and, yes, that's a word) the 68 | browser tab on the right or on your local browser and take a peek. The Next Steps are inside! 69 | 70 | -------------------------------------------------------------------------------- /test/fixtures/suite/README.md: -------------------------------------------------------------------------------- 1 | ## LoopBack Sample Application 2 | 3 | ### i-Car Rentals Corp 4 | 5 | i-Car is an (imaginary) car rental dealer with locations in major cities around 6 | the world. They need to replace their existing desktop reservation system with 7 | a new mobile app. 8 | 9 | ### End user experience 10 | 11 | The app enables customers to find the closest available cars using the i-Car app 12 | on a smartphone. The app shows a map of nearby rental locations and lists 13 | available cars in the area shown on the map. In addition, the customer can 14 | filter the list of cars by make, model, class, year and color. The customer can 15 | then select the desired car and reserve it via the app. If not logged in the app 16 | prompts the customer to login or register. The app indicates if the desired car 17 | is available and if so, confirms the reservation. 18 | 19 | ### Features 20 | 21 | - Authenticates and verifies customers' identities. 22 | - Securely exposes inventory data to mobile applications. 23 | - Allow customers to find cars available **within a specific area**. 24 | - Allow customers to reserve cars for rental. 25 | 26 | ### REST APIs 27 | 28 | - `/cars` exposes a queryable (filter, sort) collection of available cars 29 | over HTTP / JSON 30 | - `/cars/nearby?&lat=...&long=... or ?zip=...` returns a filtered set of 31 | available cars nearby the requesting user 32 | - `/cars/nearby?id=24&zip=94555` returns nearby cars of id 24. 33 | - `/cars/:id` returns a specific car from the inventory, with specific 34 | pricing and images 35 | - `/users/login` allows a customer to login 36 | - `/users/logout` allows a customer to logout 37 | 38 | ### Infrastructure 39 | 40 | #### Customer Database 41 | 42 | All customer information is available from the SalesForce API. 43 | 44 | #### Inventory Database 45 | 46 | All car inventory is already available in an **existing** Oracle X3-8 Exadata 47 | database. 48 | 49 | The Inventory DB schema looks like this: 50 | 51 | ##### **Customers** 52 | - id string 53 | - name string 54 | - username string 55 | - email string 56 | - password string 57 | - realm string 58 | - emailverified boolean 59 | - verificationtoken string 60 | - credentials string[] 61 | - challenges string[] 62 | - status string 63 | - created date 64 | - lastupdated date 65 | 66 | ##### **Reservations** 67 | - id string 68 | - product_id string 69 | - location_id string 70 | - customer_id string 71 | - qty number 72 | - status string 73 | - reserve_date date 74 | - pickup_date date 75 | - return_date date 76 | 77 | ##### **Inventory_Levels** 78 | - id string 79 | - product_id string 80 | - location_id string 81 | - available number 82 | - total number 83 | 84 | ##### **Car** 85 | - id string 86 | - vin string 87 | - year number 88 | - make string 89 | - model string 90 | - image string 91 | - carClass string 92 | - color string 93 | 94 | ##### **Location** 95 | - id string 96 | - street string 97 | - city string 98 | - zipcode string 99 | - name string 100 | - geo GeoPoint 101 | 102 | ##### **Inventory_View** 103 | 104 | **View** to return qty of available products for the given city. 105 | 106 | - product (product name) 107 | - location (location name) 108 | - available (qty available) 109 | 110 | #### Geo Lookup 111 | 112 | Google's location API is used to return the users city from a given zip or lat/long. 113 | 114 | ### Configure and run the application 115 | 116 | By default, the sample application uses the memory connector and listen on 117 | http://0.0.0.0:3000. 118 | 119 | > node app 120 | 121 | Open browser and point it to http://127.0.0.1:3000. 122 | 123 | You can configure other data sources by adding the following json into `.loopbackrc` 124 | at the root of the module. 125 | 126 | { 127 | "demo": { 128 | "memory": {}, 129 | "oracle": { 130 | "host": "your-oracle-server-ip-or-hostname", 131 | "port": 1521, 132 | "database": "XE", 133 | "username": "demo", 134 | "password": "password" 135 | }, 136 | "mongodb": { 137 | "host": "your-mongodb-server-ip-or-hostname", 138 | "database": "demo", 139 | "username": "demo", 140 | "password": "password", 141 | "port": 27017 142 | } 143 | } 144 | } 145 | 146 | The sample can be configured using the following environment variables: 147 | 148 | - DB: The db type, use 'memory', 'mongodb' or 'oracle' 149 | - IP: The http server listener ip address or hostname, default to 0.0.0.0 (any address) 150 | - PORT: The http server listener port number, default to 3000 151 | 152 | For example, 153 | 154 | To run the application at port 3001 with MongoDB: 155 | 156 | > DB=mongodb PORT=3001 node app 157 | 158 | To run the application at port 3002 with Oracle: 159 | 160 | > DB=oracle PORT=3002 node app 161 | 162 | -------------------------------------------------------------------------------- /test/fixtures/suite/app.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * App Dependencies. 8 | */ 9 | 10 | var loopback = require('loopback') 11 | , app = module.exports = loopback() 12 | , fs = require('fs') 13 | , path = require('path') 14 | , cors = require('cors') 15 | , request = require('request') 16 | , TaskEmitter = require('strong-task-emitter'); 17 | 18 | // Require models, make sure it happens before api explorer 19 | fs 20 | .readdirSync(path.join(__dirname, './models')) 21 | .filter(function(m) { 22 | return path.extname(m) === '.js'; 23 | }) 24 | .forEach(function(m) { 25 | // expose model over rest 26 | app.model(require('./models/' + m)); 27 | }); 28 | 29 | // Setup LoopBack access-control 30 | var db = require('./data-sources/db'); 31 | loopback.AccessToken.attachTo(db); 32 | loopback.Role.attachTo(db); 33 | loopback.ACL.attachTo(db); 34 | loopback.RoleMapping.attachTo(db); 35 | app.dataSources.db = db; 36 | 37 | app.enableAuth(); 38 | 39 | // Set up the HTTP listener ip & port 40 | var ip = process.env.IP || '0.0.0.0'; 41 | var port = process.env.PORT || 3000; 42 | var baseURL = 'http://' + (ip === '0.0.0.0' ? 'localhost' : ip) + ':' + port; 43 | app.set('ip', ip); 44 | app.set('port', port); 45 | 46 | app.use(loopback.favicon()); 47 | // app.use(loopback.logger(app.get('env') === 'development' ? 'dev' : 'default')); 48 | app.use(loopback.bodyParser()); 49 | app.use(loopback.methodOverride()); 50 | 51 | // Establish our overly-permissive CORS rules. 52 | app.use(cors()); 53 | 54 | app.use(loopback.token()); 55 | 56 | var apiPath = '/api'; 57 | 58 | // Expose a rest api 59 | app.use(apiPath, loopback.rest()); 60 | 61 | // API explorer (if present) 62 | var explorerPath = '/explorer'; 63 | try { 64 | var explorer = require('loopback-explorer'); 65 | app.use(explorerPath, explorer(app, { basePath: apiPath })); 66 | } catch(e){ 67 | // ignore errors, explorer stays disabled 68 | } 69 | 70 | 71 | // Let express routes handle requests that were not handled 72 | // by any of the middleware registered above. 73 | // This way LoopBack REST and API Explorer take precedence over 74 | // express routes. 75 | app.use(app.router); 76 | 77 | // The static file server should come after all other routes 78 | // Every request that goes through the static middleware hits 79 | // the file system to check if a file exists. 80 | app.use(loopback.static(path.join(__dirname, 'public'))); 81 | 82 | // Requests that get this far won't be handled 83 | // by any middleware. Convert them into a 404 error 84 | // that will be handled later down the chain. 85 | app.use(loopback.urlNotFound()); 86 | 87 | // The ultimate error handler. 88 | app.use(loopback.errorHandler()); 89 | 90 | app.start = function() { 91 | // Start the server 92 | return app.listen(port, ip, function() { 93 | if(process.env.C9_PROJECT) { 94 | // Customize the url for the Cloud9 environment 95 | baseURL = 'https://' + process.env.C9_PROJECT + '-c9-' + process.env.C9_USER + '.c9.io'; 96 | } 97 | console.error('LoopBack sample is now ready at ' + baseURL); 98 | }); 99 | }; 100 | 101 | // Optionally start the server 102 | // (only if this module is the main module) 103 | if(require.main === module) { 104 | app.start(); 105 | } 106 | 107 | -------------------------------------------------------------------------------- /test/fixtures/suite/bin/create-load.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 3 | // Node module: strong-build 4 | // This file is licensed under the Artistic License 2.0. 5 | // License text available at https://opensource.org/licenses/Artistic-2.0 6 | 7 | /** 8 | * This script creates weighted random load on the sample server. 9 | */ 10 | 11 | var request = require('request'); 12 | var shuffle = require('shuffle').shuffle; 13 | var table = require('text-table'); 14 | var weighted = require('weighted'); 15 | 16 | // If false, non-GET requests are enabled. Not recommended for shared (e.g. C9) 17 | // servers. 18 | var safeMode = true; 19 | 20 | /** 21 | * Returns `body` parsed as JSON if it's not already been parsed, `body 22 | * otherwise. 23 | */ 24 | function toJSON(body) { 25 | if (typeof body !== 'string') { 26 | return body; 27 | } 28 | 29 | return JSON.parse(body); 30 | } 31 | 32 | /** 33 | * Returns a random Array with half the elements from `arr`. 34 | */ 35 | function randomHalf(arr) { 36 | var size = Math.ceil(arr.length / 2); 37 | 38 | return shuffle({ deck: arr }).draw(size); 39 | } 40 | 41 | /** 42 | * Returns a tabular string of `choices`' contents, with all weights converted 43 | * to relative percentages. 44 | */ 45 | function toWeightTable(choices) { 46 | return table([ 47 | ['Route', 'Weight'], 48 | ['-----', '-----'] 49 | ].concat(Object.keys(choices).map(function (key) { 50 | return [key, Math.round(choices[key] * 10000) / 100 + '%']; 51 | }))); 52 | } 53 | 54 | function getBaseURL() { 55 | var ip = process.env.IP || process.env.HOST || '127.0.0.1' 56 | var port = process.env.PORT || 3000; 57 | var baseURL = 'http://' + ip + ':' + port + '/api'; 58 | return baseURL; 59 | } 60 | /** 61 | * This kicks off the application 62 | * @type {[type]} 63 | */ 64 | function start() { 65 | request.get(getBaseURL() + '/routes', function (err, response, body) { 66 | if(err) 67 | throw err; 68 | body = toJSON(body); 69 | 70 | var routes = distillRoutes(body); 71 | routes = randomHalf(routes); 72 | 73 | var choices = weighChoices(routes); 74 | 75 | // We print out the choice table so the user can compare the weighted load 76 | // to analytics and monitoring data. 77 | console.log('Hitting routes with the following weights:'); 78 | console.log(toWeightTable(choices)); 79 | 80 | // Go! 81 | hit(choices); 82 | }); 83 | } 84 | 85 | /** 86 | * Returns an Array of choices distilled from the complete route table, 87 | * `routes`. 88 | */ 89 | function distillRoutes(routes) { 90 | return routes.filter(function (route) { 91 | if (safeMode && route.verb.toUpperCase() !== 'GET') { 92 | return false; 93 | } 94 | 95 | return true; 96 | }).map(function (route) { 97 | // TODO(schoon) - Handle the `accepts` in a meaningful way. 98 | return route.verb.toUpperCase() + ' ' + route.path; 99 | }); 100 | } 101 | 102 | /** 103 | * Returns a weighted choice table from an Array of choices. 104 | */ 105 | function weighChoices(routes) { 106 | var total = 0; 107 | var choices = routes.reduce(function (obj, route) { 108 | obj[route] = Math.random(); 109 | total += obj[route]; 110 | 111 | return obj; 112 | }, {}); 113 | 114 | // For simplicity, we normalize the weights to add up to 1. 115 | Object.keys(choices).forEach(function (key) { 116 | choices[key] /= total; 117 | }); 118 | 119 | return choices; 120 | } 121 | 122 | /** 123 | * Hits a randomly-selected route from the available `choices`. 124 | */ 125 | function hit(choices) { 126 | var route = weighted(choices); 127 | var parts = route.split(' '); 128 | var verb = parts[0].toLowerCase(); 129 | var path = parts[1]; 130 | 131 | // We replace any :id path fragments with 1, which we hope exists. 132 | path = path.replace(/\:id/g, 1); 133 | 134 | if (verb === 'all') { 135 | verb = 'post'; 136 | } 137 | 138 | // Make the request. 139 | request[verb](getBaseURL() + path, { 140 | json: {} 141 | }, function (err, response, body) { 142 | if (err) { 143 | console.error('Request error with %s: %s', route, err); 144 | return; 145 | } 146 | 147 | // Ignore the result. 148 | }); 149 | 150 | // TODO(schoon) - Make the request rate configurable. 151 | setTimeout(function () { 152 | hit(choices); 153 | }, 100); 154 | } 155 | 156 | // Now that all the pieces have been defined, it's time to `start` the engine! 157 | start(); 158 | -------------------------------------------------------------------------------- /test/fixtures/suite/build/readme.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * The utility sets up the default README.md based on the PARTNER environment variable 8 | * 9 | * For example `PARTNER=cloud9 npm install` will replace README.md with README.cloud9.md 10 | */ 11 | var fs = require('fs'); 12 | var path = require('path'); 13 | 14 | if (process.env.PARTNER) { 15 | var readme = path.join(__dirname, '../README.md'); // README.md 16 | var slReadme = path.join(__dirname, '../README.strongloop.md'); // README.strongloop.md 17 | var partnerReadme = path.join(__dirname, '../README.' + process.env.PARTNER + '.md'); // README..md, for example README.cloud9.md 18 | // If the provider specific README exists 19 | if (fs.existsSync(partnerReadme)) { 20 | // Rename README.md to be README.strongloop.md 21 | if (fs.existsSync(readme)) { 22 | fs.renameSync(readme, slReadme); 23 | console.log(readme, '-->', slReadme); 24 | } 25 | // Rename README..md to be README.md 26 | fs.renameSync(partnerReadme, readme); 27 | console.log(partnerReadme, '-->', readme); 28 | } 29 | } -------------------------------------------------------------------------------- /test/fixtures/suite/config.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * We leave `rc` responsible for loading and merging the various configuration 8 | * sources. 9 | */ 10 | var nodeEnv = process.env.NODE_ENV || 'demo'; 11 | var config = require('rc')('loopback', { 12 | name: 'loopback-sample-app', 13 | env: nodeEnv 14 | }); 15 | 16 | if (!config[nodeEnv]) { 17 | config[nodeEnv] = nodeEnv == 'test' ? config.demo : {}; 18 | } 19 | 20 | module.exports = config; 21 | -------------------------------------------------------------------------------- /test/fixtures/suite/data-sources/db.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Dependencies 8 | */ 9 | 10 | var loopback = require('loopback'); 11 | var config = require('../config'); 12 | 13 | // Use the memory connector by default. 14 | var DB = (process.env.DB = process.env.DB || 'memory'); 15 | 16 | // Load the environmental settings for this database. 17 | config = config[config.env][DB]; 18 | 19 | if (!config) { 20 | config = {}; 21 | } 22 | 23 | console.error('Using the %s connector.', DB); 24 | console.error('To specify another connector:'); 25 | console.error(' DB=oracle node app or DB=oracle slc run app.js'); 26 | console.error(' DB=mongodb node app or DB=mongodb slc run app.js'); 27 | console.error(' DB=mysql node app or DB=mysql slc run app.js'); 28 | 29 | switch (DB) { 30 | case 'oracle': 31 | case 'mongodb': 32 | case 'mysql': 33 | var m = 'loopback-connector-' + DB; 34 | try { 35 | config.connector = require(m); 36 | } catch (e) { 37 | console.error('could not require %s', m); 38 | console.error('make sure it is listed in package.json'); 39 | console.error('then run'); 40 | console.error(' npm install'); 41 | 42 | throw e; 43 | } 44 | break; 45 | default: 46 | config.connector = loopback.Memory; 47 | break; 48 | } 49 | 50 | try { 51 | module.exports = loopback.createDataSource(config); 52 | } catch (e) { 53 | console.error('Error while initializing the data source:'); 54 | console.error(e.stack); 55 | console.error('\nPlease check your configuration settings and try again.'); 56 | process.exit(1); 57 | } 58 | 59 | if (DB === 'memory') { 60 | process.nextTick(function () { 61 | // import data 62 | require('../test-data/import'); 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /test/fixtures/suite/data-sources/rest-geocode.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | var loopback = require('loopback'); 7 | 8 | module.exports = loopback.createDataSource({ 9 | connector: require('loopback-connector-rest'), 10 | debug: false, 11 | operations: [ 12 | { 13 | template: { 14 | 'method': 'GET', 15 | 'url': 'http://maps.googleapis.com/maps/api/geocode/{format=json}', 16 | 'headers': { 17 | 'accepts': 'application/json', 18 | 'content-type': 'application/json' 19 | }, 20 | 'query': { 21 | 'address': '{street},{city},{zipcode}', 22 | 'sensor': '{sensor=false}' 23 | }, 24 | 'responsePath': '$.results[0].geometry.location' 25 | }, 26 | functions: { 27 | 'geocode': ['street', 'city', 'zipcode'] 28 | } 29 | } 30 | ]}); 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/car.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Module Dependencies 8 | */ 9 | 10 | var db = require('../data-sources/db'); 11 | var config = require('./car.json'); 12 | 13 | /** 14 | * product Model 15 | */ 16 | 17 | var Car = db.createModel( 18 | 'car', 19 | config.properties, 20 | config.options 21 | ); 22 | 23 | module.exports = Car; -------------------------------------------------------------------------------- /test/fixtures/suite/models/car.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "car", 3 | "options": { 4 | "oracle": { 5 | "schema": "DEMO", 6 | "table": "PRODUCT" 7 | }, 8 | "relations": { 9 | "reservations": { 10 | "model": "reservation", 11 | "type": "hasMany", 12 | "foreignKey": "productId" 13 | } 14 | } 15 | }, 16 | "properties": { 17 | "id": { 18 | "type": "string", 19 | "id": true 20 | }, 21 | "vin": { 22 | "type": "string" 23 | }, 24 | "year": { 25 | "type": "number" 26 | }, 27 | "make": { 28 | "type": "string" 29 | }, 30 | "model": { 31 | "type": "string" 32 | }, 33 | "image": { 34 | "type": "string" 35 | }, 36 | "carClass": { 37 | "type": "string" 38 | }, 39 | "color": { 40 | "type": "string" 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/customer.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Module Dependencies 8 | */ 9 | 10 | var db = require('../data-sources/db'); 11 | var config = require('./customer.json'); 12 | var loopback = require('loopback'); 13 | 14 | /** 15 | * customer Model 16 | */ 17 | 18 | var customer = module.exports = loopback.User.extend( 19 | 'customer', 20 | config.properties, 21 | config.options 22 | ); 23 | 24 | // attach to the db 25 | customer.attachTo(db); 26 | 27 | // TODO - this should be available as `hideRemotely: true` 28 | customer.beforeRemote('find', function(ctx, inst, next) { 29 | var args = ctx.args; 30 | var filter = args.filter || (args.filter = {}); 31 | var fields = filter.fields || (filter.fields = {}); 32 | 33 | // always hide password 34 | fields.password = false; 35 | 36 | next(); 37 | }); 38 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/customer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Customer", 3 | "options": { 4 | "idInjection": false, 5 | "oracle": { 6 | "schema": "BLACKPOOL", 7 | "table": "CUSTOMER" 8 | }, 9 | "extend": "User", 10 | "relations": { 11 | "accessTokens": { 12 | "model": "AccessToken", 13 | "type": "hasMany", 14 | "foreignKey": "userId" 15 | } 16 | } 17 | }, 18 | "properties": { 19 | "id": { 20 | "type": "String", 21 | "length": 20, 22 | "id": 1, 23 | "oracle": { 24 | "columnName": "ID", 25 | "dataType": "VARCHAR2", 26 | "dataLength": 20, 27 | "nullable": "N" 28 | } 29 | }, 30 | "name": { 31 | "type": "String", 32 | "required": false, 33 | "length": 40, 34 | "oracle": { 35 | "columnName": "NAME", 36 | "dataType": "VARCHAR2", 37 | "dataLength": 40, 38 | "nullable": "Y" 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/inventory.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Module Dependencies 8 | */ 9 | 10 | var db = require('../data-sources/db'); 11 | var config = require('./inventory.json'); 12 | 13 | /** 14 | * inventory Model 15 | */ 16 | 17 | var inventory = module.exports = db.createModel( 18 | 'inventory', 19 | config.properties, 20 | config.options 21 | ); 22 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/inventory.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Inventory", 3 | "options": { 4 | "idInjection": false, 5 | "oracle": { 6 | "schema": "BLACKPOOL", 7 | "table": "INVENTORY" 8 | }, 9 | "plural": "inventory" 10 | }, 11 | "properties": { 12 | "id": { 13 | "type": "String", 14 | "required": true, 15 | "id": true, 16 | "length": 20, 17 | "oracle": { 18 | "columnName": "ID", 19 | "dataType": "VARCHAR2", 20 | "dataLength": 20, 21 | "nullable": "N" 22 | } 23 | }, 24 | 25 | "productId": { 26 | "type": "String", 27 | "required": true, 28 | "length": 20, 29 | "oracle": { 30 | "columnName": "PRODUCT_ID", 31 | "dataType": "VARCHAR2", 32 | "dataLength": 20, 33 | "nullable": "N" 34 | } 35 | }, 36 | "locationId": { 37 | "type": "String", 38 | "required": true, 39 | "length": 20, 40 | "oracle": { 41 | "columnName": "LOCATION_ID", 42 | "dataType": "VARCHAR2", 43 | "dataLength": 20, 44 | "nullable": "N" 45 | } 46 | }, 47 | "available": { 48 | "type": "Number", 49 | "required": false, 50 | "length": 22, 51 | "oracle": { 52 | "columnName": "AVAILABLE", 53 | "dataType": "NUMBER", 54 | "dataLength": 22, 55 | "nullable": "Y" 56 | } 57 | }, 58 | "total": { 59 | "type": "Number", 60 | "required": false, 61 | "length": 22, 62 | "oracle": { 63 | "columnName": "TOTAL", 64 | "dataType": "NUMBER", 65 | "dataLength": 22, 66 | "nullable": "Y" 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/location.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Module Dependencies 8 | */ 9 | 10 | var db = require('../data-sources/db'); 11 | var config = require('./location.json'); 12 | var loopback = require('loopback'); 13 | var GeoPoint = loopback.GeoPoint; 14 | var TaskEmitter = require('strong-task-emitter'); 15 | var rest = require('../data-sources/rest-geocode'); 16 | var Inventory = require('./inventory'); 17 | 18 | /** 19 | * location Model 20 | */ 21 | 22 | var RentalLocation = module.exports = db.createModel( 23 | 'location', 24 | config.properties, 25 | config.options 26 | ); 27 | 28 | /** 29 | * Each location has inventory. 30 | */ 31 | 32 | RentalLocation.hasMany(Inventory); 33 | 34 | /** 35 | * Find nearby locations. 36 | */ 37 | 38 | RentalLocation.nearby = function(here, page, max, fn) { 39 | if (typeof page === 'function') { 40 | fn = page; 41 | page = 0; 42 | max = 0; 43 | } 44 | 45 | if (typeof max === 'function') { 46 | fn = max; 47 | max = 0; 48 | } 49 | 50 | var limit = 10; 51 | page = page || 0; 52 | max = Number(max || 100000); 53 | 54 | RentalLocation.find({ 55 | // find locations near the provided GeoPoint 56 | where: {geo: {near: here, maxDistance: max}}, 57 | // paging 58 | skip: limit * page, 59 | limit: limit 60 | }, fn); 61 | }; 62 | 63 | /** 64 | * Expose nearby as a remote method. 65 | */ 66 | 67 | loopback.remoteMethod( 68 | RentalLocation.nearby, 69 | { 70 | description: 'Find nearby locations around the geo point', 71 | accepts: [ 72 | {arg: 'here', type: 'GeoPoint', required: true, description: 'geo location (lat & lng)'}, 73 | {arg: 'page', type: 'Number', description: 'number of pages (page size=10)'}, 74 | {arg: 'max', type: 'Number', description: 'max distance in miles'} 75 | ], 76 | returns: {arg: 'locations', root: true} 77 | } 78 | ); 79 | 80 | /** 81 | * Build the geo data when saving using the google maps api. 82 | */ 83 | 84 | RentalLocation.beforeSave = function(next, loc) { 85 | // geo code the address 86 | if (!loc.geo) { 87 | rest.geocode(loc.street, loc.city, loc.state, function(err, result, res) { 88 | if (result && result[0]) { 89 | loc.geo = result[0].lng + ',' + result[0].lat; 90 | next(); 91 | } else { 92 | next(new Error('could not find location')); 93 | } 94 | }); 95 | } else { 96 | next(); 97 | } 98 | }; 99 | 100 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/location.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Location", 3 | "options": { 4 | "idInjection": false, 5 | "oracle": { 6 | "schema": "BLACKPOOL", 7 | "table": "LOCATION" 8 | } 9 | }, 10 | "properties": { 11 | "id": { 12 | "type": "String", 13 | "length": 20, 14 | "id": 1, 15 | "oracle": { 16 | "columnName": "ID", 17 | "dataType": "VARCHAR2", 18 | "dataLength": 20, 19 | "nullable": "N" 20 | } 21 | }, 22 | "street": { 23 | "type": "String", 24 | "required": false, 25 | "length": 64, 26 | "oracle": { 27 | "columnName": "STREET", 28 | "dataType": "VARCHAR2", 29 | "dataLength": 64, 30 | "nullable": "Y" 31 | } 32 | }, 33 | "city": { 34 | "type": "String", 35 | "required": false, 36 | "length": 64, 37 | "oracle": { 38 | "columnName": "CITY", 39 | "dataType": "VARCHAR2", 40 | "dataLength": 64, 41 | "nullable": "Y" 42 | } 43 | }, 44 | "zipcode": { 45 | "type": "Number", 46 | "required": false, 47 | "length": 20, 48 | "oracle": { 49 | "columnName": "ZIPCODE", 50 | "dataType": "VARCHAR2", 51 | "dataLength": 20, 52 | "nullable": "Y" 53 | } 54 | }, 55 | "name": { 56 | "type": "String", 57 | "required": false, 58 | "length": 32, 59 | "oracle": { 60 | "columnName": "NAME", 61 | "dataType": "VARCHAR2", 62 | "dataLength": 32, 63 | "nullable": "Y" 64 | } 65 | }, 66 | "geo": { 67 | "type": "GeoPoint" 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /test/fixtures/suite/models/note.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Module Dependencies 8 | */ 9 | 10 | var db = require('../data-sources/db'); 11 | var config = require('./note.json'); 12 | 13 | /** 14 | * Ammo Model 15 | */ 16 | 17 | var Note = module.exports = db.createModel( 18 | 'note', 19 | config.properties, 20 | config.options 21 | ); 22 | -------------------------------------------------------------------------------- /test/fixtures/suite/models/note.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Note", 3 | "options": { 4 | "plural": "notes" 5 | }, 6 | "properties": null 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/suite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "loopback-example-app", 3 | "version": "2.0.5", 4 | "description": "LoopBack Example Application", 5 | "main": "app.js", 6 | "dependencies": { 7 | "async": "~0.9.0", 8 | "loopback": "~1.9.1", 9 | "loopback-datasource-juggler": "~1.6.2", 10 | "request": "~2.36.0", 11 | "strong-task-emitter": "~0.0.5", 12 | "rc": "~0.4.0", 13 | "cors": "~2.3.1", 14 | "loopback-explorer": "~1.1.1", 15 | "loopback-connector-rest": "~1.1.4", 16 | "loopback-connector-mongodb": "~1.4.1", 17 | "loopback-connector-mysql": "~1.4.1", 18 | "weighted": "~0.2.2", 19 | "shuffle": "~0.2.1", 20 | "text-table": "~0.2.0", 21 | "iconv": "^2.1.10" 22 | }, 23 | "optionalDependencies": { 24 | "loopback-connector-mongodb": "~1.4.1", 25 | "loopback-connector-mysql": "~1.4.1", 26 | "weighted": "~0.2.2", 27 | "shuffle": "~0.2.1", 28 | "text-table": "~0.2.0" 29 | }, 30 | "devDependencies": { 31 | "supertest": "~0.13.0", 32 | "mocha": "~1.20.1" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "https://github.com/strongloop/loopback-example-app.git" 37 | }, 38 | "scripts": { 39 | "start": "node app", 40 | "install": "node build/readme.js", 41 | "test": "mocha -b -R spec" 42 | }, 43 | "author": { 44 | "name": "Ritchie Martori" 45 | }, 46 | "private": false, 47 | "readmeFilename": "README.cloud9.md", 48 | "license": "MIT", 49 | "readme": "# Welcome to the StrongLoop Cloud Experience!\n\nThis Cloud 9 Workspace contains several StrongLoop products including slc (CLI),\nLoopBack (API Server) and the StrongOps agent (DevOps and Performance Monitoring) \nfor you to try in the cloud (Cloud 9, to be exact) before [downloading and\ninstalling](http://www.strongloop.com/get-started) them on your own.\n\n## Before You Begin\n\nBefore we really get into the StrongLoop Suite, take a minute to familiarize\nyourself with the Cloud 9 IDE.\n\n * The left edge of the screen is a full view of the projects, with all the\nwonderful files and folders therein. Feel free to have a look around, but we\nrecommend finishing this README before exploring _too_ much.\n * On the top of the screen is the main menu, the toolbar, and the tabs. Boring,\nbut necessary. The most necessary element is the \"Run\" button, which we'll come\nto shortly.\n * At the bottom is a two-tab interface: Terminal and Output. The Terminal\nallows you to (as you might expect) run commands and interact with your current\napplication/project. The Output tab contains the logs for the running instance\nof your application.\n * When you run the application it'll be necessary to open multiple\n terminal windows to run multiple processes at the same time, to open\n a new terminal window go to the \"View\" menu and choose\n \"Terminals\"->New Terminal\n * The panel you're currently reading from is the main code editor; as we walk\nthrough Node examples, this panel will host all of the code being discussed.\n * The panel on the right is a full, working browser window. No browser? Let's\nfix that:\n\n## Running and Viewing the SLS Sample Application\n\nRemember that friendly, green \"Run\" button in the main menu? Go ahead and give\n'er a click. The LoopBack Sample App should begin running, and the bottom Output\ntab should become visible. If not, select Output in the bottom pane and take\nnote of a few things:\n\n```\nYour code is running at 'http://strongloop.soandso.cloud9beta.com'.\nImportant: use 'process.env.PORT' as the port and 'process.env.IP' as the host in your scripts!\nUsing the memory connector.\nTo specify another connector:\n DB=oracle node app\n```\n\nThe first is that the application has run successfully. If that's no the case\n(for example, if the word Exception and a big, scary stack trace have been\nproduced, then something's wrong!), then please [reach out](http://wwww.strongloop.com/strongloop-suite/strongsupport) and we'll\ngladly give you a hand.\n\nSecond, click the blue link in that first message. If the right browser tab was\nhidden, it'll reveal itself ... along with the home page of the SLS Sample.\n\nThird, it's MUCH better if you view the home page of the sample\napplication in a browser on your local machine. To do so, click the\ndetach icon on the browser pane, next to the close icon. This will\nopen a new browser tab or window on your local browser with the home\npage.\n\nLastly, note the URL of your sample app in the browser pane. It's\nthe same URL that should be displayed in the Output tab after \"Your\ncode is running at...\".\n\n## Where do I go from here?\n\nNow that you have the Sample App running, embiggen (and, yes, that's a word) the\nbrowser tab on the right or on your local browser and take a peek. The Next Steps are inside!\n\n", 50 | "bugs": { 51 | "url": "https://github.com/strongloop/loopback-example-app/issues" 52 | }, 53 | "homepage": "https://github.com/strongloop/loopback-example-app", 54 | "_id": "loopback-example-app@2.0.5", 55 | "_shasum": "ba1b215486898340d72cb3c9b79956c5d5fc9aa2", 56 | "_resolved": "git://github.com/strongloop/loopback-example-app#2530a004ce1e6577007d983ca8451a09b76168f0", 57 | "_from": "git://github.com/strongloop/loopback-example-app#production", 58 | "_fromGithub": true 59 | } 60 | -------------------------------------------------------------------------------- /test/fixtures/suite/public/css/code.css: -------------------------------------------------------------------------------- 1 | pre code { 2 | display: block; 3 | background: #1d1f21; 4 | color: #c5c8c6; 5 | font-family: Menlo, Monaco, Consolas, monospace; 6 | line-height: 1.5; 7 | border: 1px solid #ccc; 8 | padding: 10px; 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/suite/public/css/main.css: -------------------------------------------------------------------------------- 1 | /* Since we generate (both via Marked and JSDoc) empty anchors as jump link 2 | targets, we can target them here to offset said jump links. */ 3 | a[name] { 4 | display: block; position: relative; top: -70px; visibility: hidden; 5 | } 6 | 7 | .column { 8 | padding-top: 20px; 9 | padding-bottom: 20px; 10 | } 11 | 12 | .scroll-spy-target { 13 | border-right: 1px solid #ccc; 14 | } 15 | 16 | .readability { 17 | padding-left: 40px; 18 | max-width: 700px; 19 | } 20 | 21 | .hide-mobile { 22 | display: none; 23 | } 24 | 25 | @media (min-width: 992px) { 26 | .hide-mobile { 27 | display: inherit; 28 | } 29 | 30 | .column { 31 | position: absolute; 32 | top: 52px; 33 | bottom: 0; 34 | overflow-y: auto; 35 | } 36 | } 37 | 38 | .nav-pills a { 39 | color: #08592b; 40 | } 41 | 42 | .nav .depth-1 > a { font-size: 170%; } 43 | .nav .depth-2 > a { font-size: 140%; } 44 | .nav .depth-3 > a { font-size: 120%; } 45 | .nav .depth-4 > a { font-size: 100%; font-weight: 500; } 46 | .nav .depth-5 > a { font-size: 90%; font-weight: 500; } 47 | .nav .depth-6 > a { font-size: 80%; font-weight: 500; } 48 | 49 | .nav-pills li > a { 50 | padding: 3px 6px; 51 | } 52 | .nav .depth-1 { margin: 0; padding: 0 0 0 0; } 53 | .nav .depth-2 { margin: 0; padding: 20px 0 0 0; } 54 | .nav .depth-3 { margin: 0; padding: 10px 0 0 10px; } 55 | .nav .depth-4 { margin: 0; padding: 0 0 0 20px; } 56 | .nav .depth-5 { margin: 0; padding: 0 0 0 40px; } 57 | .nav .depth-6 { margin: 0; padding: 0 0 0 60px; } 58 | 59 | /* Annotations */ 60 | .code-arguments-hdr { 61 | text-transform: capitalize; 62 | font-size: 120%; 63 | margin-top: 20px; 64 | display: block; 65 | } 66 | .code-arg { 67 | margin-top: 10px; 68 | margin-left: 20px; 69 | } 70 | .code-arg div { 71 | display: inline; 72 | } 73 | .code-arg-types { 74 | font-weight: bold; 75 | } 76 | .code-arg-types:before { content: "{"; } 77 | .code-arg-types:after { content: "}"; } 78 | 79 | .readability { 80 | position: relative; 81 | height: 100%; 82 | } 83 | 84 | .intentionally-left-blank { 85 | display: block; 86 | height: 100%; 87 | } 88 | -------------------------------------------------------------------------------- /test/fixtures/suite/public/fonts/0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/strong-build/c2409b705115264e68ffb789ce728e740e565209/test/fixtures/suite/public/fonts/0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff -------------------------------------------------------------------------------- /test/fixtures/suite/public/fonts/OsJ2DjdpjqFRVUSto6IffLO3LdcAZYWl9Si6vvxL-qU.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/strong-build/c2409b705115264e68ffb789ce728e740e565209/test/fixtures/suite/public/fonts/OsJ2DjdpjqFRVUSto6IffLO3LdcAZYWl9Si6vvxL-qU.woff -------------------------------------------------------------------------------- /test/fixtures/suite/public/fonts/_aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/strong-build/c2409b705115264e68ffb789ce728e740e565209/test/fixtures/suite/public/fonts/_aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff -------------------------------------------------------------------------------- /test/fixtures/suite/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | LoopBack Sample Application 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 |
22 | 37 |
38 |
39 |
40 |

LoopBack

41 |

Welcome to the LoopBack sample application!

42 | LoopBack is an open source API Server built with Node.js for connecting enterprise data to devices and browsers. 43 | This sample application simulates an (imaginary) car rental dealer with locations in major cities around the world. 44 | They need to replace their existing desktop reservation system with a new mobile app. The app exposes a set of REST APIs for inventory data.

45 | Click around and explore the APIs! 46 |

47 |
48 |

Sample Requests

49 |

Click on the friendly GET buttons below to try out a few example requests!

50 |
51 |

GET /api/cars

52 |
53 |
54 |
55 |

GET /api/cars/2

56 |
57 |
58 |
59 |

GET /api/cars?filter

60 |

Actual request: GET /api/cars?filter[order]=make&filter[where][carClass]=fullsize&filter[limit]=3 (The three full size cars ordered by make)

61 |
62 |
63 |
64 |

GET /api/locations

65 |
66 |
67 |
68 |

GET /api/locations/nearby

69 |

Actual request: GET /api/locations/nearby?here[lat]=37.587409&here[lng]=-122.338225

70 |
71 |
72 |
73 |

GET /api/locations/:id/inventory

74 |

Actual request: GET /api/locations/88/inventory

75 |
76 |
77 |

Next Steps

78 |
79 |

API Explorer

80 |

The API Explorer is a great resource to check out the rest of the methods available to you through the LoopBack's default REST API.

81 |

Choose Your Own Adventure

82 |

83 | 88 |
89 |
90 |
91 |
92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /test/fixtures/suite/public/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap.js v3.0.0 by @fat and @mdo 3 | * Copyright 2013 Twitter Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover"},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .accordion-group > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),""===a.find(".popover-title").html()&&a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); -------------------------------------------------------------------------------- /test/fixtures/suite/public/js/jquery-scrollTo.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery.ScrollTo 3 | * Copyright (c) 2007-2012 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 4 | * Dual licensed under MIT and GPL. 5 | * Date: 4/09/2012 6 | * 7 | * @projectDescription Easy element scrolling using jQuery. 8 | * http://flesler.blogspot.com/2007/10/jqueryscrollto.html 9 | * @author Ariel Flesler 10 | * @version 1.4.3.1 11 | * 12 | * @id jQuery.scrollTo 13 | * @id jQuery.fn.scrollTo 14 | * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. 15 | * The different options for target are: 16 | * - A number position (will be applied to all axes). 17 | * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes 18 | * - A jQuery/DOM element ( logically, child of the element to scroll ) 19 | * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) 20 | * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. 21 | * - A percentage of the container's dimension/s, for example: 50% to go to the middle. 22 | * - The string 'max' for go-to-end. 23 | * @param {Number, Function} duration The OVERALL length of the animation, this argument can be the settings object instead. 24 | * @param {Object,Function} settings Optional set of settings or the onAfter callback. 25 | * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. 26 | * @option {Number, Function} duration The OVERALL length of the animation. 27 | * @option {String} easing The easing method for the animation. 28 | * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. 29 | * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. 30 | * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. 31 | * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. 32 | * @option {Function} onAfter Function to be called after the scrolling ends. 33 | * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. 34 | * @return {jQuery} Returns the same jQuery object, for chaining. 35 | * 36 | * @desc Scroll to a fixed position 37 | * @example $('div').scrollTo( 340 ); 38 | * 39 | * @desc Scroll relatively to the actual position 40 | * @example $('div').scrollTo( '+=340px', { axis:'y' } ); 41 | * 42 | * @desc Scroll using a selector (relative to the scrolled element) 43 | * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); 44 | * 45 | * @desc Scroll to a DOM element (same for jQuery object) 46 | * @example var second_child = document.getElementById('container').firstChild.nextSibling; 47 | * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ 48 | * alert('scrolled!!'); 49 | * }}); 50 | * 51 | * @desc Scroll on both axes, to different values 52 | * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); 53 | */ 54 | 55 | ;(function( $ ){ 56 | 57 | var $scrollTo = $.scrollTo = function( target, duration, settings ){ 58 | $(window).scrollTo( target, duration, settings ); 59 | }; 60 | 61 | $scrollTo.defaults = { 62 | axis:'xy', 63 | duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1, 64 | limit:true 65 | }; 66 | 67 | // Returns the element that needs to be animated to scroll the window. 68 | // Kept for backwards compatibility (specially for localScroll & serialScroll) 69 | $scrollTo.window = function( scope ){ 70 | return $(window)._scrollable(); 71 | }; 72 | 73 | // Hack, hack, hack :) 74 | // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) 75 | $.fn._scrollable = function(){ 76 | return this.map(function(){ 77 | var elem = this, 78 | isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1; 79 | 80 | if( !isWin ) 81 | return elem; 82 | 83 | var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem; 84 | 85 | return /webkit/i.test(navigator.userAgent) || doc.compatMode == 'BackCompat' ? 86 | doc.body : 87 | doc.documentElement; 88 | }); 89 | }; 90 | 91 | $.fn.scrollTo = function( target, duration, settings ){ 92 | if( typeof duration == 'object' ){ 93 | settings = duration; 94 | duration = 0; 95 | } 96 | if( typeof settings == 'function' ) 97 | settings = { onAfter:settings }; 98 | 99 | if( target == 'max' ) 100 | target = 9e9; 101 | 102 | settings = $.extend( {}, $scrollTo.defaults, settings ); 103 | // Speed is still recognized for backwards compatibility 104 | duration = duration || settings.duration; 105 | // Make sure the settings are given right 106 | settings.queue = settings.queue && settings.axis.length > 1; 107 | 108 | if( settings.queue ) 109 | // Let's keep the overall duration 110 | duration /= 2; 111 | settings.offset = both( settings.offset ); 112 | settings.over = both( settings.over ); 113 | 114 | return this._scrollable().each(function(){ 115 | // Null target yields nothing, just like jQuery does 116 | if (target == null) return; 117 | 118 | var elem = this, 119 | $elem = $(elem), 120 | targ = target, toff, attr = {}, 121 | win = $elem.is('html,body'); 122 | 123 | switch( typeof targ ){ 124 | // A number will pass the regex 125 | case 'number': 126 | case 'string': 127 | if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){ 128 | targ = both( targ ); 129 | // We are done 130 | break; 131 | } 132 | // Relative selector, no break! 133 | targ = $(targ,this); 134 | if (!targ.length) return; 135 | case 'object': 136 | // DOMElement / jQuery 137 | if( targ.is || targ.style ) 138 | // Get the real position of the target 139 | toff = (targ = $(targ)).offset(); 140 | } 141 | $.each( settings.axis.split(''), function( i, axis ){ 142 | var Pos = axis == 'x' ? 'Left' : 'Top', 143 | pos = Pos.toLowerCase(), 144 | key = 'scroll' + Pos, 145 | old = elem[key], 146 | max = $scrollTo.max(elem, axis); 147 | 148 | if( toff ){// jQuery / DOMElement 149 | attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); 150 | 151 | // If it's a dom element, reduce the margin 152 | if( settings.margin ){ 153 | attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; 154 | attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; 155 | } 156 | 157 | attr[key] += settings.offset[pos] || 0; 158 | 159 | if( settings.over[pos] ) 160 | // Scroll to a fraction of its width/height 161 | attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos]; 162 | }else{ 163 | var val = targ[pos]; 164 | // Handle percentage values 165 | attr[key] = val.slice && val.slice(-1) == '%' ? 166 | parseFloat(val) / 100 * max 167 | : val; 168 | } 169 | 170 | // Number or 'number' 171 | if( settings.limit && /^\d+$/.test(attr[key]) ) 172 | // Check the limits 173 | attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max ); 174 | 175 | // Queueing axes 176 | if( !i && settings.queue ){ 177 | // Don't waste time animating, if there's no need. 178 | if( old != attr[key] ) 179 | // Intermediate animation 180 | animate( settings.onAfterFirst ); 181 | // Don't animate this axis again in the next iteration. 182 | delete attr[key]; 183 | } 184 | }); 185 | 186 | animate( settings.onAfter ); 187 | 188 | function animate( callback ){ 189 | $elem.animate( attr, duration, settings.easing, callback && function(){ 190 | callback.call(this, target, settings); 191 | }); 192 | }; 193 | 194 | }).end(); 195 | }; 196 | 197 | // Max scrolling position, works on quirks mode 198 | // It only fails (not too badly) on IE, quirks mode. 199 | $scrollTo.max = function( elem, axis ){ 200 | var Dim = axis == 'x' ? 'Width' : 'Height', 201 | scroll = 'scroll'+Dim; 202 | 203 | if( !$(elem).is('html,body') ) 204 | return elem[scroll] - $(elem)[Dim.toLowerCase()](); 205 | 206 | var size = 'client' + Dim, 207 | html = elem.ownerDocument.documentElement, 208 | body = elem.ownerDocument.body; 209 | 210 | return Math.max( html[scroll], body[scroll] ) 211 | - Math.min( html[size] , body[size] ); 212 | }; 213 | 214 | function both( val ){ 215 | return typeof val == 'object' ? val : { top:val, left:val }; 216 | }; 217 | 218 | })( jQuery ); -------------------------------------------------------------------------------- /test/fixtures/suite/public/js/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | // Switch from empty anchors to id-ed headings 3 | $('a[name]').get().forEach(function (i) { 4 | var $i = $(i); 5 | 6 | $i.next().attr('id', $i.attr('name')); 7 | $i.detach(); 8 | }); 9 | 10 | $('.scroll-spy-target').on('activate.bs.scrollspy', function (event) { 11 | var $this = $(this); 12 | var $target = $(event.target); 13 | 14 | $this.scrollTo($target, 0, { 15 | offset: -($this.innerHeight() / 2) 16 | }); 17 | }); 18 | 19 | function truncate(json, length) { 20 | if (length == null) { 21 | length = 20; 22 | } 23 | 24 | var split = json.split('\n'); 25 | 26 | if (split.length <= length) { 27 | return json; 28 | } 29 | 30 | return split.slice(0, length).join('\n') + '\n...'; 31 | } 32 | 33 | $('[data-route]').each(function () { 34 | // For now, GET only. 35 | var $this = $(this); 36 | var $target = $($this.attr('data-target')); 37 | var route = $this.attr('data-route'); 38 | var length = $this.attr('data-truncate') || Infinity; 39 | 40 | $this.click(function () { 41 | $target.text('Loading ' + route + ' ...'); 42 | 43 | $.ajax(route, { 44 | success: function (data) { 45 | var json = truncate(JSON.stringify(data, null, 2), length); 46 | $target.text(json); 47 | } 48 | }); 49 | 50 | console.log($this.attr('data-route')); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/fixtures/suite/test-data/customers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "email": "foo@bar.com", 4 | "username": "foo", 5 | "password": "123456" 6 | }, 7 | { 8 | "email": "bar@bar.com", 9 | "username": "bar", 10 | "password": "123456" 11 | }, 12 | { 13 | "email": "bat@bar.com", 14 | "username": "bat", 15 | "password": "123456" 16 | }, 17 | { 18 | "email": "baz@bar.com", 19 | "username": "baz", 20 | "password": "123456" 21 | } 22 | ] -------------------------------------------------------------------------------- /test/fixtures/suite/test-data/discover.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Run `node import.js` to import the test data into the db. 8 | */ 9 | 10 | var cars = require('./cars.json'); 11 | var loopback = require('loopback'); 12 | var fs = require('fs'); 13 | var path = require('path'); 14 | var db = require('../data-sources/db'); 15 | var modelsDir = path.join(__dirname, '..', 'models'); 16 | 17 | // tables we care about 18 | // TOOD - remove this once oracle is cleaned out 19 | var include = [ 20 | 'PRODUCT', 21 | 'INVENTORY', 22 | 'LOCATION', 23 | 'CUSTOMER' 24 | ]; 25 | 26 | // discover tables 27 | db.discoverModelDefinitions(function(err, models) { 28 | if (err) { 29 | console.log(err); 30 | } else { 31 | models.forEach(function(def) { 32 | if (~include.indexOf(def.name)) { 33 | console.log('discovering', def.name); 34 | 35 | db.discoverSchema(def.name, function(err, schema) { 36 | fs.writeFileSync( 37 | path.join(modelsDir, schema.name.toLowerCase() + '.json'), 38 | JSON.stringify(schema, null, 2) 39 | ); 40 | }); 41 | 42 | var template = [ 43 | '/** ', 44 | ' * Module Dependencies ', 45 | ' */ ', 46 | ' ', 47 | 'var db = require("../data-sources/db"); ', 48 | 'var config = require("./{name}.json"); ', 49 | ' ', 50 | '/** ', 51 | ' * {name} Model ', 52 | ' */ ', 53 | ' ', 54 | 'var {name} = module.exports = db.createModel( ', 55 | ' "{name}", ', 56 | ' config.properties, ', 57 | ' config.options ', 58 | '); ']; 59 | 60 | template = template.join('\n').replace(/\{name\}/g, def.name.toLowerCase()); 61 | fs.writeFileSync(path.join(modelsDir, def.name.toLowerCase() + '.js'), template); 62 | } 63 | }); 64 | } 65 | }); 66 | -------------------------------------------------------------------------------- /test/fixtures/suite/test-data/generate-inventory.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /* 7 | * This short script is only for generating data in a JSON file. 8 | * 9 | * Usage: 10 | * node app.js # in the app dir 11 | * node generate-inventory.js # in this dir 12 | */ 13 | 14 | var fs = require('fs'); 15 | var inventory = []; 16 | var request = require('request'); 17 | 18 | request('http://localhost:3000/cars', {json: true}, function(err, res, cars) { 19 | request('http://localhost:3000/locations', {json: true}, function(err, res, locations) { 20 | locations.forEach(function(loc) { 21 | cars.forEach(function(car) { 22 | var availableAtLocation = rand(0, 100); 23 | 24 | inventory.push({ 25 | productId: car.id, 26 | locationId: loc.id, 27 | available: rand(0, availableAtLocation), 28 | total: availableAtLocation 29 | }); 30 | 31 | }); 32 | }); 33 | 34 | fs.writeFileSync('inventory.json', JSON.stringify(inventory, null, 2)); 35 | }); 36 | }); 37 | 38 | function rand(min, max) { 39 | return Math.floor(Math.random() * (max - min + 1)) + min; 40 | } 41 | -------------------------------------------------------------------------------- /test/fixtures/suite/test-data/import.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Run `node import.js` to import the test data into the db. 8 | */ 9 | 10 | var db = require('../data-sources/db'); 11 | var cars = require('./cars.json'); 12 | var customers = require('./customers.json'); 13 | var inventory = require('./inventory.json'); 14 | var locations = require('./locations.json'); 15 | // var loopback = require('loopback'); 16 | var Inventory = require('../models/inventory'); 17 | var Location = require('../models/location'); 18 | var Customer = require('../models/customer'); 19 | var Car = require('../models/car'); 20 | 21 | var async = require('async'); 22 | 23 | var events = require('events'); 24 | var emitter = new events.EventEmitter(); 25 | 26 | module.exports = emitter; 27 | 28 | var ids = { 29 | }; 30 | 31 | function importData(Model, data, cb) { 32 | 33 | // console.log('Importing data for ' + Model.modelName); 34 | Model.destroyAll(function (err) { 35 | if(err) { 36 | cb(err); 37 | return; 38 | } 39 | async.each(data, function (d, callback) { 40 | if(ids[Model.modelName] === undefined) { 41 | ids[Model.modelName] = 1; 42 | } 43 | d.id = ids[Model.modelName]++; 44 | Model.create(d, callback); 45 | }, cb); 46 | }); 47 | } 48 | 49 | async.series( 50 | [ 51 | function (cb) { 52 | db.autoupdate(cb); 53 | }, 54 | 55 | importData.bind(null, Location, locations), 56 | importData.bind(null, Car, cars), 57 | importData.bind(null, Inventory, inventory), 58 | importData.bind(null, Customer, customers) 59 | 60 | /* 61 | function (cb) { 62 | Car.destroyAll(function (err) { 63 | if(err) { 64 | cb(err); 65 | return; 66 | } 67 | async.eachSeries(cars, function (car, callback) { 68 | car.id = ids.car++; 69 | delete car.dealerId; 70 | Car.create(car, callback); 71 | }, cb); 72 | }); 73 | }, 74 | */], function (err, results) { 75 | if(err) { 76 | console.error(err); 77 | emitter.emit('error', err); 78 | } else { 79 | emitter.emit('done'); 80 | } 81 | }); 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /test/fixtures/suite/test-data/locations.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "City Rent-a-Car", 4 | "street": "1433 Bush St", 5 | "city": "San Francisco", 6 | "state": "CA", 7 | "country": "US", 8 | "phone": "(415) 359-1331" 9 | }, 10 | { 11 | "name": "Thrifty Car Rental", 12 | "street": "350 O'Farrell St", 13 | "city": "San Francisco", 14 | "state": "CA", 15 | "country": "US", 16 | "phone": "(415) 788-8111" 17 | }, 18 | { 19 | "name": "GoCar Tours", 20 | "street": "431 Beach St", 21 | "city": "San Francisco", 22 | "state": "CA", 23 | "country": "US", 24 | "phone": "(415) 441-5695" 25 | }, 26 | { 27 | "name": "Enterprise Rent-A-Car", 28 | "street": "727 Folsom St", 29 | "city": "San Francisco", 30 | "state": "CA", 31 | "country": "US", 32 | "phone": "(415) 546-6777" 33 | }, 34 | { 35 | "name": "Budget Rent A Car", 36 | "street": "5 Embarcadero Center", 37 | "city": "San Francisco", 38 | "state": "CA", 39 | "country": "US", 40 | "phone": "(415) 433-3717" 41 | }, 42 | { 43 | "name": "National Car Rental", 44 | "street": "750 Bush St", 45 | "city": "San Francisco", 46 | "state": "CA", 47 | "country": "US", 48 | "phone": "(650) 238-5302" 49 | }, 50 | { 51 | "name": "Enterprise Rent-A-Car", 52 | "street": "1600 Mission St", 53 | "city": "San Francisco", 54 | "state": "CA", 55 | "country": "US", 56 | "phone": "(415) 522-5900" 57 | }, 58 | { 59 | "name": "Alamo Rent A Car", 60 | "street": "750 Bush St", 61 | "city": "San Francisco", 62 | "state": "CA", 63 | "country": "US", 64 | "phone": "(888) 826-6893" 65 | }, 66 | { 67 | "name": "DOLLAR RENT A CAR", 68 | "street": "364 O'Farrell St", 69 | "city": "San Francisco", 70 | "state": "CA", 71 | "country": "US", 72 | "phone": "(866) 434-2226" 73 | }, 74 | { 75 | "name": "RelayRides", 76 | "street": "116 Natoma St", 77 | "city": "San Francisco", 78 | "state": "CA", 79 | "country": "US", 80 | "phone": "(866) 735-2901" 81 | }, 82 | { 83 | "name": "Pacific Car Rentals", 84 | "street": "501 W Georgia St", 85 | "city": "Vancouver", 86 | "state": "BC", 87 | "country": "Canada", 88 | "phone": "+1 604-689-4506" 89 | }, 90 | { 91 | "name": "Enterprise Rent-A-Car", 92 | "street": "1250 Granville St #2", 93 | "city": "Vancouver", 94 | "state": "BC", 95 | "country": "Canada", 96 | "phone": "+1 604-688-5500" 97 | }, 98 | { 99 | "name": "Riz Rent a Car & Truck", 100 | "street": "1361 Robson St", 101 | "city": "Vancouver", 102 | "state": "BC", 103 | "country": "Canada", 104 | "phone": "+1 604-689-1231" 105 | }, 106 | { 107 | "name": "Vancouver West Side", 108 | "street": "906 W Broadway #101", 109 | "city": "Vancouver", 110 | "state": "BC", 111 | "country": "Canada ‎", 112 | "phone": "+1 604-606-2821" 113 | }, 114 | { 115 | "name": "Enterprise Rent-A-Car ", 116 | "street": "3510 Fraser St", 117 | "city": "Vancouver", 118 | "state": "BC", 119 | "country": "Canada", 120 | "phone": "+1 604-872-7368" 121 | }, 122 | { 123 | "name": "Hertz Rent a Car", 124 | "street": "1270 Granville St", 125 | "city": "Vancouver", 126 | "state": "BC", 127 | "country": "Canada", 128 | "phone": "+1 604-606-4711" 129 | }, 130 | { 131 | "name": "Mr Rent-A-Car (Downtown) Ltd", 132 | "street": "968 Kingsway", 133 | "city": "Vancouver", 134 | "state": "BC", 135 | "country": "Canada", 136 | "phone": "+1 604-876-7777" 137 | }, 138 | { 139 | "name": "Thrifty Car Rental", 140 | "street": "413 Seymour St", 141 | "city": "Vancouver", 142 | "state": "BC", 143 | "country": "Canada", 144 | "phone": "+1 604-606-1666" 145 | }, 146 | { 147 | "name": "Budget Car & Truck Rental", 148 | "street": "855 Kingsway", 149 | "city": "Vancouver", 150 | "state": "BC", 151 | "country": "Canada", 152 | "phone": "+1 604-668-7000" 153 | }, 154 | { 155 | "name": "Pacific Car Rentals", 156 | "street": "1132 W Hastings St", 157 | "city": "Vancouver", 158 | "state": "BC", 159 | "country": "Canada", 160 | "phone": "+1 604-689-3994" 161 | }, 162 | { 163 | "name": "Dollar Rent A Car", 164 | "street": "1659 Airport Blvd", 165 | "city": "San Jose", 166 | "state": "CA", 167 | "country": "US", 168 | "phone": "(866) 434-2226" 169 | }, 170 | { 171 | "name": "Avis Rent A Car", 172 | "street": "1659 Airport Blvd #3", 173 | "city": "San Jose", 174 | "state": "CA", 175 | "country": "US", 176 | "phone": "(408) 993-2224" 177 | }, 178 | { 179 | "name": "National Car Rental", 180 | "street": "1659 Airport Blvd #8", 181 | "city": "San Jose", 182 | "state": "CA", 183 | "country": "US", 184 | "phone": "(408) 288-4662" 185 | }, 186 | { 187 | "name": "Alamo Rent A Car", 188 | "street": "2300 Airport Blvd #120", 189 | "city": "San Jose", 190 | "state": "CA", 191 | "country": "US", 192 | "phone": "(888) 826-6893" 193 | }, 194 | { 195 | "name": "Enterprise Rent-A-Car", 196 | "street": "598 S 1st St", 197 | "city": "San Jose", 198 | "state": "CA", 199 | "country": "US", 200 | "phone": "(408) 286-4444" 201 | }, 202 | { 203 | "name": "Club Sportiva", 204 | "street": "521 Charcot Ave #237", 205 | "city": "San Jose", 206 | "state": "CA", 207 | "country": "US", 208 | "phone": "(866) 719-1600" 209 | }, 210 | { 211 | "name": "Service Rent-A-Car Inc", 212 | "street": "698 S 1st St", 213 | "city": "San Jose", 214 | "state": "CA", 215 | "country": "US", 216 | "phone": "(408) 554-6351" 217 | }, 218 | { 219 | "name": "Budget Rent A Car", 220 | "street": "1659 Airport Blvd #3", 221 | "city": "San Jose", 222 | "state": "CA", 223 | "country": "US", 224 | "phone": "(408) 286-7850" 225 | }, 226 | { 227 | "name": "Hertz Rent A Car", 228 | "street": "1659 Airport Blvd", 229 | "city": "San Jose", 230 | "state": "CA", 231 | "country": "US", 232 | "phone": "(408) 450-6000" 233 | }, 234 | { 235 | "name": "Enterprise Rent-A-Car", 236 | "street": "1485 Kerley Dr", 237 | "city": "San Jose", 238 | "state": "CA", 239 | "country": "US", 240 | "phone": "(408) 437-1001" 241 | }, 242 | { 243 | "name": "Enterprise Rent-A-Car", 244 | "street": "445 SW Pine St", 245 | "city": "Portland", 246 | "state": "OR", 247 | "country": "US", 248 | "phone": "(503) 275-5359" 249 | }, 250 | { 251 | "name": "Enterprise Rent-A-Car", 252 | "street": "1623 W Burnside St", 253 | "city": "Portland", 254 | "state": "OR", 255 | "country": "US", 256 | "phone": "(503) 220-8200" 257 | }, 258 | { 259 | "name": "Enterprise Rent-A-Car", 260 | "street": "8360 SW Barbur Blvd", 261 | "city": "Portland", 262 | "state": "OR", 263 | "country": "US", 264 | "phone": "(503) 977-7700" 265 | }, 266 | { 267 | "name": "Enterprise Rent-A-Car", 268 | "street": "611 E Burnside St", 269 | "city": "Portland", 270 | "state": "OR", 271 | "country": "US", 272 | "phone": "(503) 230-1212" 273 | }, 274 | { 275 | "name": "Enterprise Rent-A-Car", 276 | "street": "2740 NE Sandy Blvd", 277 | "city": "Portland", 278 | "state": "OR", 279 | "country": "US", 280 | "phone": "(503) 963-1795" 281 | }, 282 | { 283 | "name": "Budget Rent-A-Car", 284 | "street": "3400 NE Columbia Blvd", 285 | "city": "Portland", 286 | "state": "OR", 287 | "country": "US", 288 | "phone": "(503) 288-2985" 289 | }, 290 | { 291 | "name": "Hertz Rent A Car", 292 | "street": "1441 NE 2nd Ave", 293 | "city": "Portland", 294 | "state": "OR", 295 | "country": "US", 296 | "phone": "(503) 282-2644" 297 | }, 298 | { 299 | "name": "Avis Rent A Car", 300 | "street": "330 SW Washington St", 301 | "city": "Portland", 302 | "state": "OR", 303 | "country": "US", 304 | "phone": "(503) 227-0220" 305 | }, 306 | { 307 | "name": "Dollar Rent a Car", 308 | "street": "132 NW Broadway", 309 | "city": "Portland", 310 | "state": "OR", 311 | "country": "US", 312 | "phone": "(503) 228-3540" 313 | }, 314 | { 315 | "name": "Crown Auto Rental", 316 | "street": "4826 NE 105 Ave", 317 | "city": "Portland", 318 | "state": "OR", 319 | "country": "US", 320 | "phone": "(503) 230-1103" 321 | } 322 | ] -------------------------------------------------------------------------------- /test/fixtures/suite/test-data/updatedb.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Run `node import.js` to import the test data into the db. 8 | */ 9 | 10 | var app = require('../app'); 11 | var db = app.dataSources.db; 12 | 13 | var models = ['AccessToken', 'Role', 'ACL', 'RoleMapping']; 14 | db.autoupdate(models, function(err) { 15 | if(err) { 16 | console.error(err); 17 | } else { 18 | console.log('Tables are created for ', models); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /test/fixtures/suite/test/rest.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * REST API Tests 8 | */ 9 | 10 | function json(verb, url) { 11 | return request(app)[verb](url) 12 | .set('Content-Type', 'application/json') 13 | .set('Accept', 'application/json') 14 | .expect('Content-Type', /json/); 15 | } 16 | 17 | describe('REST', function() { 18 | /** 19 | * Expected Input Tests 20 | */ 21 | 22 | describe('Expected Usage', function() { 23 | 24 | describe('GET /api/cars', function() { 25 | it('should return a list of all cars', function(done) { 26 | json('get', '/api/cars') 27 | .expect(200) 28 | .end(function(err, res) { 29 | assert(Array.isArray(res.body)); 30 | assert.equal(res.body.length, testData.cars.length); 31 | 32 | done(); 33 | }); 34 | }); 35 | }); 36 | 37 | describe('POST /api/cars', function() { 38 | it('should create a new car', function(done) { 39 | json('post', '/api/cars') 40 | .send({ 41 | "vin": "ebaddaa5-35bb-4b33-a388-87203acb6478", 42 | "year": "2013", 43 | "make": "Dodge", 44 | "model": "Taurus", 45 | "image": "/images/car/car_0.jpg", 46 | "carClass": "suv", 47 | "color": "white" 48 | }) 49 | .expect(200) 50 | .end(function(err, res) { 51 | assert(typeof res.body === 'object'); 52 | assert(res.body.id, 'must have an id'); 53 | done(); 54 | }); 55 | }); 56 | }); 57 | 58 | describe('PUT /api/cars/:id', function() { 59 | it('should update a car with the given id', function(done) { 60 | json('get', '/api/cars') 61 | .expect(200, function(err, res) { 62 | var cars = res.body; 63 | var car = cars[0]; 64 | 65 | json('put', '/api/cars/' + car.id) 66 | .send({ 67 | year: 2000, 68 | color: 'red' 69 | }) 70 | .expect(200, function(err, res) { 71 | var updatedCar = res.body; 72 | assert(updatedCar); 73 | assert(updatedCar.id); 74 | assert.equal(updatedCar.id, car.id); 75 | assert.equal(updatedCar.year, 2000); 76 | json('get', '/api/cars/' + car.id) 77 | .expect(200, function(err, res) { 78 | var foundCar = res.body; 79 | assert.equal(foundCar.id, car.id); 80 | assert.equal(foundCar.year, 2000); 81 | assert.equal(foundCar.color, 'red'); 82 | done(); 83 | }); 84 | }); 85 | }); 86 | }); 87 | }); 88 | 89 | describe('GET /api/locations', function() { 90 | it('should return a list of locations', function(done) { 91 | json('get', '/api/locations') 92 | .expect(200, function(err, res) { 93 | var locations = res.body; 94 | assert(Array.isArray(locations)); 95 | assert.equal(locations.length, testData.locations.length); 96 | done(); 97 | }); 98 | }); 99 | }); 100 | 101 | describe('GET /api/locations/nearby', function() { 102 | it('should return a list of locations near given point', function(done) { 103 | json('get', '/api/locations/nearby?here[lat]=37.7883415&here[lng]=-122.4209035') 104 | .expect(200, function(err, res) { 105 | var locations = res.body; 106 | assert(Array.isArray(locations)); 107 | assert.equal(locations[0].name, 'City Rent-a-Car'); 108 | assert.equal(locations.length, 10); 109 | locations.forEach(function(l) { 110 | assert(l.geo); 111 | assert.equal(typeof l.geo.lat, 'number'); 112 | assert.equal(typeof l.geo.lng, 'number'); 113 | }); 114 | assert.equal(locations[locations.length - 1].city, 'San Francisco'); 115 | done(); 116 | }); 117 | }); 118 | }); 119 | 120 | describe('GET /api/locations/:id/inventory', function() { 121 | it('should return a list of inventory for the given location id', function(done) { 122 | json('get', '/api/locations/5/inventory') 123 | .expect(200, function(err, res) { 124 | var inventory = res.body; 125 | assert.equal(inventory.length, 87); 126 | inventory.forEach(function(inv) { 127 | assert.equal(typeof inv.total, 'number'); 128 | assert.equal(typeof inv.available, 'number'); 129 | }); 130 | done(); 131 | }); 132 | }); 133 | }); 134 | 135 | // describe('GET /api/customers', function(){ 136 | // it('should return a 401 when not logged in as an admin', function(done) { 137 | // json('get', '/api/customers').expect(401, done); 138 | // }); 139 | // 140 | // it('should return all users when logged in as an admin', function(done) { 141 | // 142 | // }); 143 | // }); 144 | 145 | describe('/api/customers', function() { 146 | var credentials = { email: 'a-@example.com', password: 'a-password' }; 147 | var customer; 148 | var token; 149 | it('should create new customer on POST', function(done) { 150 | json('post', '/api/customers') 151 | .send(credentials) 152 | .expect(200, function(err, res) { 153 | if (err) return done(err); 154 | customer = res.body; 155 | assert.equal(customer.email, credentials.email); 156 | done(); 157 | }); 158 | }); 159 | 160 | it('should login existing customer on POST /api/customers/login', function(done) { 161 | json('post', '/api/customers/login') 162 | .send(credentials) 163 | .expect(200, function(err, res) { 164 | if (err) return done(err); 165 | token = res.body; 166 | assert.equal(token.userId, customer.id); 167 | done(); 168 | }); 169 | }); 170 | 171 | it('should allow GET /api/customers/{my-id}', function(done) { 172 | json('get', '/api/customers/' + customer.id) 173 | .set('Authorization', token.id) 174 | .expect(200, function(err, res) { 175 | if (err) return done(err); 176 | assert.equal(customer.email, res.body.email); 177 | done(); 178 | }); 179 | }); 180 | 181 | it('should not allow GET /api/customers/{another-id}', function(done) { 182 | json('get', '/api/customers/' + (customer.id + 1000)) 183 | .set('Authorization', token.id) 184 | .expect(401, function(err) { 185 | done(err); 186 | }); 187 | }); 188 | 189 | it('should logout existing customer on POST /api/customers/logout', function(done) { 190 | json('post', '/api/customers/logout') 191 | .set('Authorization', token.id) 192 | .send({}) 193 | .expect(204, done); 194 | }); 195 | }); 196 | }); 197 | 198 | describe('Unexpected Usage', function(){ 199 | describe('POST /api/cars/:id', function(){ 200 | it('should not crash the server when posting a bad id', function(done) { 201 | json('post', '/api/cars/foobar').send({}).expect(404, done); 202 | }); 203 | }); 204 | }); 205 | 206 | }); 207 | -------------------------------------------------------------------------------- /test/fixtures/suite/test/support.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | /** 7 | * Force memory adapter 8 | */ 9 | 10 | /** 11 | * Utils 12 | */ 13 | process.env.NODE_ENV = 'test'; 14 | 15 | request = require('supertest'); 16 | app = require('../app'); 17 | assert = require('assert'); 18 | importer = require('../test-data/import'); 19 | 20 | /** 21 | * Test Data 22 | */ 23 | 24 | testData = { 25 | cars: require('../test-data/cars'), 26 | locations: require('../test-data/locations') 27 | }; 28 | 29 | before(function(done) { 30 | this.timeout(50000); 31 | console.error('Importing test data, this may take long time.'); 32 | importer.on('error', done); 33 | importer.on('done', done); 34 | }); 35 | -------------------------------------------------------------------------------- /test/fixtures/zero-dependency/index.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | require('net').createServer().listen(0); 9 | -------------------------------------------------------------------------------- /test/fixtures/zero-dependency/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zero-dependency", 3 | "version": "1.0.0", 4 | "description": "package with no dependecies and no node_modules for test", 5 | "main": "index.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/test-basic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | function touchAndCommit() { 6 | touch $1 7 | git add $1 8 | git commit -m "add-$1" 9 | } 10 | 11 | function die() { 12 | echo "not ok # $1" 13 | exit 1 14 | } 15 | 16 | rm -rf _onto 17 | mkdir _onto 18 | cd _onto 19 | git init 20 | touchAndCommit .gitignore 21 | echo '{"name": "build-test-pkg"}' > package.json 22 | touchAndCommit package.json 23 | 24 | git checkout -b dst master 25 | touchAndCommit only-on-dst 26 | 27 | git checkout -b src master 28 | touchAndCommit only-on-src 29 | 30 | git diff --quiet src dst 2> /dev/null && die 'src and dst must be different' 31 | 32 | BEFORE_HEAD=`git show-ref -s --head HEAD` 33 | BEFORE_DST=`git show-ref -s --heads dst` 34 | 35 | touch build.out 36 | node ../../bin/sl-build --commit --onto dst 37 | 38 | AFTER_DST=`git show-ref -s --heads dst` 39 | AFTER_HEAD=`git show-ref -s --head HEAD` 40 | 41 | test "$BEFORE_HEAD" == "$AFTER_HEAD" || die 'sl-build should not modify HEAD' 42 | test "$BEFORE_DST" != "$AFTER_DST" || die 'sl-build should modify --onto branch' 43 | 44 | git cat-file -e master:build.out 2> /dev/null && die 'build.out should not exist on master' 45 | git cat-file -e dst:build.out || die 'build.out should exist on dst' 46 | 47 | BEFORE_HEAD=`git show-ref -s --head HEAD` 48 | BEFORE_DST=`git show-ref -s --heads dst` 49 | 50 | node ../../bin/sl-build --commit --onto dst 51 | 52 | AFTER_DST=`git show-ref -s --heads dst` 53 | AFTER_HEAD=`git show-ref -s --head HEAD` 54 | 55 | echo "BH $BEFORE_HEAD BD $BEFORE_DST AH $AFTER_HEAD AD $AFTER_DST" 56 | test "$BEFORE_HEAD" == "$AFTER_HEAD" || die 'sl-build should not modify HEAD' 57 | test "$BEFORE_DST" == "$AFTER_DST" || die 'sl-build should not modify --onto branch if nothing changed' 58 | 59 | BEFORE_HEAD=`git show-ref -s --head HEAD` 60 | BEFORE_DST=`git show-ref -s --heads dst` 61 | BEFORE_SRC=`git show-ref -s --heads src` 62 | 63 | touch build2.out 64 | node ../../bin/sl-build --commit --onto HEAD || die 'Failed to deploy to current branch' 65 | 66 | AFTER_SRC=`git show-ref -s --heads src` 67 | AFTER_DST=`git show-ref -s --heads dst` 68 | AFTER_HEAD=`git show-ref -s --head HEAD` 69 | 70 | echo "BH $BEFORE_HEAD BD $BEFORE_DST AH $AFTER_HEAD AD $AFTER_DST" 71 | test "$BEFORE_HEAD" != "$AFTER_HEAD" || die 'sl-build should modify HEAD if used for deploy' 72 | test "$BEFORE_SRC" != "$AFTER_SRC" || die 'sl-build should modify branch HEAD pointed to' 73 | test "$BEFORE_DST" == "$AFTER_DST" || die 'sl-build should NOT modify other branches' 74 | 75 | git cat-file -e master:build2.out 2> /dev/null && die 'build.out should not exist on master' 76 | git cat-file -e dst:build2.out 2> /dev/null && die 'build.out should not exist on dst' 77 | git cat-file -e src:build2.out || die 'build.out should exist on src' 78 | 79 | BEFORE_HEAD=`git show-ref -s --head HEAD` 80 | BEFORE_DST=`git show-ref -s --heads dst` 81 | BEFORE_SRC=`git show-ref -s --heads src` 82 | 83 | node ../../bin/sl-build --commit --onto HEAD || die 'Failed to deploy to current branch' 84 | 85 | AFTER_SRC=`git show-ref -s --heads src` 86 | AFTER_DST=`git show-ref -s --heads dst` 87 | AFTER_HEAD=`git show-ref -s --head HEAD` 88 | 89 | test "$BEFORE_HEAD" == "$AFTER_HEAD" || die 'sl-build should modify HEAD if used for deploy' 90 | test "$BEFORE_SRC" == "$AFTER_SRC" || die 'sl-build should modify branch HEAD pointed to' 91 | test "$BEFORE_DST" == "$AFTER_DST" || die 'sl-build should NOT modify other branches' 92 | -------------------------------------------------------------------------------- /test/test-build-bundle-scripts.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var build = require('./build-example'); 10 | var debug = require('debug')('strong-build:test'); 11 | var fs = require('fs'); 12 | var path = require('path'); 13 | var tar = require('tar'); 14 | var util = require('util'); 15 | 16 | build('suite', ['--install', '--pack', '--bundle', '--scripts'], function(er) { 17 | assert.ifError(er); 18 | var info = fs.readJsonSync('package.json'); 19 | var tgz = path.join('..', util.format('%s-%s.tgz', info.name, info.version)); 20 | 21 | // iconv build directory should be present 22 | 23 | tar.list(tgz, function(er, paths) { 24 | var iconvBuildPaths = paths.filter(function(file) { 25 | return file.match(/iconv\/build/); 26 | }); 27 | 28 | debug('tarfile %s contains iconv build dirs:', tgz, iconvBuildPaths); 29 | assert(iconvBuildPaths.length > 0); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/test-build-install-nodeps.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2015,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var debug = require('debug')('strong-build:test'); 10 | var find = require('shelljs').find; 11 | var test = require('shelljs').test; 12 | 13 | require('./build-example')('zero-dependency', ['-i'], function(er) { 14 | debug('built with error?', er); 15 | assert.ifError(er); 16 | if (test('-d', 'node_modules')) { 17 | assert.deepEqual(find('node_modules'), ['node_modules'], 'empty directory'); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /test/test-build-install.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var debug = require('debug')('strong-build:test'); 10 | var sh = require('shelljs'); 11 | 12 | require('./build-example')('suite', ['-i'], function(er) { 13 | debug('built with error?', er); 14 | assert.ifError(er); 15 | assert(sh.test('-d', 'node_modules')); 16 | 17 | var addons = sh.find('node_modules').filter(function(path) { 18 | // .../mongodb/node_modules/bson/ contains compiled addons. Others could in 19 | // the future, but for now, this works to assert addons weren't compiled 20 | // during the `npm install` 21 | return path.match(/.*\.node$/) && !path.match(/node_modules\/bson/); 22 | }); 23 | 24 | debug('addons:', addons); 25 | 26 | assert.equal(addons.length, 0); 27 | }); 28 | -------------------------------------------------------------------------------- /test/test-build-pack.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var debug = require('debug')('strong-build:test'); 10 | var path = require('path'); 11 | var tar = require('tar'); 12 | var test = require('shelljs').test; 13 | var util = require('util'); 14 | 15 | require('./build-example')('suite', ['--install', '-p'], function(er) { 16 | debug('built with error?', er); 17 | assert.ifError(er); 18 | assert(test('-d', 'node_modules')); 19 | var info = require(path.resolve('package.json')); 20 | assert.equal(info.name, 'loopback-example-app'); 21 | var tgz = path.join('..', util.format('%s-%s.tgz', info.name, info.version)); 22 | assert(test('-f', tgz), 'expected to find ' + tgz); 23 | 24 | tar.list(tgz, function(er, paths) { 25 | var bundled = paths.filter(function(path) { 26 | return path.match(/node_modules/); 27 | }); 28 | debug('tarfile %s bundles:', tgz, bundled); 29 | 30 | assert.notEqual(bundled.length, 0, 'bundling not requested'); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/test-build-scripts.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var debug = require('debug')('strong-build:test'); 10 | var sh = require('shelljs'); 11 | 12 | require('./build-example')('suite', ['-i', '--scripts'], function(er) { 13 | debug('built with error?', er); 14 | assert.ifError(er); 15 | assert(sh.test('-d', 'node_modules')); 16 | 17 | var addons = sh.find('node_modules').filter(function(path) { 18 | // .../mongodb/node_modules/bson/ contains compiled addons. Others could in 19 | // the future, but for now, this works to assert addons weren't compiled 20 | // during the `npm install` 21 | return path.match(/.*\.node$/) && !path.match(/node_modules\/bson/); 22 | }); 23 | 24 | debug('addons:', addons); 25 | 26 | assert(addons.length > 0); 27 | }); 28 | -------------------------------------------------------------------------------- /test/test-script-only.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var debug = require('debug')('strong-build:test'); 10 | var path = require('path'); 11 | var sh = require('shelljs'); 12 | 13 | require('./build-example')('suite', ['--scripts'], function(er) { 14 | debug('built with error?', er); 15 | assert.ifError(er); 16 | assert(sh.test('-d', 'node_modules')); 17 | var info = require(path.resolve('package.json')); 18 | assert.equal(info.name, 'loopback-example-app'); 19 | 20 | var gitLsOutput = sh.exec('git ls-tree -r --long deploy', {silent: 1}).output; 21 | var paths = gitLsOutput.split('\n'); 22 | var bundled = paths.filter(function(path) { 23 | return path.match(/node_modules/); 24 | }); 25 | debug('git branch bundles:', bundled); 26 | 27 | assert(bundled.length > 0, 'dependencies should be bundled'); 28 | 29 | var iconvBuildPaths = paths.filter(function(file) { 30 | return file.match(/iconv\/build/); 31 | }); 32 | 33 | debug('git branch contains iconv build dirs:', iconvBuildPaths); 34 | assert(iconvBuildPaths.length > 0, 'build scripts should be present'); 35 | }); 36 | -------------------------------------------------------------------------------- /test/test-usage.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2014,2016. All Rights Reserved. 2 | // Node module: strong-build 3 | // This file is licensed under the Artistic License 2.0. 4 | // License text available at https://opensource.org/licenses/Artistic-2.0 5 | 6 | 'use strict'; 7 | 8 | var assert = require('assert'); 9 | var async = require('async'); 10 | var build = require('../').build; 11 | var debug = require('debug')('strong-build:test'); 12 | 13 | // Check for node silently exiting with code 0 when tests have not passed. 14 | var ok = false; 15 | 16 | process.on('exit', function(code) { 17 | if (code === 0) { 18 | assert(ok); 19 | } 20 | }); 21 | 22 | function expectError(er) { 23 | if (er) { 24 | return null; 25 | } else { 26 | return Error('expected error'); 27 | } 28 | } 29 | 30 | // argv [0] and [1] are ignored (they are node and script name, not options) 31 | async.parallel([ 32 | build.bind(null, ['', '', '-h']), 33 | build.bind(null, ['', '', '--help']), 34 | build.bind(null, ['', '', '-hv']), 35 | build.bind(null, ['', '', '-v']), 36 | build.bind(null, ['', '', '--version']), 37 | build.bind(null, ['', '', '-vh']), 38 | function(callback) { 39 | build(['', '', 'no-such-arg'], function(er) { 40 | return callback(expectError(er)); 41 | }); 42 | }, 43 | function(callback) { 44 | build(['', '', '--no-such-option'], function(er) { 45 | return callback(expectError(er)); 46 | }); 47 | }, 48 | function(callback) { 49 | build(['', '', '-Z'], function(er) { 50 | return callback(expectError(er)); 51 | }); 52 | }, 53 | ], function(er, results) { 54 | debug('test-help: error=%s:', er, results); 55 | assert.ifError(er); 56 | ok = true; 57 | }); 58 | --------------------------------------------------------------------------------