├── tests ├── unit │ └── all.ts ├── intern-local.ts ├── support │ ├── util.ts │ └── Reporter.ts └── intern.ts ├── src ├── ex8-modules.ts ├── ex5-unionAndExtension.ts ├── ex1-typing.ts ├── ex3-functiontypes.ts ├── ex4-arraytypes.ts ├── ex6-generics.ts ├── ex2-interfaces.ts └── ex7-classes.ts ├── .gitignore ├── .editorconfig ├── Gruntfile.js ├── .gitattributes ├── tsconfig.json ├── typings.json ├── package.json ├── LICENSE ├── README.md └── tslint.json /tests/unit/all.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by pshannon on 8/10/15. 3 | */ 4 | 5 | -------------------------------------------------------------------------------- /src/ex8-modules.ts: -------------------------------------------------------------------------------- 1 | import Animal, { Phylum, Dog } from './ex7-classes'; 2 | 3 | class Cat extends Animal { 4 | 5 | } 6 | 7 | const myPet = new Dog('Barky'); 8 | -------------------------------------------------------------------------------- /src/ex5-unionAndExtension.ts: -------------------------------------------------------------------------------- 1 | import { Drawable } from './ex2-interfaces'; 2 | 3 | let id: number | string; 4 | 5 | interface Rectangle extends Drawable { 6 | width: number; 7 | height: number; 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.js.map 3 | !/*.js 4 | !/tasks/*.js 5 | /_build 6 | /bower_components 7 | /dist 8 | /html-report 9 | /node_modules 10 | /typings 11 | .baseDir.ts 12 | .tscache 13 | coverage-unmapped.json 14 | npm-debug.log 15 | -------------------------------------------------------------------------------- /src/ex1-typing.ts: -------------------------------------------------------------------------------- 1 | import { Drawable } from './ex2-interfaces'; 2 | 3 | enum Color { Red, Green, Blue } 4 | 5 | let check = true; 6 | 7 | const defaultColor: Color = Color.Red; 8 | 9 | function draw(target: Node, ...items: Drawable[]): void { } 10 | -------------------------------------------------------------------------------- /tests/intern-local.ts: -------------------------------------------------------------------------------- 1 | export * from './intern'; 2 | 3 | export var tunnel = 'NullTunnel'; 4 | export var tunnelOptions = { 5 | hostname: 'localhost', 6 | port: '4444' 7 | }; 8 | 9 | export var environments = [ 10 | { browserName: 'chrome' } 11 | ]; 12 | -------------------------------------------------------------------------------- /src/ex3-functiontypes.ts: -------------------------------------------------------------------------------- 1 | interface DrawMethod { 2 | (target: Node): void; 3 | (target: number, options?: Object): void; 4 | } 5 | 6 | let method: DrawMethod = function(target: any) { }; 7 | 8 | let anotherMethod: DrawMethod = function(target: any, options?: Object) { }; 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | require('grunt-dojo2').initConfig(grunt); 4 | grunt.registerTask('dev', [ 5 | 'typings', 6 | 'tslint', 7 | 'clean:dev', 8 | 'ts:dev', 9 | 'copy:staticTestFiles', 10 | 'updateTsconfig' 11 | ]); 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /src/ex4-arraytypes.ts: -------------------------------------------------------------------------------- 1 | interface MyMap { 2 | [index: string]: string; 3 | } 4 | 5 | let translations: MyMap = { 6 | ok: 'Alright' 7 | }; 8 | 9 | // XXX This does not work because all pairs must match 10 | let anotherMap: MyMap = { 11 | '0': 'first' 12 | // length: 1 13 | }; 14 | -------------------------------------------------------------------------------- /src/ex6-generics.ts: -------------------------------------------------------------------------------- 1 | function combinor(...rest: T[]): T { 2 | return rest.reduce((previous: T, current: T) => { 3 | return previous + current; 4 | }, rest[0] || null); 5 | } 6 | 7 | const strs = combinor('one', 'two'); 8 | 9 | const nums = combinor(1, 2); 10 | -------------------------------------------------------------------------------- /src/ex2-interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface Drawable { 2 | id: number; 3 | name?: string; 4 | draw: (target: Node) => void; 5 | } 6 | 7 | class Rect implements Drawable { 8 | id: number; 9 | draw(target: Node) {} 10 | } 11 | 12 | const thing: Drawable = { 13 | id: 0, 14 | draw: function (target: Node) { } 15 | }; 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case users don't have core.autocrlf set 2 | * text=auto 3 | 4 | # Files that should always be normalized and converted to native line 5 | # endings on checkout. 6 | *.js text 7 | *.json text 8 | *.ts text 9 | *.md text 10 | *.yml text 11 | LICENSE text 12 | 13 | # Files that are truly binary and should not be modified 14 | *.png binary 15 | *.jpg binary 16 | *.jpeg binary 17 | *.gif binary 18 | *.jar binary 19 | *.zip binary 20 | *.psd binary 21 | -------------------------------------------------------------------------------- /src/ex7-classes.ts: -------------------------------------------------------------------------------- 1 | export enum Phylum { Chordata } 2 | 3 | interface Loyalty { 4 | beLoyal(): any; 5 | } 6 | 7 | export default class Animal { 8 | private _type: Phylum; 9 | 10 | get type() { return this._type; } 11 | 12 | constructor(type: Phylum) { this._type = type; } 13 | } 14 | 15 | export class Dog extends Animal implements Loyalty { 16 | private _name: string; 17 | 18 | constructor(name: string) { 19 | super(Phylum.Chordata); 20 | this._name = name; 21 | } 22 | 23 | beLoyal() { } 24 | } 25 | -------------------------------------------------------------------------------- /tests/support/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Thenable represents any object with a callable `then` property. 3 | */ 4 | export interface Thenable { 5 | then(onFulfilled?: (value?: T) => U | Thenable, onRejected?: (error?: any) => U | Thenable): Thenable; 6 | } 7 | 8 | export function isEventuallyRejected(promise: Thenable): Thenable { 9 | return promise.then(function () { 10 | throw new Error('unexpected code path'); 11 | }, function () { 12 | return true; // expect rejection 13 | }); 14 | } 15 | 16 | export function throwImmediatly() { 17 | throw new Error('unexpected code path'); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.8.0", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "module": "umd", 6 | "noImplicitAny": true, 7 | "outDir": "_build/", 8 | "removeComments": false, 9 | "sourceMap": true, 10 | "target": "es5" 11 | }, 12 | "filesGlob": [ 13 | "./typings/index.d.ts", 14 | "./src/**/*.ts", 15 | "./tests/**/*.ts" 16 | ], 17 | "files": [ 18 | "./typings/index.d.ts", 19 | "./src/ex1-typing.ts", 20 | "./src/ex2-interfaces.ts", 21 | "./src/ex3-functiontypes.ts", 22 | "./src/ex4-arraytypes.ts", 23 | "./src/ex5-unionAndExtension.ts", 24 | "./src/ex6-generics.ts", 25 | "./src/ex7-classes.ts", 26 | "./src/ex8-modules.ts", 27 | "./tests/intern-local.ts", 28 | "./tests/intern.ts", 29 | "./tests/support/Reporter.ts", 30 | "./tests/support/util.ts", 31 | "./tests/unit/all.ts" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Typescript-examples", 3 | "devDependencies": { 4 | "chai": "registry:npm/chai#3.5.0+20160415060238", 5 | "glob": "registry:npm/glob#6.0.0+20160211003958" 6 | }, 7 | "globalDevDependencies": { 8 | "digdug": "github:dojo/typings/custom/digdug/digdug.d.ts#a62873258aa1deed48f9882c193c335436100d4b", 9 | "dojo-loader": "npm:dojo-loader", 10 | "dojo2": "github:dojo/typings/custom/dojo2/dojo.d.ts#a62873258aa1deed48f9882c193c335436100d4b", 11 | "dojo2-dev": "github:dojo/typings/custom/dev/dev.d.ts#288d3a9868194f0e1ad6687371dffd1d96cb39a1", 12 | "gruntjs": "registry:dt/gruntjs#0.4.0+20160316171810", 13 | "intern": "github:dojo/typings/custom/intern/intern.d.ts#92fcf688c0982c1107fca278de52e4bfe23bd8af", 14 | "leadfoot": "github:dojo/typings/custom/leadfoot/leadfoot.d.ts#a62873258aa1deed48f9882c193c335436100d4b", 15 | "node": "github:dojo/typings/custom/node/node.d.ts#a62873258aa1deed48f9882c193c335436100d4b" 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-examples", 3 | "version": "0.0.0", 4 | "description": "Typescript Examples", 5 | "license": "BSD-3-Clause", 6 | "bugs": { 7 | "url": "https://github.com/SitePen/TypeScript-examples" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/agubler/TypeScript-examples.git" 12 | }, 13 | "scripts": { 14 | "test": "grunt test" 15 | }, 16 | "devDependencies": { 17 | "codecov.io": "^0.1.6", 18 | "dojo-loader": ">=2.0.0-beta.5", 19 | "dts-generator": "1.7.0", 20 | "grunt": "^1.0.1", 21 | "grunt-contrib-clean": "^1.0.0", 22 | "grunt-contrib-copy": "^1.0.0", 23 | "grunt-contrib-watch": "^1.0.0", 24 | "grunt-dojo2": ">=2.0.0-beta.7", 25 | "grunt-text-replace": "^0.4.0", 26 | "grunt-ts": "^5.5.1", 27 | "grunt-tslint": "^3.1.0", 28 | "grunt-typings": ">=0.1.5", 29 | "intern": "^3.2.3", 30 | "istanbul": "^0.4.3", 31 | "remap-istanbul": "^0.6.4", 32 | "tslint": "^3.10.2", 33 | "typescript": "1.8.10" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The "New" BSD License 2 | ********************* 3 | 4 | Copyright (c) 2004-2015, The Dojo Foundation 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | * Neither the name of the Dojo Foundation nor the names of its contributors 16 | may be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Typescript Examples 2 | 3 | Enjoy these delightful samples taken from [SitePen](http://sitepen.com)'s TypeScript in Depth. 4 | 5 | In addition to the samples, there is a complete development environment for working with TypeScript 6 | including Grunt and Intern support! 7 | 8 | ## About Us 9 | 10 | ![](https://www.sitepen.com/images/headerLogo.png) 11 | 12 | SitePen has a 10-year history in open source. Our first release was the Dojo Toolkit, and we continue to foster the 13 | open-source community through the Dojo Foundation, which oversees such projects as grunt and lodash. We are a 14 | contributing member to, and creator of, Intern, Mayhem, dgrid, dstore, among others. We consult as expert advisors to 15 | international companies and startups, and train their developers. 16 | 17 | SitePen has [workshops on TypeScript](https://www.sitepen.com/workshops/private.html?workshop=12) and offers [support 18 | plans](https://www.sitepen.com/support/index.html) to help transfer our expert knowledge to you. 19 | 20 | 21 | ## Quick Start 22 | 23 | * `npm install` 24 | 25 | ## Grunt Commands 26 | 27 | * `grunt lint` - validates style rules 28 | * `grunt test` - runs intern's node client 29 | * `grunt test-local` - runs intern's runner with local configuration 30 | * `grunt test-proxy` - starts intern's testing proxy 31 | * `grunt test-runner` - runs intern's runner 32 | * `grunt ci` - runs tests in a continuous integration environment 33 | * `grunt clean` - cleans development work 34 | * `grunt` - compiles files 35 | 36 | ## More Resources! 37 | 38 | * [SitePen's TypeScript blogs](https://www.sitepen.com/blog/?s=typescript) 39 | * [TypeScript Handbook](http://www.typescriptlang.org/Handbook) 40 | * [TypeScript Playground](http://www.typescriptlang.org/Playground) 41 | * [TypeScript roadmap](https://github.com/Microsoft/TypeScript/wiki/Roadmap) 42 | * [Dojo 2 core](https://github.com/dojo/core) 43 | * [Decorators and metadata](https://github.com/devpaul/TSDecorators-example) 44 | * [ES6 features](https://github.com/lukehoban/es6features) 45 | * [DefinitelyTyped](http://definitelytyped.org/) 46 | * [ES6 compatibility table](https://kangax.github.io/compat-table/es6/) 47 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "align": false, 4 | "ban": [], 5 | "class-name": true, 6 | "comment-format": [ true, "check-space" ], 7 | "curly": true, 8 | "eofline": true, 9 | "forin": false, 10 | "indent": [ true, "tabs" ], 11 | "interface-name": [ true, "never-prefix" ], 12 | "jsdoc-format": true, 13 | "label-position": true, 14 | "label-undefined": true, 15 | "max-line-length": 120, 16 | "member-access": false, 17 | "member-ordering": false, 18 | "no-any": false, 19 | "no-arg": true, 20 | "no-bitwise": false, 21 | "no-consecutive-blank-lines": true, 22 | "no-console": false, 23 | "no-construct": false, 24 | "no-constructor-vars": true, 25 | "no-debugger": true, 26 | "no-duplicate-key": true, 27 | "no-duplicate-variable": true, 28 | "no-empty": false, 29 | "no-eval": true, 30 | "no-inferrable-types": [ true, "ignore-params" ], 31 | "no-shadowed-variable": false, 32 | "no-string-literal": false, 33 | "no-switch-case-fall-through": false, 34 | "no-trailing-whitespace": true, 35 | "no-unreachable": true, 36 | "no-unused-expression": false, 37 | "no-unused-variable": false, 38 | "no-use-before-declare": false, 39 | "no-var-keyword": true, 40 | "no-var-requires": false, 41 | "object-literal-sort-keys": false, 42 | "one-line": [ true, "check-open-brace", "check-whitespace" ], 43 | "quotemark": [ true, "single" ], 44 | "radix": true, 45 | "semicolon": [ true, "always" ], 46 | "trailing-comma": [ true, { 47 | "multiline": "never", 48 | "singleline": "never" 49 | } ], 50 | "triple-equals": [ true, "allow-null-check" ], 51 | "typedef": false, 52 | "typedef-whitespace": [ true, { 53 | "call-signature": "nospace", 54 | "index-signature": "nospace", 55 | "parameter": "nospace", 56 | "property-declaration": "nospace", 57 | "variable-declaration": "nospace" 58 | }, { 59 | "call-signature": "onespace", 60 | "index-signature": "onespace", 61 | "parameter": "onespace", 62 | "property-declaration": "onespace", 63 | "variable-declaration": "onespace" 64 | } ], 65 | "use-strict": false, 66 | "variable-name": [ false, "check-format", "allow-leading-underscore", "ban-keywords" ], 67 | "whitespace": [ true, "check-branch", "check-decl", "check-operator", "check-module", "check-separator", "check-type", "check-typecast" ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/intern.ts: -------------------------------------------------------------------------------- 1 | export const proxyPort = 9000; 2 | 3 | // A fully qualified URL to the Intern proxy 4 | export const proxyUrl = 'http://localhost:9000/'; 5 | 6 | // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the 7 | // specified browser environments in the `environments` array below as well. See 8 | // https://code.google.com/p/selenium/wiki/DesiredCapabilities for standard Selenium capabilities and 9 | // https://saucelabs.com/docs/additional-config#desired-capabilities for Sauce Labs capabilities. 10 | // Note that the `build` capability will be filled in with the current commit ID from the Travis CI environment 11 | // automatically 12 | export const capabilities = { 13 | 'browserstack.debug': false, 14 | project: 'Dojo 2', 15 | name: 'Typescript Examples' 16 | }; 17 | 18 | // Browsers to run integration testing against. Note that version numbers must be strings if used with Sauce 19 | // OnDemand. Options that will be permutated are browserName, version, platform, and platformVersion; any other 20 | // capabilities options specified for an environment will be copied as-is 21 | export const environments = [ 22 | { browserName: 'IE', version: [ '10.0', '11.0' ], platform: 'Windows' }, 23 | { browserName: 'Edge', platform: 'Windows' }, 24 | { browserName: 'Firefox', platform: 'Windows' }, 25 | { browserName: 'Chrome', platform: 'Windows' }, 26 | { browserName: 'Opera', platform: 'Windows' }, 27 | { browserName: 'Safari', version: [ '8.0', '9.0' ], platform: 'MAC' } 28 | ]; 29 | 30 | // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service 31 | export const maxConcurrency = 1; 32 | 33 | // Name of the tunnel class to use for WebDriver tests 34 | export const tunnel = 'BrowserStackTunnel'; 35 | 36 | export const initialBaseUrl: string = (function () { 37 | if (typeof location !== 'undefined' && location.pathname.indexOf('__intern/') > -1) { 38 | return '/'; 39 | } 40 | return null; 41 | })(); 42 | 43 | // The desired AMD loader to use when running unit tests (client.html/client.js). Omit to use the default Dojo 44 | // loader 45 | export const loaders = { 46 | 'host-browser': 'node_modules/dojo-loader/dojo.js', 47 | 'host-node': 'dojo-loader' 48 | }; 49 | 50 | // Configuration options for the module loader; any AMD configuration options supported by the specified AMD loader 51 | // can be used here 52 | export const loaderOptions = { 53 | // Packages that should be registered with the loader in each testing environment 54 | packages: [ 55 | { name: 'src', location: '_build/src' }, 56 | { name: 'tests', location: '_build/tests' }, 57 | { name: 'node_modules', location: '_build/node_modules' }, 58 | { name: 'dojo-core', location: 'node_modules/dojo-core/dist/umd' } 59 | ] 60 | }; 61 | 62 | // Non-functional test suite(s) to run in each browser 63 | export const suites = [ 'tests/unit/all' ]; 64 | 65 | // Functional test suite(s) to run in each browser once non-functional tests are completed 66 | export const functionalSuites = [ 'tests/functional/all' ]; 67 | 68 | // A regular expression matching URLs to files that should not be included in code coverage analysis 69 | export const excludeInstrumentation = /(?:node_modules|bower_components|tests)[\/\\]/; 70 | -------------------------------------------------------------------------------- /tests/support/Reporter.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as nodeUtil from 'util'; 3 | import * as path from 'path'; 4 | import * as intern from 'intern'; 5 | import Collector = require('istanbul/lib/collector'); 6 | import glob = require('glob'); 7 | import JsonReporter = require('istanbul/lib/report/json'); 8 | import Instrumenter = require('istanbul/lib/instrumenter'); 9 | import 'istanbul/index'; 10 | 11 | import Runner = require('intern/lib/reporters/Runner'); 12 | import util = require('intern/lib/util'); 13 | 14 | const LIGHT_RED = '\x1b[91m'; 15 | const LIGHT_GREEN = '\x1b[92m'; 16 | const LIGHT_MAGENTA = '\x1b[95m'; 17 | 18 | class Reporter extends Runner { 19 | private _filename: string; 20 | private _collector: Collector; 21 | private _reporter: JsonReporter; 22 | private reporter: any; 23 | private _errors: { [sessionId: string ]: any[] } = {}; 24 | 25 | constructor(config: any = {}) { 26 | super(config); 27 | 28 | this._filename = config.file || 'coverage-final.json'; 29 | this._collector = new Collector(); 30 | this.reporter = { 31 | writeReport: function () {} 32 | }; 33 | this._reporter = new JsonReporter({ 34 | file: this._filename, 35 | watermarks: config.watermarks 36 | }); 37 | } 38 | 39 | coverage(sessionId: string, coverage: any) { 40 | if (intern.mode === 'client' || sessionId) { 41 | const session = this.sessions[sessionId || '']; 42 | session.coverage = true; 43 | this._collector.add(coverage); 44 | } 45 | } 46 | 47 | runEnd() { 48 | let numEnvironments = 0; 49 | let numTests = 0; 50 | let numFailedTests = 0; 51 | let numSkippedTests = 0; 52 | 53 | for (const sessionId in this.sessions) { 54 | const session = this.sessions[sessionId]; 55 | ++numEnvironments; 56 | numTests += session.suite.numTests; 57 | numFailedTests += session.suite.numFailedTests; 58 | numSkippedTests += session.suite.numSkippedTests; 59 | } 60 | 61 | this.charm.write('\n'); 62 | 63 | if (intern.mode === 'client') { 64 | for (let sid in this._errors) { 65 | this._errors[sid].forEach((test) => { 66 | this.charm 67 | .write(LIGHT_RED) 68 | .write('× ' + test.id) 69 | .foreground('white') 70 | .write(' (' + (test.timeElapsed / 1000) + 's)') 71 | .write('\n') 72 | .foreground('red') 73 | .write(test.error) 74 | .display('reset') 75 | .write('\n\n'); 76 | }); 77 | } 78 | } 79 | 80 | let message = `TOTAL: tested ${numEnvironments} platforms, ${numFailedTests}/${numTests} failed`; 81 | 82 | if (numSkippedTests) { 83 | message += ` (${numSkippedTests} skipped)`; 84 | } 85 | if (this.hasErrors && !numFailedTests) { 86 | message += '; fatal error occurred'; 87 | } 88 | 89 | this.charm 90 | .foreground(numFailedTests > 0 || this.hasErrors ? 'red' : 'green') 91 | .write(message) 92 | .display('reset') 93 | .write('\n'); 94 | 95 | this._writeCoverage(); 96 | } 97 | 98 | suiteStart(suite: any): void { 99 | if (!suite.hasParent) { 100 | this.sessions[suite.sessionId || ''] = { suite: suite }; 101 | if (suite.sessionId) { 102 | this.charm.write('\n‣ Created session ' + suite.name + ' (' + suite.sessionId + ')\n'); 103 | } 104 | } 105 | } 106 | 107 | suiteEnd(suite: any): void { 108 | if (!suite.hasParent) { 109 | // runEnd will report all of this information, so do not repeat it 110 | if (intern.mode === 'client') { 111 | return; 112 | } 113 | 114 | // Runner mode test with no sessionId was some failed test, not a bug 115 | if (!suite.sessionId) { 116 | return; 117 | } 118 | 119 | const session = this.sessions[suite.sessionId]; 120 | 121 | if (session.coverage) { 122 | this.reporter.writeReport(session.coverage); 123 | } 124 | else { 125 | this.charm 126 | .write('No unit test coverage for ' + suite.name) 127 | .display('reset') 128 | .write('\n'); 129 | } 130 | 131 | this.charm 132 | .write('\n\n'); 133 | 134 | if (this._errors[suite.sessionId]) { 135 | this._errors[suite.sessionId].forEach((test) => { 136 | this.charm 137 | .write(LIGHT_RED) 138 | .write('× ' + test.id) 139 | .foreground('white') 140 | .write(' (' + (test.timeElapsed / 1000) + 's)') 141 | .write('\n') 142 | .foreground('red') 143 | .write(test.error) 144 | .display('reset') 145 | .write('\n\n'); 146 | }); 147 | } 148 | 149 | const name = suite.name; 150 | const hasError = (function hasError(suite: any) { 151 | return suite.tests ? (suite.error || suite.tests.some(hasError)) : false; 152 | })(suite); 153 | const numFailedTests = suite.numFailedTests; 154 | const numTests = suite.numTests; 155 | const numSkippedTests = suite.numSkippedTests; 156 | 157 | let summary = nodeUtil.format('%s: %d/%d tests failed', name, numFailedTests, numTests); 158 | if (numSkippedTests) { 159 | summary += ' (' + numSkippedTests + ' skipped)'; 160 | } 161 | 162 | if (hasError) { 163 | summary += '; fatal error occurred'; 164 | } 165 | 166 | this.charm 167 | .write(numFailedTests || hasError > 0 ? LIGHT_RED : LIGHT_GREEN) 168 | .write(summary) 169 | .display('reset') 170 | .write('\n\n'); 171 | } 172 | } 173 | 174 | testFail(test: any): void { 175 | if (!this._errors[test.sessionId]) { 176 | this._errors[test.sessionId] = []; 177 | } 178 | 179 | this._errors[test.sessionId].push({ 180 | id: test.id, 181 | timeElapsed: test.timeElapsed, 182 | error: util.getErrorMessage(test.error) 183 | }); 184 | 185 | this.charm 186 | .write(LIGHT_RED) 187 | .write('×') 188 | .display('reset'); 189 | } 190 | 191 | testPass(test: any): void { 192 | this.charm 193 | .write(LIGHT_GREEN) 194 | .write('✓') 195 | .display('reset'); 196 | } 197 | 198 | testSkip(test: any): void { 199 | this.charm 200 | .write(LIGHT_MAGENTA) 201 | .write('~') 202 | .display('reset'); 203 | } 204 | 205 | _writeCoverage(): void { 206 | let coverage: any; 207 | if (fs.existsSync(this._filename)) { 208 | coverage = JSON.parse(fs.readFileSync(this._filename, { encoding: 'utf8' })); 209 | } 210 | else { 211 | coverage = {}; 212 | const coveredFiles = this._collector.files(); 213 | const instrumenter = new Instrumenter({ 214 | noCompact: true, 215 | noAutoWrap: true 216 | }); 217 | glob.sync('_build/**/*.js').filter(function (filepath) { 218 | return !( intern.executor).config.excludeInstrumentation.test(filepath) && coveredFiles.indexOf(path.resolve(filepath)) === -1; 219 | }).forEach(function (filepath) { 220 | try { 221 | const wholename = path.resolve(filepath); 222 | instrumenter.instrumentSync(fs.readFileSync(wholename, 'utf8'), wholename); 223 | coverage[wholename] = instrumenter.lastFileCoverage(); 224 | for (let i in coverage[wholename].s) { 225 | coverage[wholename].s[i] = 0; 226 | } 227 | } 228 | catch (error) { 229 | console.error(filepath + ': ' + error); 230 | } 231 | }); 232 | } 233 | this._collector.add(coverage); 234 | this._reporter.writeReport(this._collector, true); 235 | } 236 | } 237 | 238 | export = Reporter; 239 | --------------------------------------------------------------------------------