{"id":18764473,"url":"https://github.com/theintern/intern-tutorial","last_synced_at":"2025-08-03T17:32:45.471Z","repository":{"id":9531437,"uuid":"11433052","full_name":"theintern/intern-tutorial","owner":"theintern","description":"Learn how to use Intern by following this tutorial!","archived":false,"fork":false,"pushed_at":"2023-03-04T02:27:17.000Z","size":1060,"stargazers_count":143,"open_issues_count":27,"forks_count":44,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-08T11:01:33.427Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://theintern.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/theintern.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-07-15T20:33:51.000Z","updated_at":"2025-01-02T01:12:12.000Z","dependencies_parsed_at":"2024-11-07T18:35:11.494Z","dependency_job_id":null,"html_url":"https://github.com/theintern/intern-tutorial","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/theintern/intern-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theintern%2Fintern-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theintern%2Fintern-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theintern%2Fintern-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theintern%2Fintern-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/theintern","download_url":"https://codeload.github.com/theintern/intern-tutorial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theintern%2Fintern-tutorial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268582638,"owners_count":24273888,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T18:29:57.609Z","updated_at":"2025-08-03T17:32:45.411Z","avatar_url":"https://github.com/theintern.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Intern tutorial\n\n[![Intern](https://theintern.io/images/intern-v4.svg)](https://github.com/theintern/intern/)\n\nIn this tutorial we will walk through how to set up Intern and how to write\ntests and run tests. This repository contains a basic Hello World demo\n“application” that we’ll be using as an example to build on. In order to\ncomplete this tutorial, you will need the following:\n\n- A Bourne-compatible shell, like bash or zsh (or knowledge of how to execute\n  equivalent commands in your environment)\n- [Git](https://git-scm.com/)\n- [Node 6.0.0+](https://nodejs.org/) and\n  [npm 5.3.0+](https://www.npmjs.com/package/npm)\n- [Java 1.8+](https://java.com/) (for running a local Selenium server)\n- A\n  [free BrowserStack Automate trial account](https://www.browserstack.com/users/sign_up)\n\nOnce you have all the necessary prerequisites, download the demo application by\ncloning this repository and installing the dependencies:\n\n```bash\ngit clone https://github.com/theintern/intern-tutorial.git\ncd intern-tutorial\nnpm install\n```\n\nThe application itself consists of a basic HTML page and a single “app” package\nwritten in TypeScript. Several `npm` scripts have been provided to simplify the\nbuilding and testing processes:\n\n- `npm run compile` - runs the TypeScript compiler once\n- `npm run compile:watch` - runs the TypeScript compiler in watch mode (runs the\n  compiler, waits for changes, and re-compiles when changes are detected)\n- `npm run copy` - copies any assets from `src` to `_dist/src`\n- `npm run copy:watch` - watches for changes to assets in `src` and copies files\n  to `_src/dist` when changes are detected\n- `npm run build` - runs `npm run compile` and `npm run copy` in parallel\n- `npm run build:watch` - runs `npm run compile:watch` and `npm run copy:watch`\n  in parallel\n- `npm test` - builds the application and runs Intern\n\n_In order for the demo application to work properly during the tutorial, make\nsure that you access it using a real web server. Like most applications, it will\nnot work from a `file:` URL due to cross-protocol browser security\nrestrictions._\n\n## What can Intern test?\n\nIntern can test all sorts of things:\n\n- Plain JavaScript code, in any module format (or no module format!)\n- Web pages generated by server-side languages like Java, PHP, or Ruby\n- Native or hybrid iOS, Android, and Firefox OS applications\n\nIntern is minimally prescriptive and enforces only a basic set of best practices\ndesigned to ensure your tests stay maintainable over time. Its extensible\narchitecture allows you to write custom test interfaces, executors, and\nreporters to influence how your tests run and easily integrate with your\nexisting coding environment.\n\nUnlike most other testing systems, Intern also supports two different types of\ntesting: unit testing and functional testing. **Unit testing** works by\nexecuting code directly and inspecting the result, such as calling a function\nand then checking that it returns an expected value. **Functional testing**\nworks by mimicking user interaction with a browser by issuing commands through a\nWebDriver server (an executable that lets testing tools interact with a\nbrowser).\n\nThis is a powerful notion: Intern allows us to test _code_ with regular unit\ntests, but also allows us to test _functionality_ by mimicking user interaction\nwith the browser.\n\n## Step 1: Download Intern\n\nIntern is distributed as an [npm package](https://npmjs.org/package/intern) so\nit can be easily added as a dependency to any JavaScript project. We’ll install\nIntern using `npm install --save-dev` so that npm adds it automatically as a\ndevelopment dependency to application’s\n[package.json](https://npmjs.org/doc/json.html):\n\n```bash\nnpm install --save-dev intern\n```\n\nWe also need to tell TypeScript to load Intern’s and SystemJS’s type definitions\nby default. This ensures that typings for global variables provided by Intern\nand SystemJS will be available in tests. Add the following to the\n`\"compilerOptions\"` object in `tsconfig.json`:\n\n```json\n    \"types\": [\n        \"intern\",\n        \"systemjs\"\n    ]\n```\n\nThat’s it! Installation is complete.\n\n## Step 2: Configure Intern\n\nIntern needs to be configured so it can find and run our tests. This is done by\ncreating an Intern configuration file named `intern.json` at the root of our\nproject:\n\n```json\n{\n  \"browser\": {\n    \"loader\": {\n      \"script\": \"systemjs\"\n    },\n    \"plugins\": {\n      \"script\": \"_dist/src/system.config.js\",\n      \"useLoader\": true\n    }\n  },\n  \"environments\": [\"node\", { \"browserName\": \"chrome\" }]\n}\n```\n\nThis configuration tells Intern that, in the browser, we want to use SystemJS to\nload modules, and that we want to load a plugin to configure SystemJS. The\nplugin needs to have access to the SystemJS loader, so we set the \"useLoader\"\nflag to true. Without the \"useLoader\" flag the plugin would be loaded _before_\nthe external loader, meaning it wouldn’t have access to SystemJS.\n\nThe configuration also tells Intern that, in addition to running our unit tests\nin Node.js, we want to run our tests in Chrome. You can find more information\nabout possible configuration options in\n[the Configuration section of the Intern documentation](https://theintern.io/docs.html#Intern/4/docs/docs%2Fconfiguration.md/properties).\n\nWe’ll be doing a little more configuration shortly when we start adding tests,\nbut for now, we already have a complete configuration. You can verify that\neverything is working by running Intern:\n\n```bash\nnpm test\n```\n\nIt should output:\n\n```\nNo unit test coverage for node\nnode: 0 passed, 0 failed\n```\n\nNow that we’ve configured Intern, we need to create a test module which will\ncontain the actual tests for our application.\n\n## Step 3: Write a unit test\n\nThere are a variety of syntaxes used to write unit tests, and Intern comes with\nbuilt-in support for several of them, including\n[TDD](https://github.com/theintern/intern/blob/master/docs/writing_tests.md#tdd),\n[BDD](https://github.com/theintern/intern/blob/master/docs/writing_tests.md#bdd),\nand\n[object](https://github.com/theintern/intern/blob/master/docs/writing_tests.md#object).\nIn this tutorial, we will use the **TDD** syntax, but this is an individual\npreference. All of these interfaces support the same basic functionality, so\npick whichever one you think is the clearest when you start writing your own\ntests!\n\nBefore getting any further into writing tests, we need to take a moment to\nreview the terminology that is used by Intern:\n\n- An **assertion** is a function call that verifies that a variable contains (or\n  a function returns) an expected value (e.g. `assert.isTrue(someVariable)`)\n- A **test interface** is a programming interface for registering tests with\n  Intern\n- A **test case** (or, just **test**) is a function that makes calls to\n  application code and makes assertions about what it should have done\n- A **test suite** is a collection of tests (and, optionally, sub-suites) that\n  are related to each other\n- A **test module** is a JavaScript module that contains test suites\n\nThese pieces can be visualized in a hierarchy, like this:\n\n- test module\n  - test suite\n    - test suite\n      - test case\n        - ...\n      - ...\n    - test case\n      - assertion\n      - assertion\n      - ...\n    - ...\n  - test suite\n  - ...\n- test module\n- ...\n\nTest modules are typically split up so that there’s one test module for each\ncorresponding code module being tested. First, create a new subdirectory for\nstoring all of the unit tests:\n\n```bash\nmkdir -p tests/unit\n```\n\nWe have one code module in our demo app (`app/hello`), so we’ll create a new\nunit test module at `intern-tutorial/tests/unit/hello.ts` and put the following\nboilerplate into it:\n\n```ts\nconst { suite, test } = intern.getPlugin('interface.tdd');\nconst { assert } = intern.getPlugin('chai');\n\nimport { greet } from '../../src/app/hello';\n```\n\nThis bit of code loads the `suite` and `test` functions from the TDD test\ninterface, the `assert` function of [Chai](http://chaijs.com/api/assert/), and\nthe `greet` function we want to test.\n\nNow that the basics of our `hello` test module are in place, the next step is to\nuse `suite` to register a test suite and `test` to register a test case for our\napp. We’ll start by testing the `greet` function.\n\nLooking at the source code for `app/hello`, we can see that when `greet` is\ncalled it will return the string `\"Hello, world!\"` if no name is passed, or\n`\"Hello, \u003cname\u003e!\"` if a name is passed. We need to make sure we test both of\nthese code branches. If we’ve done it right, our test code will end up looking\nsomething like this:\n\n```ts\nconst { suite, test } = intern.getPlugin('interface.tdd');\nconst { assert } = intern.getPlugin('chai');\n\nimport { greet } from '../../src/app/hello';\n\nsuite('hello', () =\u003e {\n  test('greet', () =\u003e {\n    assert.strictEqual(\n      greet('Murray'),\n      'Hello, Murray!',\n      'greet should return a greeting for the person named in the first argument'\n    );\n    assert.strictEqual(\n      greet(),\n      'Hello, world!',\n      'greet with no arguments should return a greeting to \"world\"'\n    );\n  });\n});\n```\n\n_Note: This example test uses `assert.strictEqual`, which is just one of many\navailable assertions. For a complete list of available methods, see the\n[Chai documentation](http://chaijs.com/api/)._\n\nIn this test module, we’ve registered a new suite for our `hello` module and\nnamed it “hello”, written a new test case for the `greet` method and named it\n“greet”, and added two assertions: one where we call `greet` and pass an\nargument, and one where we call `greet` without any argument. If either of these\nassertions fails, they will throw an error and the test case will be considered\nfailed at that point.\n\nEach of our assertions also contains a message that describes what logic the\nassertion is actually checking. Similar to good code comments that describe\n_why_ a piece of code exists, these messages are used to describe the intent of\nthe code being checked rather than simply describing the assertion. For\ninstance, “Calling greet('Murray') should return \"Hello, Murray!\"” would be a\nbad assertion message because it just describes what the assertion is doing,\nrather than describing the desired outcome. With the message we’ve used in the\ncode above, if the `greet` function were changed in the future to return\n`\"Hi, \u003cname\u003e!\"` instead, it would be clear that the test itself needed to be\nupdated because the code still fulfills the described business logic. Similarly,\nif the method were changed to return `\"You suck, \u003cname\u003e!\"` instead, it would\nthen be clear that the application code was updated incorrectly.\n\nNow that we’ve created our first test module, we need to update the TypeScript\nconfig to actually compile the test. Add a glob for the tests directory to the\n“include“ property in `tsconfig.json`:\n\n```json\n\"include\": [\n  \"src/**/*.ts\",\n  \"tests/**/*.ts\"\n]\n```\n\nNote how the suite file accesses `getPlugin` on a global `intern` variable. This\nvariable will be created when Intern is loaded, but the typings won't know about\nit without an additional config update. Add a `types` property to the\n`compilerOptions` section of the `tsconfig.json`:\n\n```json\n\"compilerOptions\": {\n  \"types\": [\"intern\"]\n}\n```\n\nThis property tells the Typescript compiler to load Intern's types implicitly\nwhen compiling the tests, which ensures that Typescript knows about the global\n`intern` variable.\n\nThe final step when writing a new test module is to add the **compiled**\nmodule‘s path to our configuration file so that it is loaded when we run Intern.\nTo do this, add a `suites` property to the top-level object of `intern.json`\nwith the string `\"_dist/tests/unit/hello.js\"`:\n\n```json\n\"suites\": \"_dist/tests/unit/hello.js\",\n```\n\nNow if we go back and run the same `npm test` command from the end of Step 2, we\nshould see our tests running (and passing) in both Node.js and Chrome:\n\n```\nListening on localhost:9000 (ws 9001)\nTunnel started\n✓ node - hello - greet (0.001s)\nNo unit test coverage for node\nnode: 1 passed, 0 failed\n\n‣ Created remote session chrome 59.0.3071.115 on MAC (714dfe2b-ebc0-4249-b235-a3756d004fc8)\n✓ chrome 59.0.3071.115 on MAC - hello - greet (0.001s)\nNo unit test coverage for chrome 59.0.3071.115 on MAC\nchrome 59.0.3071.115 on MAC: 1 passed, 0 failed\nTOTAL: tested 2 platforms, 2 passed, 0 failed\n```\n\nThese same tests can be run directly within a Web browser by running\n`npm test serveOnly` and navigating to\n`http://localhost:9000/__intern/index.html`.\n\n## Step 4: Write a functional test\n\nFunctional tests are different from unit tests in that they _mimic user\ninteraction_ by sending commands to browsers using an external server instead of\nrunning directly in the environment being tested. This enables us to generate\nreal DOM events and test UI interactions just like a real user, with no\nJavaScript security sandbox limitations. As well as enabling testing of\nsandbox-restricted actions like file uploads, functional testing also allows us\nto test interactions that span multiple pages and interactions with third party\nsites (like OAuth authorization flows). Our demo app contains an HTML file with\na basic form that should display a greeting using `app/hello.greet`. For this\ntutorial, we’ll simulate a user filling out a form and clicking a button to\nsubmit it in order to verify this page works as expected.\n\nIntern’s functional testing is based on the\n[standard WebDriver protocol](http://www.w3.org/TR/webdriver/) and comes with\nbuilt-in support for remote testing services as well as\n[self-hosted WebDriver servers](http://docs.seleniumhq.org/docs/03_webdriver.jsp#running-standalone-selenium-server-for-use-with-remotedrivers).\nThe rest of this tutorial assumes you are using BrowserStack.\n\nTo get started, create a new directory to hold the functional tests (in order to\ndifferentiate them from our normal unit tests) at\n`intern-tutorial/tests/functional`:\n\n```bash\nmkdir -p tests/functional\n```\n\nNext, create a test module at `intern-tutorial/tests/functional/index.ts` with\nthe following boilerplate:\n\n```ts\nconst { suite, test, before } = intern.getPlugin('interface.tdd');\nconst { assert } = intern.getPlugin('chai');\n\nsuite('index', () =\u003e {\n  before(() =\u003e {});\n\n  test('greeting form', () =\u003e {});\n});\n```\n\nJust like the unit test we created before, we’re using the object test interface\nand assert-style assertions. However, instead of loading any application code\ndirectly, we’ll be using WebDriver to load our page in the browser.\n\nTo facilitate functional testing, an object is passed to every lifecycle and\ntest function which has a `remote` property. The `remote` property exposes an\nobject that provides an interface for interacting with the remote browser\nenvironment. Using the methods on `remote`, we can load a Web page, interact\nwith it, and retrieve data from it to assert that our actions caused the\nexpected result. Since all calls to the remote browser are asynchronous, all\nmethods of the `remote` object return promises. This allows us to either chain\ncommands (like jQuery) and retrieve results using standard promises-style `then`\ncalls or use async/await to write synchronous-looking tests. When we make a\ncall, it is enqueued and executed once all the previous commands have completed.\nIf this description is a little confusing, don’t worry — it should be clearer\nonce we look at some code.\n\nLooking at the HTML page at `index.html`, we can see that it consists of a\nsimple form with a single input. It loads `app/main` which sets up our event\nlisteners and adds a CSS class of “loaded” to the body element. We want to make\nsure this form works properly by testing interaction like a real user: focusing\nthe input, typing a string, and clicking submit. We can then verify that the\ngreeting was properly updated. Once finished, this test will look something like\nthis:\n\n```ts\nconst { suite, test, before } = intern.getPlugin('interface.tdd');\nconst { assert } = intern.getPlugin('chai');\n\nsuite('index', () =\u003e {\n  before(({ remote }) =\u003e {\n    return remote\n      .get('_dist/src/index.html')\n      .setFindTimeout(5000)\n      .findDisplayedByCssSelector('body.loaded');\n  });\n\n  test('greeting form', ({ remote }) =\u003e {\n    return remote\n      .findById('nameField')\n      .click()\n      .type('Elaine')\n      .end()\n\n      .findByCssSelector('#loginForm input[type=submit]')\n      .click()\n      .end()\n\n      .findById('greeting')\n      .getVisibleText()\n      .then(text =\u003e {\n        assert.strictEqual(\n          text,\n          'Hello, Elaine!',\n          'Greeting should be displayed when the form is submitted'\n        );\n      });\n  });\n});\n```\n\nIt could also be written using async/await:\n\n```ts\nconst { suite, test, before } = intern.getPlugin('interface.tdd');\nconst { assert } = intern.getPlugin('chai');\n\nsuite('index', () =\u003e {\n  before(async ({ remote }) =\u003e {\n    await remote.get('_dist/src/index.html');\n    await remote.setFindTimeout(5000);\n    await remote.findDisplayedByCssSelector('body.loaded');\n  });\n\n  test('greeting form', async ({ remote }) =\u003e {\n    const name = await remote.findById('nameField');\n    await name.click();\n    await name.type('Elaine');\n\n    const button = await remote.findByCssSelector(\n      '#loginForm input[type=submit]'\n    );\n    await button.click();\n\n    const greeting = await remote.findById('greeting');\n    const text = await greeting.getVisibleText();\n\n    assert.strictEqual(\n      text,\n      'Hello, Elaine!',\n      'Greeting should be displayed when the form is submitted'\n    );\n  });\n});\n```\n\n_Note: To learn which methods are available on the `remote` object, check\nLeadfoot’s\n[Command object documentation](https://theintern.io/leadfoot/module-leadfoot_Command.html)._\n\nIn the code above, calling `remote.get` loads the HTML page we want to test into\nthe browser. Then, we wait for the “loaded” CSS class to appear on the body, for\na maximum of five seconds. Once this element exists, we go through the process\nof finding, clicking, and typing into elements. Finally, we retrieve the text\nfrom the greeting element and check it to confirm that it matches what was\nexpected.\n\nNow that this test module is complete, the final step is to add it to our Intern\nconfiguration in the special `functionalSuites` top-level property:\n\n```json\n\"functionalSuites\": \"_dist/tests/functional/index.js\",\n```\n\nNow if we go back and run the same `npm test` command from the end of Steps 2\nand 3, we will see our unit tests running in both Node.js and Chrome, our\nfunctional tests running in Chrome, and all of them passing:\n\n```\nListening on localhost:9000 (ws 9001)\nTunnel started\n✓ node - hello - greet (0.001s)\nNo unit test coverage for node\nnode: 1 passed, 0 failed\n\n‣ Created remote session chrome 59.0.3071.115 on MAC (f1bffddb-f4ae-46ba-8633-2357f925939d)\n✓ chrome 59.0.3071.115 on MAC - hello - greet (0.001s)\n✓ chrome 59.0.3071.115 on MAC - index - greeting form (0.236s)\nNo unit test coverage for chrome 59.0.3071.115 on MAC\nchrome 59.0.3071.115 on MAC: 2 passed, 0 failed\nTOTAL: tested 2 platforms, 3 passed, 0 failed\n```\n\n## Step 5: Code coverage\n\nAt this point, all of our unit and functional tests are passing. The next step\nis enabling code coverage. Intern is unique in that it not only runs unit and\nfunctional tests in one command, but it can also gather coverage information for\nboth types of tests as well! To enable code coverage, set the `\"coverage\"`\nproperty of the top-level object in `intern.json` to a glob pattern (or an array\nof glob patterns) of compiled files to cover:\n\n```json\n\"coverage\": [\n    \"_dist/src/**/*.js\",\n    \"!_dist/src/system.config.js\"\n]\n```\n\nThis will tell Intern to get coverage information for all JavaScript files in\n`_dist/src` except for `_dist/src/system.config.js`. Now when we run `npm test`,\nthe output will tell us the coverage we have:\n\n```\nListening on localhost:9000 (ws 9001)\nTunnel started\n✓ node - hello - greet (0.001s)\n\n----------|----------|----------|----------|----------|----------------|\nFile      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n----------|----------|----------|----------|----------|----------------|\nAll files |      100 |      100 |      100 |      100 |                |\n hello.ts |      100 |      100 |      100 |      100 |                |\n----------|----------|----------|----------|----------|----------------|\nnode: 1 passed, 0 failed\n\n‣ Created remote session chrome 59.0.3071.115 on MAC (dbc5f5cc-2f89-43e1-b062-7df608334314)\n✓ chrome 59.0.3071.115 on MAC - hello - greet (0.001s)\n✓ chrome 59.0.3071.115 on MAC - index - greeting form (0.231s)\n\n----------|----------|----------|----------|----------|----------------|\nFile      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n----------|----------|----------|----------|----------|----------------|\nAll files |      100 |      100 |      100 |      100 |                |\n hello.ts |      100 |      100 |      100 |      100 |                |\n----------|----------|----------|----------|----------|----------------|\nchrome 59.0.3071.115 on MAC: 2 passed, 0 failed\n\nTotal coverage\n----------|----------|----------|----------|----------|----------------|\nFile      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n----------|----------|----------|----------|----------|----------------|\nAll files |      100 |      100 |      100 |      100 |                |\n hello.ts |      100 |      100 |      100 |      100 |                |\n main.ts  |      100 |      100 |      100 |      100 |                |\n----------|----------|----------|----------|----------|----------------|\nTOTAL: tested 2 platforms, 3 passed, 0 failed\n```\n\nOne thing to note: Intern (via Istanbul) automatically remaps the coverage\ninformation back to our source files using source maps. We can see that the unit\ntests only report coverage for `hello.ts`, but our functional test - since it\nexercises the entire application - reports coverage for `main.ts` as well.\n\nIntern also allows us to output an HTML coverage report to see graphically which\nlines have been exercised by our tests. To enable this feature, add the\n`htmlcoverage` reporter in the top-level object of `intern.json`:\n\n```json\n\"reporters+\": \"htmlcoverage\"\n```\n\nAs you can see, instead of using `\"reporters\"`, we have used `\"reporters+\"`.\nThis will add `\"htmlcoverage\"` to the default array of reporters instead of\noverriding it. When we run `npm test` we’ll still see the same output as before\nin the console, and we will also have an HTML report in\n`intern-tutorial/coverage/`.\n\n## Step 6: Remote testing\n\nAt this point, all our tests are written and running in Node.js and Chrome. The\nonly thing that’s left to do is to run all our tests on all the platforms we\nwant to support. We’ll do this by setting up a `browserstack` configuration\nwithin `intern.json` to run our tests with BrowserStack:\n\n```json\n\"reporters+\": \"htmlcoverage\",\n\"configs\": {\n    \"browserstack\": {\n        \"tunnel\": \"browserstack\",\n        \"maxConcurrency\": 2,\n        \"capabilities\": {\n            \"idle-timeout\": 60,\n            \"fixSessionCapabilities\": \"no-detect\"\n        },\n        \"environments\": [\n            { \"browser\": \"internet explorer\", \"version\": [ \"10\", \"11\" ] },\n            { \"browser\": \"firefox\", \"version\": [ \"latest\" ], \"platform\": [ \"WINDOWS\", \"MAC\" ] },\n            { \"browser\": \"chrome\", \"version\": [ \"latest\" ], \"platform\": [ \"WINDOWS\", \"MAC\" ] },\n            { \"browser\": \"safari\", \"version\": [ \"9\", \"10\" ] }\n        ]\n    }\n}\n```\n\nThis sets up a child configuration named `browserstack` with our environments\nand tunnel. Intern will use this information to communicate with our remote\ntesting service (in this case, BrowserStack) to run our unit and functional\ntests in all of the browsers we specified in our `environments` array, reporting\nback test results and coverage for each browser. Since we are using\nBrowserStack, we will need to provide our credentials and our child\nconfiguration name:\n\n```bash\nBROWSERSTACK_USERNAME=\u003cyour username\u003e BROWSERSTACK_ACCESS_KEY=\u003cyour access key\u003e npm test config=@browserstack\n```\n\nYou can also specify your username and access key on the `tunnelOptions` object\nin your Intern configuration, using the `username` and `apiKey` keys, if you\ndon’t want to put them on the command line:\n\n```json\n\"tunnel\": \"browserstack\",\n\"tunnelOptions\": {\n  \"username\": \"\u003cyour username\u003e\",\n  \"apiKey\": \"\u003cyour access key\u003e\"\n},\n\"maxConcurrency\": 2,\n```\n\nHowever, keep in mind that keeping this information in a configuration file can\nexpose your username and access key to others if the file is checked into a\npublic repository.\n\nIf everything was done correctly, you should see the results of the test run\nbeing output to your terminal:\n\n```\nListening on localhost:9000 (ws 9001)\nTunnel started\n✓ node - hello - greet (0.001s)\n\n----------|----------|----------|----------|----------|----------------|\nFile      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n----------|----------|----------|----------|----------|----------------|\nAll files |      100 |      100 |      100 |      100 |                |\n hello.ts |      100 |      100 |      100 |      100 |                |\n----------|----------|----------|----------|----------|----------------|\nnode: 1 passed, 0 failed\n\n‣ Created remote session internet explorer 10 on WINDOWS (2ebde91d356d8dd326514caddc3e600a0aeb58f6)\n✓ internet explorer 10 on WINDOWS - hello - greet (0s)\n✓ internet explorer 10 on WINDOWS - index - greeting form (1.832s)\n\n----------|----------|----------|----------|----------|----------------|\nFile      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n----------|----------|----------|----------|----------|----------------|\nAll files |      100 |      100 |      100 |      100 |                |\n hello.ts |      100 |      100 |      100 |      100 |                |\n----------|----------|----------|----------|----------|----------------|\ninternet explorer 10 on WINDOWS: 2 passed, 0 failed\n\n‣ Created remote session internet explorer 11 on WINDOWS (f8ab8d0a315172af0337b9fcd91ae3457098a906)\n\n‣ Created remote session firefox on windows_nt 6.3 (1cb854da49290a1818d8147244c069504c51775b)\n✓ internet explorer 11 on WINDOWS - hello - greet (0s)\n✓ firefox on windows_nt 6.3 - hello - greet (0.001s)\n✓ internet explorer 11 on WINDOWS - index - greeting form (3.618s)\n\n----------|----------|----------|----------|----------|----------------|\nFile      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n----------|----------|----------|----------|----------|----------------|\nAll files |      100 |      100 |      100 |      100 |                |\n hello.ts |      100 |      100 |      100 |      100 |                |\n----------|----------|----------|----------|----------|----------------|\n\n...\n\nTotal coverage\n----------|----------|----------|----------|----------|----------------|\nFile      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |\n----------|----------|----------|----------|----------|----------------|\nAll files |      100 |      100 |      100 |      100 |                |\n hello.ts |      100 |      100 |      100 |      100 |                |\n main.ts  |      100 |      100 |      100 |      100 |                |\n----------|----------|----------|----------|----------|----------------|\nTOTAL: tested 9 platforms, 17 passed, 0 failed\n```\n\nWhen you start testing your actual application, it’s a good idea to use Intern\nin conjunction with a continuous integration service like\n[Travis CI](https://travis-ci.org) or [Jenkins](https://jenkins.io) so you know\nthat the code in your repository is passing its tests at all times, and so you\ncan monitor your code coverage figures. Instructions are available in the\n[continuous integration section](https://github.com/theintern/intern/blob/master/docs/ci.md)\nof the documentation for running Intern with Jenkins, Travis CI, and TeamCity.\n\nIf you’d like a complete working copy of this project with Intern already\nconfigured and the tests already written,\n[download the completed-tutorial branch](https://github.com/theintern/intern-tutorial/archive/completed.zip).\nIf you have any questions, please\n[let us know](https://github.com/theintern/intern/blob/master/docs/help.md).\nPull requests to enhance this tutorial are also accepted and appreciated!\n\nOnce you’re ready to dive in and start writing tests for your own application,\ntake a look at\n[Intern’s project documentation](https://github.com/theintern/intern#more-information).\nIt contains references and documentation for all of the features of Intern.\n\nHappy testing!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheintern%2Fintern-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheintern%2Fintern-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheintern%2Fintern-tutorial/lists"}