diff --git a/public/docs/ts/latest/guide/_data.json b/public/docs/ts/latest/guide/_data.json index 98529fe97b..56acbce0df 100644 --- a/public/docs/ts/latest/guide/_data.json +++ b/public/docs/ts/latest/guide/_data.json @@ -26,5 +26,21 @@ "unit-testing-01": { "title": "Unit Testing Overview" + }, + + "jasmine-testing-101": { + "title": "Jasmine Testing 101" + }, + + "application-under-test": { + "title": "The Application Under Test" + }, + + "first-app-tests": { + "title": "First App Tests" + }, + + "testing-an-angular-pipe": { + "title": "Testing an Angular Pipe" } -} +} \ No newline at end of file diff --git a/public/docs/ts/latest/guide/application-under-test.jade b/public/docs/ts/latest/guide/application-under-test.jade new file mode 100644 index 0000000000..340915bd4a --- /dev/null +++ b/public/docs/ts/latest/guide/application-under-test.jade @@ -0,0 +1,35 @@ +include ../../../../_includes/_util-fns + +:markdown + We’ll need an Angular application to test, one as simple as possible while having all the angular features we want to test. + + We have such an app that you can download [here](./#). It’s a one-screen variation on the “Tour of Heroes” that should be familiar to you as a reader of this Developers Guide. + + Our test app displays a list of heroes - all favorites of the user named “Bongo”. It looks like this: + +figure.image-display + img(src='/resources/images/devguide/application-under-test/bongos-heroes.png' style="width:400px;" alt="Bong's Heroes") + +:markdown + At the top is a master list of heroes; at the bottom the detail for the current hero. Click a hero in the list to change the current hero. Change the name in the textbox and that name updates everywhere. The *Update* button modifies the `Hero.name` in an arbitrary way and that change also propagates everywhere on screen. The *Delete* button deletes the hero from the list and a new hero becomes current. *Refresh* clears both the list and detail, then restores the original list of heroes. + + You can see a short video of the app in action [here](./#) + + This simple app illustrates a number of Angular features that we’d like to test. + + - A simple service that presents the `username` (“Bongo”) + - A dataservice that fetches and caches the list of heroes. + - The dataservice depends in turn on another “backend” service that handles the interaction with a remote web api + - A master `HeroesComponent` presents the list + - The master communicates with a detail component `HeroDetailComponent` about the current hero both through an attribute and an event. + - The detail’s template is nested within the master component’s template. + - The `name` textbox illustrates two-way databinding + - The update button demonstrates that a programmatic change to the databound model propagates to both component views + - The delete button triggers an event that is caught by the parent component + - [TBD: need to add a filter and a directive to this sample] + - [TBD: need to shoehorn the router in somehow] + + We’ll examine the implementation details as we evolve our tests. + + ## What’s Next? + Now that we’re familiar with how the test app works, we’re ready to poke at it with our first application tests written in Jasmine. diff --git a/public/docs/ts/latest/guide/example.js b/public/docs/ts/latest/guide/example.js new file mode 100644 index 0000000000..323d961994 --- /dev/null +++ b/public/docs/ts/latest/guide/example.js @@ -0,0 +1,98 @@ +var path = require('canonical-path'); +var fs = require("fs"); +var FRAGMENT_DIR = "./public/docs/_fragments"; + +/** + * @dgService exampleInlineTagDef + * @description + * Process inline example tags (of the form {@example relativePath region -title='some title' -stylePattern='{some style pattern}' }), + * replacing them with a jade makeExample mixin call. + * @kind function + * @param {Object} path The relative path to example + * @param {Function} docs error message + * @return {String} The jade makeExample mixin call + * + * @property {boolean} relativeLinks Whether we expect the links to be relative to the originating doc + */ +module.exports = function exampleInlineTagDef(getLinkInfo, createDocMessage, log) { + return { + name: 'example', + description: 'Process inline example tags (of the form {@example some/uri Some Title}), replacing them with HTML anchors', + handler: function(doc, tagName, tagDescription) { + + var tagArgs = parseArgs(tagDescription); + var unnamedArgs = tagArgs._; + var relativePath = unnamedArgs[0]; + var region = unnamedArgs.length > 1 && unnamedArgs[1]; + var title = tagArgs.title; + // TODO: not yet implemented here + var stylePattern = tagArgs.stylePattern; + + var dir = path.join("_api", path.dirname(relativePath)); + var extn = path.extname(relativePath); + var baseNameNoExtn = path.basename(relativePath, extn); + var fileName = region ? baseNameNoExtn + "-" + region + extn : baseNameNoExtn + extn; + var fullFileName = path.join(FRAGMENT_DIR, dir, fileName); + if ( !fs.existsSync(fileName)) { + log.warn(createDocMessage('Invalid example (unable to locate fragment file: ' + quote(fullFileName), doc)); + } + + var comma = ', ' + var res = [ "+makeExample(", quote(dir), comma, quote(fileName), comma, title ? quote(title) : 'null', ")" ].join(''); + return res; + } + + }; +}; + +function quote(str) { + if (str == null || str.length === 0) return str; + str = str.replace("'","'\'"); + return "'" + str + "'"; +} + + +// processes an arg string in 'almost' the same fashion that the command processor does +// and returns an args object in yargs format. +function parseArgs(str) { + // regex from npm string-argv + //[^\s'"] Match if not a space ' or " + + //+|['] or Match ' + //([^']*) Match anything that is not ' + //['] Close match if ' + + //+|["] or Match " + //([^"]*) Match anything that is not " + //["] Close match if " + var rx = /[^\s'"]+|[']([^']*?)[']|["]([^"]*?)["]/gi; + var value = str; + var unnammedArgs = []; + var args = { _: unnammedArgs }; + var match, key; + do { + //Each call to exec returns the next regex match as an array + match = rx.exec(value); + if (match !== null) { + //Index 1 in the array is the captured group if it exists + //Index 0 is the matched text, which we use if no captured group exists + var arg = match[2] ? match[2] : (match[1]?match[1]:match[0]); + if (key) { + args[key] = arg; + key = null; + } else { + if (arg.substr(arg.length-1) === '=') { + key = arg.substr(0, arg.length-1); + // remove leading '-' if it exists. + if (key.substr(0,1)=='-') { + key = key.substr(1); + } + } else { + unnammedArgs.push(arg) + key = null; + } + } + } + } while (match !== null); + return args; +} diff --git a/public/docs/ts/latest/guide/first-app-tests.jade b/public/docs/ts/latest/guide/first-app-tests.jade new file mode 100644 index 0000000000..abb3613a11 --- /dev/null +++ b/public/docs/ts/latest/guide/first-app-tests.jade @@ -0,0 +1,328 @@ +include ../../../../_includes/_util-fns + +:markdown + In this chapter we’ll setup the environment for testing our sample application and write a few easy Jasmine tests of the app’s simplest parts. + + We learn: + - to test one of our application classes + - why we prefer our test files to be next to their corresponding source files + - to run tests with an `npm` command + - load the test file with systemJS + + ## Prerequisites + + We assume + + - you’ve learned the basics of Angular 2, from this Developers Guide or elsewhere. We won’t re-explain the Angular 2 architecture, its key parts, or the recommended development techniques. + you’ve read the [Jasmine 101](./jasmine-testing-101.html) chapter. + + - you’ve downloaded the [Heroes application we’re about to test](./#). + + ## Create the test-runner HTML + + Step away from the Jasmine 101 folder and turn to the root folder of the application that we downloaded in the previous chapter. + + Locate the `src` folder that contains the application `index.html` + + Create a new, sibling HTML file, ** `unit-tests.html` ** and copy over the same basic material from the `unit-tests.html` in the [Jasmine 101](./jasmine-testing-101.html) chapter. + + ``` + +