├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bs-config.e2e.json ├── bs-config.json ├── e2e ├── app.e2e-spec.ts └── tsconfig.json ├── karma-test-shim.js ├── karma.conf.js ├── non-essential-files.osx.txt ├── non-essential-files.txt ├── package.json ├── protractor.config.js ├── src ├── app │ ├── app.component.spec.ts │ ├── app.component.ts │ └── app.module.ts ├── favicon.ico ├── index.html ├── main.ts ├── styles.css ├── systemjs-angular-loader.js ├── systemjs.config.extras.js ├── systemjs.config.js └── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | 16 | # Indentation override 17 | #[lib/**.js] 18 | #[{package.json,.travis.yml}] 19 | #[**/**.js] 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | jspm_packages 4 | npm-debug.log 5 | debug.log 6 | src/**/*.js 7 | !src/systemjs.config.extras.js 8 | !src/systemjs.config.js 9 | !src/systemjs-angular-loader.js 10 | *.js.map 11 | e2e/**/*.js 12 | e2e/**/*.js.map 13 | _test-output 14 | _temp 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | node_js: 5 | - "5" 6 | os: 7 | - linux 8 | env: 9 | global: 10 | - DBUS_SESSION_BUS_ADDRESS=/dev/null 11 | - DISPLAY=:99.0 12 | - CHROME_BIN=chromium-browser 13 | before_script: 14 | - sh -e /etc/init.d/xvfb start 15 | install: 16 | - npm install 17 | script: 18 | - npm run lint 19 | - npm run test:once 20 | - npm run e2e 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Angular Documentation QuickStart Changelog 2 | Upgraders: for a fresh start, consider running these commands 3 | * `git clean -xdf` 4 | * `npm install` 5 | 6 | 7 | # 0.4.1 (2017-03-24) 8 | * Replace systemjs-angular-loader with version that works for IE 9 | 10 | 11 | # 0.4.0 (2017-03-24) 12 | * Update to Angular 4.0.0 13 | 14 | 15 | # 0.3.0 (2017-03-22) 16 | * Remove moduleId with a systemjs loader. 17 | 18 | 19 | # 0.2.22 (2017-01-05) 20 | * Add `non-essential-files.txt` and instructions to use it to README 21 | 22 | 23 | # 0.2.21 (2016-12-14) 24 | * Update to in-memory-web-api v.0.2.1 25 | 26 | 27 | # 0.2.20 (2016-12-07) 28 | * Update to Angular 2.3.0 29 | 30 | 31 | # 0.2.19 (2016-11-30) 32 | * remove upgrade mappings from `systemjs.config.js` PR #301 33 | 34 | 35 | # 0.2.18 (2016-11-30) 36 | * remove `exclude` clause from `tsconfig.json`; it was just confusing people 37 | * karma.config + karma-test-shim can handle multiple spec source paths (issue #294) 38 | * cosmetic `app.component.spec.ts` changes 39 | * cosmetic `karma.config.js` changes 40 | 41 | 42 | # 0.2.17 (2016-11-16) 43 | * Conform to updated QuickStart advice 44 | * removed docker everywhere (was nice but not necessary) 45 | * removed wallaby 46 | * shrink styles.css 47 | * refine tsconfig.json 48 | * `AppComponent` uses interpolation 49 | 50 | 51 | # 0.2.16 (2016-11-14) 52 | * Update to Angular 2.2.0 53 | 54 | 55 | # 0.2.15 (2016-10-29) 56 | * Revert to Jasmine 2.4.1 because bug in 2.5.x (see [jasmine issue #1231](https://github.com/jasmine/jasmine/issues/1231)) 57 | 58 | 59 | # 0.2.14 (2016-10-29) 60 | * Remove bootstrap.css install 61 | * Angular v2.1.2 62 | 63 | 64 | # 0.2.13 (2016-10-20) 65 | * Protractor 4 66 | * Move from `typings` to `@types`. See `tsconfig.json` changes. 67 | * Angular v2.1.1 68 | 69 | 70 | # 0.2.12 (2016-10-06) 71 | * Angular v2.1.0 72 | 73 | 74 | # 0.2.11 (2016-10-06) 75 | * Angular v2.0.2 76 | * License is MIT 77 | * Current testing configuration 78 | * No code changes 79 | 80 | 81 | # 0.2.10 (2016-09-19) 82 | * All "Angular 2" references become just "Angular" 83 | * No code changes 84 | 85 | 86 | # 0.2.9 (2016-09-14) 87 | * Angular 2.0.0 version 88 | * Update to Typescript 2.0.2 89 | * Fix e2e test missing dir 90 | 91 | 92 | # 0.2.8 (2016-09-01) 93 | * remove @angular test libraries from system.js (now in shim) 94 | * update test related files 95 | * wallaby doesn't completely work. Researching. 96 | 97 | 98 | # 0.2.7 (2016-08-31) 99 | * Angular 2 RC6 version 100 | * Updated new forms, router, angular2-in-memory-web-api, karma, core-js, rxjs and zone.js packages 101 | * Removed router-deprecated package 102 | * Updated karma.conf.js and systemjs.config.js 103 | 104 | 105 | # 0.2.6 (2016-08-09) 106 | * Angular 2 RC5 version 107 | * Updated new forms, router and angular2-in-memory-web-api 108 | 109 | 110 | # 0.2.5 (2016-06-30) 111 | * Angular 2 RC4 version 112 | * Updated new forms and router 113 | 114 | 115 | # 0.2.4 (2016-06-21) 116 | * Angular 2 RC3 version 117 | * Add new forms and router 118 | * Add support for TS e2e tests 119 | 120 | 121 | # 0.2.3 (2016-06-15) 122 | * Angular 2 RC2 version 123 | 124 | 125 | # 0.2.2 (2016-05-21) 126 | * Update to Typings 1.x 127 | 128 | 129 | # 0.2.1 (2016-05-03) 130 | * Angular 2 RC01 version 131 | 132 | 133 | # 0.2.0 (2016-05-02) 134 | * Angular 2 RC0 version 135 | 136 | 137 | # 0.1.17 (2016-04-29) 138 | * update packages 139 | * Angular 2 beta 17 140 | * RxJS 5.0.0-beta.6 141 | * a2-in-memory-web-api 0.1.17 142 | 143 | 144 | # 0.1.16 (2016-04-26) 145 | * update packages 146 | * Angular 2 beta 16 147 | * a2-in-memory-web-api 0.1.6 148 | * protractor 3.3.0 149 | * typings 0.8.1 150 | * zone.js 0.6.12 151 | 152 | * added favicon.ico 153 | 154 | * testing 155 | - updated wallaby.js and karma.conf.js 156 | - updated app.component.spec.ts 157 | 158 | 159 | 160 | # 0.1.15 (2016-04-13) 161 | * Add testing support 162 | * npm scripts 163 | * karma/jasmine 164 | * protractor 165 | 166 | * update packages 167 | * Angular 2 beta 15 168 | * lite-server 2.2.0 169 | * systemjs 0.19.26 170 | * typescript 1.8.10 171 | * typings 0.7.12 172 | 173 | * add run packages 174 | * a2-in-memory-web-api 175 | 176 | * add testing dev-dependency packages 177 | * canonical-path: 0.0.2, 178 | * http-server: ^0.9.0, 179 | * jasmine-core: ~2.4.1, 180 | * karma: ^0.13.22, 181 | * karma-chrome-launcher: ^0.2.3, 182 | * karma-cli: ^0.1.2, 183 | * karma-htmlfile-reporter: ^0.2.2, 184 | * karma-jasmine: ^0.3.8, 185 | * protractor: ^3.2.2, 186 | * rimraf: ^2.5.2 187 | 188 | 189 | # 0.1.14 (2016-04-07) 190 | * update packages 191 | * Angular 2 beta 14 192 | * lite-server 2.2.0 193 | * typings 0.7.12 194 | 195 | 196 | # 0.1.13 (2016-03-31) 197 | * update packages 198 | * Angular 2 beta 13 199 | 200 | 201 | # 0.1.12 (2016-03-23) 202 | * update packages 203 | * Angular 2 beta 12 204 | * zones 0.6.6 205 | * remove es6-promise because no longer needed. 206 | 207 | 208 | # 0.1.11 (2016-03-18) 209 | * update packages 210 | * Angular 2 beta 11 211 | * zones 0.6.4 212 | * typescript 1.8.9 213 | * typings 0.7.9 214 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2016 Google, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular QuickStart Source 2 | [![Build Status][travis-badge]][travis-badge-url] 3 | 4 | **This repository is now deprecated. The Angular Quickstart project was a nice starting point for creating Angular applications. Now we recommend using the [Angular CLI](https://github.com/angular/angular-cli) to create new Angular projects.** 5 | 6 | **Starting from 1 November 2017, all the Angular documentation, at [angular.io](https://angular.io), is based on the Angular CLI.** 7 | 8 | **Let's [get started](https://angular.io/guide/quickstart)** 9 | 10 | --- 11 | 12 | This repository holds the TypeScript source code of the [angular.io quickstart](https://angular.io/docs/ts/latest/quickstart.html), 13 | the foundation for most of the documentation samples and potentially a good starting point for your application. 14 | 15 | It's been extended with testing support so you can start writing tests immediately. 16 | 17 | **This is not the perfect arrangement for your application. It is not designed for production. 18 | It exists primarily to get you started quickly with learning and prototyping in Angular** 19 | 20 | We are unlikely to accept suggestions about how to grow this QuickStart into something it is not. 21 | Please keep that in mind before posting issues and PRs. 22 | 23 | ## Updating to a newer version of the Quickstart Repo 24 | 25 | From time to time the QuickStart will be enhanced with support for new features or to reflect 26 | changes to the [official Style Guide](https://angular.io/docs/ts/latest/guide/style-guide.html). 27 | 28 | You can update your existing project to an up-to-date QuickStart by following these instructions: 29 | - Create a new project using the [instructions below](#create-a-new-project-based-on-the-quickstart) 30 | - Copy the code you have in your project's `main.ts` file onto `src/app/main.ts` in the new project 31 | - Copy your old `app` folder into `src/app` 32 | - Delete `src/app/main.ts` if you have one (we now use `src/main.ts` instead) 33 | - Copy your old `index.html`, `styles.css` and `tsconfig.json` into `src/` 34 | - Install all your third party dependencies 35 | - Copy your old `e2e/` folder into `e2e/` 36 | - Copy over any other files you added to your project 37 | - Copy your old `.git` folder into your new project's root 38 | 39 | Now you can continue working on the new project. 40 | 41 | ## Prerequisites 42 | 43 | Node.js and npm are essential to Angular development. 44 | 45 | 46 | Get it now if it's not already installed on your machine. 47 | 48 | **Verify that you are running at least node `v4.x.x` and npm `3.x.x`** 49 | by running `node -v` and `npm -v` in a terminal/console window. 50 | Older versions produce errors. 51 | 52 | We recommend [nvm](https://github.com/creationix/nvm) for managing multiple versions of node and npm. 53 | 54 | ## Create a new project based on the QuickStart 55 | 56 | Clone this repo into new project folder (e.g., `my-proj`). 57 | ```shell 58 | git clone https://github.com/angular/quickstart my-proj 59 | cd my-proj 60 | ``` 61 | 62 | We have no intention of updating the source on `angular/quickstart`. 63 | Discard the `.git` folder.. 64 | ```shell 65 | rm -rf .git # OS/X (bash) 66 | rd .git /S/Q # windows 67 | ``` 68 | ### Delete _non-essential_ files (optional) 69 | 70 | You can quickly delete the _non-essential_ files that concern testing and QuickStart repository maintenance 71 | (***including all git-related artifacts*** such as the `.git` folder and `.gitignore`!) 72 | by entering the following commands while in the project folder: 73 | 74 | ##### OS/X (bash) 75 | ```shell 76 | xargs rm -rf < non-essential-files.osx.txt 77 | rm src/app/*.spec*.ts 78 | rm non-essential-files.osx.txt 79 | ``` 80 | 81 | ##### Windows 82 | ```shell 83 | for /f %i in (non-essential-files.txt) do del %i /F /S /Q 84 | rd .git /s /q 85 | rd e2e /s /q 86 | ``` 87 | 88 | ### Create a new git repo 89 | You could [start writing code](#start-development) now and throw it all away when you're done. 90 | If you'd rather preserve your work under source control, consider taking the following steps. 91 | 92 | Initialize this project as a *local git repo* and make the first commit: 93 | ```shell 94 | git init 95 | git add . 96 | git commit -m "Initial commit" 97 | ``` 98 | 99 | >Recover the deleted `.gitignore` from the QuickStart repository 100 | if you lost it in the _Delete non-essential files_ step. 101 | 102 | Create a *remote repository* for this project on the service of your choice. 103 | 104 | Grab its address (e.g. *`https://github.com//my-proj.git`*) and push the *local repo* to the *remote*. 105 | ```shell 106 | git remote add origin 107 | git push -u origin master 108 | ``` 109 | ## Install npm packages 110 | 111 | > See npm and nvm version notes above 112 | 113 | Install the npm packages described in the `package.json` and verify that it works: 114 | 115 | ```shell 116 | npm install 117 | npm start 118 | ``` 119 | 120 | >Doesn't work in _Bash for Windows_ which does not support servers as of January, 2017. 121 | 122 | The `npm start` command first compiles the application, 123 | then simultaneously re-compiles and runs the `lite-server`. 124 | Both the compiler and the server watch for file changes. 125 | 126 | Shut it down manually with `Ctrl-C`. 127 | 128 | You're ready to write your application. 129 | 130 | ### npm scripts 131 | 132 | We've captured many of the most useful commands in npm scripts defined in the `package.json`: 133 | 134 | * `npm start` - runs the compiler and a server at the same time, both in "watch mode". 135 | * `npm run build` - runs the TypeScript compiler once. 136 | * `npm run build:w` - runs the TypeScript compiler in watch mode; the process keeps running, awaiting changes to TypeScript files and re-compiling when it sees them. 137 | * `npm run serve` - runs the [lite-server](https://www.npmjs.com/package/lite-server), a light-weight, static file server, written and maintained by 138 | [John Papa](https://github.com/johnpapa) and 139 | [Christopher Martin](https://github.com/cgmartin) 140 | with excellent support for Angular apps that use routing. 141 | 142 | Here are the test related scripts: 143 | * `npm test` - compiles, runs and watches the karma unit tests 144 | * `npm run e2e` - compiles and run protractor e2e tests, written in Typescript (*e2e-spec.ts) 145 | 146 | ## Testing 147 | 148 | The QuickStart documentation doesn't discuss testing. 149 | This repo adds both karma/jasmine unit test and protractor end-to-end testing support. 150 | 151 | These tools are configured for specific conventions described below. 152 | 153 | *It is unwise and rarely possible to run the application, the unit tests, and the e2e tests at the same time. 154 | We recommend that you shut down one before starting another.* 155 | 156 | ### Unit Tests 157 | TypeScript unit-tests are usually in the `src/app` folder. Their filenames must end in `.spec.ts`. 158 | 159 | Look for the example `src/app/app.component.spec.ts`. 160 | Add more `.spec.ts` files as you wish; we configured karma to find them. 161 | 162 | Run it with `npm test` 163 | 164 | That command first compiles the application, then simultaneously re-compiles and runs the karma test-runner. 165 | Both the compiler and the karma watch for (different) file changes. 166 | 167 | Shut it down manually with `Ctrl-C`. 168 | 169 | Test-runner output appears in the terminal window. 170 | We can update our app and our tests in real-time, keeping a weather eye on the console for broken tests. 171 | Karma is occasionally confused and it is often necessary to shut down its browser or even shut the command down (`Ctrl-C`) and 172 | restart it. No worries; it's pretty quick. 173 | 174 | ### End-to-end (E2E) Tests 175 | 176 | E2E tests are in the `e2e` directory, side by side with the `src` folder. 177 | Their filenames must end in `.e2e-spec.ts`. 178 | 179 | Look for the example `e2e/app.e2e-spec.ts`. 180 | Add more `.e2e-spec.js` files as you wish (although one usually suffices for small projects); 181 | we configured Protractor to find them. 182 | 183 | Thereafter, run them with `npm run e2e`. 184 | 185 | That command first compiles, then simultaneously starts the `lite-server` at `localhost:8080` 186 | and launches Protractor. 187 | 188 | The pass/fail test results appear at the bottom of the terminal window. 189 | A custom reporter (see `protractor.config.js`) generates a `./_test-output/protractor-results.txt` file 190 | which is easier to read; this file is excluded from source control. 191 | 192 | Shut it down manually with `Ctrl-C`. 193 | 194 | [travis-badge]: https://travis-ci.org/angular/quickstart.svg?branch=master 195 | [travis-badge-url]: https://travis-ci.org/angular/quickstart 196 | -------------------------------------------------------------------------------- /bs-config.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "open": false, 3 | "logLevel": "silent", 4 | "port": 8080, 5 | "server": { 6 | "baseDir": "src", 7 | "routes": { 8 | "/node_modules": "node_modules" 9 | }, 10 | "middleware": { 11 | "0": null 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "baseDir": "src", 4 | "routes": { 5 | "/node_modules": "node_modules" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | describe('QuickStart E2E Tests', function () { 4 | 5 | let expectedMsg = 'Hello Angular'; 6 | 7 | beforeEach(function () { 8 | browser.get(''); 9 | }); 10 | 11 | it('should display: ' + expectedMsg, function () { 12 | expect(element(by.css('h1')).getText()).toEqual(expectedMsg); 13 | }); 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // /*global jasmine, __karma__, window*/ 2 | Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing. 3 | 4 | // Uncomment to get full stacktrace output. Sometimes helpful, usually not. 5 | // Error.stackTraceLimit = Infinity; // 6 | 7 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 8 | 9 | // builtPaths: root paths for output ("built") files 10 | // get from karma.config.js, then prefix with '/base/' (default is 'src/') 11 | var builtPaths = (__karma__.config.builtPaths || ['src/']) 12 | .map(function(p) { return '/base/'+p;}); 13 | 14 | __karma__.loaded = function () { }; 15 | 16 | function isJsFile(path) { 17 | return path.slice(-3) == '.js'; 18 | } 19 | 20 | function isSpecFile(path) { 21 | return /\.spec\.(.*\.)?js$/.test(path); 22 | } 23 | 24 | // Is a "built" file if is JavaScript file in one of the "built" folders 25 | function isBuiltFile(path) { 26 | return isJsFile(path) && 27 | builtPaths.reduce(function(keep, bp) { 28 | return keep || (path.substr(0, bp.length) === bp); 29 | }, false); 30 | } 31 | 32 | var allSpecFiles = Object.keys(window.__karma__.files) 33 | .filter(isSpecFile) 34 | .filter(isBuiltFile); 35 | 36 | System.config({ 37 | // Base URL for System.js calls. 'base/' is where Karma serves files from. 38 | baseURL: 'base/src', 39 | // Extend usual application package list with test folder 40 | packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } }, 41 | 42 | // Assume npm: is set in `paths` in systemjs.config 43 | // Map the angular testing umd bundles 44 | map: { 45 | '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', 46 | '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', 47 | '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js', 48 | '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js', 49 | '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', 50 | '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js', 51 | '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js', 52 | '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js', 53 | }, 54 | }); 55 | 56 | System.import('systemjs.config.js') 57 | .then(importSystemJsExtras) 58 | .then(initTestBed) 59 | .then(initTesting); 60 | 61 | /** Optional SystemJS configuration extras. Keep going w/o it */ 62 | function importSystemJsExtras(){ 63 | return System.import('systemjs.config.extras.js') 64 | .catch(function(reason) { 65 | console.log( 66 | 'Warning: System.import could not load the optional "systemjs.config.extras.js". Did you omit it by accident? Continuing without it.' 67 | ); 68 | console.log(reason); 69 | }); 70 | } 71 | 72 | function initTestBed(){ 73 | return Promise.all([ 74 | System.import('@angular/core/testing'), 75 | System.import('@angular/platform-browser-dynamic/testing') 76 | ]) 77 | 78 | .then(function (providers) { 79 | var coreTesting = providers[0]; 80 | var browserTesting = providers[1]; 81 | 82 | coreTesting.TestBed.initTestEnvironment( 83 | browserTesting.BrowserDynamicTestingModule, 84 | browserTesting.platformBrowserDynamicTesting()); 85 | }) 86 | } 87 | 88 | // Import all spec files and start karma 89 | function initTesting () { 90 | return Promise.all( 91 | allSpecFiles.map(function (moduleName) { 92 | return System.import(moduleName); 93 | }) 94 | ) 95 | .then(__karma__.start, __karma__.error); 96 | } 97 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | 3 | var appBase = 'src/'; // transpiled app JS and map files 4 | var appSrcBase = appBase; // app source TS files 5 | 6 | // Testing helpers (optional) are conventionally in a folder called `testing` 7 | var testingBase = 'testing/'; // transpiled test JS and map files 8 | var testingSrcBase = 'testing/'; // test source TS files 9 | 10 | config.set({ 11 | basePath: '', 12 | frameworks: ['jasmine'], 13 | 14 | plugins: [ 15 | require('karma-jasmine'), 16 | require('karma-chrome-launcher'), 17 | require('karma-jasmine-html-reporter') 18 | ], 19 | 20 | client: { 21 | builtPaths: [appBase, testingBase], // add more spec base paths as needed 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | 25 | customLaunchers: { 26 | // From the CLI. Not used here but interesting 27 | // chrome setup for travis CI using chromium 28 | Chrome_travis_ci: { 29 | base: 'Chrome', 30 | flags: ['--no-sandbox'] 31 | } 32 | }, 33 | 34 | files: [ 35 | // System.js for module loading 36 | 'node_modules/systemjs/dist/system.src.js', 37 | 38 | // Polyfills 39 | 'node_modules/core-js/client/shim.js', 40 | 41 | // zone.js 42 | 'node_modules/zone.js/dist/zone.js', 43 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 44 | 'node_modules/zone.js/dist/proxy.js', 45 | 'node_modules/zone.js/dist/sync-test.js', 46 | 'node_modules/zone.js/dist/jasmine-patch.js', 47 | 'node_modules/zone.js/dist/async-test.js', 48 | 'node_modules/zone.js/dist/fake-async-test.js', 49 | 50 | // RxJs 51 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 52 | { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, 53 | 54 | // Paths loaded via module imports: 55 | // Angular itself 56 | { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false }, 57 | { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false }, 58 | 59 | { pattern: appBase + '/systemjs.config.js', included: false, watched: false }, 60 | { pattern: appBase + '/systemjs.config.extras.js', included: false, watched: false }, 61 | 'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels 62 | 63 | // transpiled application & spec code paths loaded via module imports 64 | { pattern: appBase + '**/*.js', included: false, watched: true }, 65 | { pattern: testingBase + '**/*.js', included: false, watched: true }, 66 | 67 | 68 | // Asset (HTML & CSS) paths loaded via Angular's component compiler 69 | // (these paths need to be rewritten, see proxies section) 70 | { pattern: appBase + '**/*.html', included: false, watched: true }, 71 | { pattern: appBase + '**/*.css', included: false, watched: true }, 72 | 73 | // Paths for debugging with source maps in dev tools 74 | { pattern: appBase + '**/*.ts', included: false, watched: false }, 75 | { pattern: appBase + '**/*.js.map', included: false, watched: false }, 76 | { pattern: testingSrcBase + '**/*.ts', included: false, watched: false }, 77 | { pattern: testingBase + '**/*.js.map', included: false, watched: false} 78 | ], 79 | 80 | // Proxied base paths for loading assets 81 | proxies: { 82 | // required for modules fetched by SystemJS 83 | '/base/src/node_modules/': '/base/node_modules/' 84 | }, 85 | 86 | exclude: [], 87 | preprocessors: {}, 88 | reporters: ['progress', 'kjhtml'], 89 | 90 | port: 9876, 91 | colors: true, 92 | logLevel: config.LOG_INFO, 93 | autoWatch: true, 94 | browsers: ['Chrome'], 95 | singleRun: false 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /non-essential-files.osx.txt: -------------------------------------------------------------------------------- 1 | .git .gitignore .travis.yml bs-config.e2e.json CHANGELOG.md e2e favicon.ico karma.conf.js karma-test-shim.js LICENSE non-essential-files.txt protractor.config.js README.md -------------------------------------------------------------------------------- /non-essential-files.txt: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .travis.yml 4 | *.spec*.ts 5 | bs-config.e2e.json 6 | CHANGELOG.md 7 | e2e 8 | favicon.ico 9 | karma.conf.js 10 | karma-test-shim.js 11 | LICENSE 12 | non-essential-files.txt 13 | non-essential-files.osx.txt 14 | protractor.config.js 15 | README.md 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-quickstart", 3 | "version": "1.0.0", 4 | "description": "QuickStart package.json from the documentation, supplemented with testing support", 5 | "scripts": { 6 | "build": "tsc -p src/", 7 | "build:watch": "tsc -p src/ -w", 8 | "build:e2e": "tsc -p e2e/", 9 | "serve": "lite-server -c=bs-config.json", 10 | "serve:e2e": "lite-server -c=bs-config.e2e.json", 11 | "prestart": "npm run build", 12 | "start": "concurrently \"npm run build:watch\" \"npm run serve\"", 13 | "pree2e": "npm run build:e2e", 14 | "e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first", 15 | "preprotractor": "webdriver-manager update", 16 | "protractor": "protractor protractor.config.js", 17 | "pretest": "npm run build", 18 | "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"", 19 | "pretest:once": "npm run build", 20 | "test:once": "karma start karma.conf.js --single-run", 21 | "lint": "tslint ./src/**/*.ts -t verbose" 22 | }, 23 | "keywords": [], 24 | "author": "", 25 | "license": "MIT", 26 | "dependencies": { 27 | "@angular/common": "~4.3.4", 28 | "@angular/compiler": "~4.3.4", 29 | "@angular/core": "~4.3.4", 30 | "@angular/forms": "~4.3.4", 31 | "@angular/http": "~4.3.4", 32 | "@angular/platform-browser": "~4.3.4", 33 | "@angular/platform-browser-dynamic": "~4.3.4", 34 | "@angular/router": "~4.3.4", 35 | 36 | "angular-in-memory-web-api": "~0.3.0", 37 | "systemjs": "0.19.40", 38 | "core-js": "^2.4.1", 39 | "rxjs": "5.0.1", 40 | "zone.js": "^0.8.4" 41 | }, 42 | "devDependencies": { 43 | "concurrently": "^3.2.0", 44 | "lite-server": "^2.2.2", 45 | "typescript": "~2.1.0", 46 | 47 | "canonical-path": "0.0.2", 48 | "tslint": "^3.15.1", 49 | "lodash": "^4.16.4", 50 | "jasmine-core": "~2.4.1", 51 | "karma": "^1.3.0", 52 | "karma-chrome-launcher": "^2.0.0", 53 | "karma-cli": "^1.0.1", 54 | "karma-jasmine": "^1.0.2", 55 | "karma-jasmine-html-reporter": "^0.2.2", 56 | "protractor": "~4.0.14", 57 | "rimraf": "^2.5.4", 58 | 59 | "@types/node": "^6.0.46", 60 | "@types/jasmine": "2.5.36" 61 | }, 62 | "repository": {} 63 | } 64 | -------------------------------------------------------------------------------- /protractor.config.js: -------------------------------------------------------------------------------- 1 | // FIRST TIME ONLY- run: 2 | // ./node_modules/.bin/webdriver-manager update 3 | // 4 | // Try: `npm run webdriver:update` 5 | // 6 | // AND THEN EVERYTIME ... 7 | // 1. Compile with `tsc` 8 | // 2. Make sure the test server (e.g., lite-server: localhost:8080) is running. 9 | // 3. ./node_modules/.bin/protractor protractor.config.js 10 | // 11 | // To do all steps, try: `npm run e2e` 12 | 13 | var fs = require('fs'); 14 | var path = require('canonical-path'); 15 | var _ = require('lodash'); 16 | 17 | 18 | exports.config = { 19 | directConnect: true, 20 | 21 | // Capabilities to be passed to the webdriver instance. 22 | capabilities: { 23 | 'browserName': 'chrome' 24 | }, 25 | 26 | // Framework to use. Jasmine is recommended. 27 | framework: 'jasmine', 28 | 29 | // Spec patterns are relative to this config file 30 | specs: ['**/*e2e-spec.js' ], 31 | 32 | 33 | // For angular tests 34 | useAllAngular2AppRoots: true, 35 | 36 | // Base URL for application server 37 | baseUrl: 'http://localhost:8080', 38 | 39 | // doesn't seem to work. 40 | // resultJsonOutputFile: "foo.json", 41 | 42 | onPrepare: function() { 43 | //// SpecReporter 44 | //var SpecReporter = require('jasmine-spec-reporter'); 45 | //jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'none'})); 46 | //// jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'all'})); 47 | 48 | // debugging 49 | // console.log('browser.params:' + JSON.stringify(browser.params)); 50 | jasmine.getEnv().addReporter(new Reporter( browser.params )) ; 51 | 52 | // Allow changing bootstrap mode to NG1 for upgrade tests 53 | global.setProtractorToNg1Mode = function() { 54 | browser.useAllAngular2AppRoots = false; 55 | browser.rootEl = 'body'; 56 | }; 57 | }, 58 | 59 | jasmineNodeOpts: { 60 | // defaultTimeoutInterval: 60000, 61 | defaultTimeoutInterval: 10000, 62 | showTiming: true, 63 | print: function() {} 64 | } 65 | }; 66 | 67 | // Custom reporter 68 | function Reporter(options) { 69 | var _defaultOutputFile = path.resolve(process.cwd(), './_test-output', 'protractor-results.txt'); 70 | options.outputFile = options.outputFile || _defaultOutputFile; 71 | 72 | initOutputFile(options.outputFile); 73 | options.appDir = options.appDir || './'; 74 | var _root = { appDir: options.appDir, suites: [] }; 75 | log('AppDir: ' + options.appDir, +1); 76 | var _currentSuite; 77 | 78 | this.suiteStarted = function(suite) { 79 | _currentSuite = { description: suite.description, status: null, specs: [] }; 80 | _root.suites.push(_currentSuite); 81 | log('Suite: ' + suite.description, +1); 82 | }; 83 | 84 | this.suiteDone = function(suite) { 85 | var statuses = _currentSuite.specs.map(function(spec) { 86 | return spec.status; 87 | }); 88 | statuses = _.uniq(statuses); 89 | var status = statuses.indexOf('failed') >= 0 ? 'failed' : statuses.join(', '); 90 | _currentSuite.status = status; 91 | log('Suite ' + _currentSuite.status + ': ' + suite.description, -1); 92 | }; 93 | 94 | this.specStarted = function(spec) { 95 | 96 | }; 97 | 98 | this.specDone = function(spec) { 99 | var currentSpec = { 100 | description: spec.description, 101 | status: spec.status 102 | }; 103 | if (spec.failedExpectations.length > 0) { 104 | currentSpec.failedExpectations = spec.failedExpectations; 105 | } 106 | 107 | _currentSuite.specs.push(currentSpec); 108 | log(spec.status + ' - ' + spec.description); 109 | }; 110 | 111 | this.jasmineDone = function() { 112 | outputFile = options.outputFile; 113 | //// Alternate approach - just stringify the _root - not as pretty 114 | //// but might be more useful for automation. 115 | // var output = JSON.stringify(_root, null, 2); 116 | var output = formatOutput(_root); 117 | fs.appendFileSync(outputFile, output); 118 | }; 119 | 120 | function ensureDirectoryExistence(filePath) { 121 | var dirname = path.dirname(filePath); 122 | if (directoryExists(dirname)) { 123 | return true; 124 | } 125 | ensureDirectoryExistence(dirname); 126 | fs.mkdirSync(dirname); 127 | } 128 | 129 | function directoryExists(path) { 130 | try { 131 | return fs.statSync(path).isDirectory(); 132 | } 133 | catch (err) { 134 | return false; 135 | } 136 | } 137 | 138 | function initOutputFile(outputFile) { 139 | ensureDirectoryExistence(outputFile); 140 | var header = "Protractor results for: " + (new Date()).toLocaleString() + "\n\n"; 141 | fs.writeFileSync(outputFile, header); 142 | } 143 | 144 | // for output file output 145 | function formatOutput(output) { 146 | var indent = ' '; 147 | var pad = ' '; 148 | var results = []; 149 | results.push('AppDir:' + output.appDir); 150 | output.suites.forEach(function(suite) { 151 | results.push(pad + 'Suite: ' + suite.description + ' -- ' + suite.status); 152 | pad+=indent; 153 | suite.specs.forEach(function(spec) { 154 | results.push(pad + spec.status + ' - ' + spec.description); 155 | if (spec.failedExpectations) { 156 | pad+=indent; 157 | spec.failedExpectations.forEach(function (fe) { 158 | results.push(pad + 'message: ' + fe.message); 159 | }); 160 | pad=pad.substr(2); 161 | } 162 | }); 163 | pad = pad.substr(2); 164 | results.push(''); 165 | }); 166 | results.push(''); 167 | return results.join('\n'); 168 | } 169 | 170 | // for console output 171 | var _pad; 172 | function log(str, indent) { 173 | _pad = _pad || ''; 174 | if (indent == -1) { 175 | _pad = _pad.substr(2); 176 | } 177 | console.log(_pad + str); 178 | if (indent == 1) { 179 | _pad = _pad + ' '; 180 | } 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { AppComponent } from './app.component'; 2 | 3 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 4 | import { By } from '@angular/platform-browser'; 5 | import { DebugElement } from '@angular/core'; 6 | 7 | describe('AppComponent', function () { 8 | let de: DebugElement; 9 | let comp: AppComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ AppComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(AppComponent); 21 | comp = fixture.componentInstance; 22 | de = fixture.debugElement.query(By.css('h1')); 23 | }); 24 | 25 | it('should create component', () => expect(comp).toBeDefined() ); 26 | 27 | it('should have expected

text', () => { 28 | fixture.detectChanges(); 29 | const h1 = de.nativeElement; 30 | expect(h1.innerText).toMatch(/angular/i, 31 | '

should say something about "Angular"'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'my-app', 5 | template: `

Hello {{name}}

`, 6 | }) 7 | export class AppComponent { name = 'Angular'; } 8 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | @NgModule({ 7 | imports: [ BrowserModule ], 8 | declarations: [ AppComponent ], 9 | bootstrap: [ AppComponent ] 10 | }) 11 | export class AppModule { } 12 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/angular/quickstart/abf848628cf02fd1899ccd7b09eb7b3ffa78aa38/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Angular QuickStart 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | Loading AppComponent content here ... 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | platformBrowserDynamic().bootstrapModule(AppModule); 6 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: #369; 3 | font-family: Arial, Helvetica, sans-serif; 4 | font-size: 250%; 5 | } 6 | -------------------------------------------------------------------------------- /src/systemjs-angular-loader.js: -------------------------------------------------------------------------------- 1 | var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm; 2 | var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g; 3 | var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g; 4 | 5 | module.exports.translate = function(load){ 6 | if (load.source.indexOf('moduleId') != -1) return load; 7 | 8 | var url = document.createElement('a'); 9 | url.href = load.address; 10 | 11 | var basePathParts = url.pathname.split('/'); 12 | 13 | basePathParts.pop(); 14 | var basePath = basePathParts.join('/'); 15 | 16 | var baseHref = document.createElement('a'); 17 | baseHref.href = this.baseURL; 18 | baseHref = baseHref.pathname; 19 | 20 | if (!baseHref.startsWith('/base/')) { // it is not karma 21 | basePath = basePath.replace(baseHref, ''); 22 | } 23 | 24 | load.source = load.source 25 | .replace(templateUrlRegex, function(match, quote, url){ 26 | var resolvedUrl = url; 27 | 28 | if (url.startsWith('.')) { 29 | resolvedUrl = basePath + url.substr(1); 30 | } 31 | 32 | return 'templateUrl: "' + resolvedUrl + '"'; 33 | }) 34 | .replace(stylesRegex, function(match, relativeUrls) { 35 | var urls = []; 36 | 37 | while ((match = stringRegex.exec(relativeUrls)) !== null) { 38 | if (match[2].startsWith('.')) { 39 | urls.push('"' + basePath + match[2].substr(1) + '"'); 40 | } else { 41 | urls.push('"' + match[2] + '"'); 42 | } 43 | } 44 | 45 | return "styleUrls: [" + urls.join(', ') + "]"; 46 | }); 47 | 48 | return load; 49 | }; 50 | -------------------------------------------------------------------------------- /src/systemjs.config.extras.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Add barrels and stuff 3 | * Adjust as necessary for your application needs. 4 | */ 5 | // (function (global) { 6 | // System.config({ 7 | // packages: { 8 | // // add packages here 9 | // } 10 | // }); 11 | // })(this); 12 | -------------------------------------------------------------------------------- /src/systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular samples 3 | * Adjust as necessary for your application needs. 4 | */ 5 | (function (global) { 6 | System.config({ 7 | paths: { 8 | // paths serve as alias 9 | 'npm:': 'node_modules/' 10 | }, 11 | // map tells the System loader where to look for things 12 | map: { 13 | // our app is within the app folder 14 | 'app': 'app', 15 | 16 | // angular bundles 17 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/http': 'npm:@angular/http/bundles/http.umd.js', 23 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 24 | '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 25 | 26 | // other libraries 27 | 'rxjs': 'npm:rxjs', 28 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js' 29 | }, 30 | // packages tells the System loader how to load when no filename and/or no extension 31 | packages: { 32 | app: { 33 | defaultExtension: 'js', 34 | meta: { 35 | './*.js': { 36 | loader: 'systemjs-angular-loader.js' 37 | } 38 | } 39 | }, 40 | rxjs: { 41 | defaultExtension: 'js' 42 | } 43 | } 44 | }); 45 | })(this); 46 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | --------------------------------------------------------------------------------