Chapter 13. Component testing

This chapter covers

  • Running tests with the Web Component Tester (WCT)
  • Using Mocha and Chai for creating tests
  • Alternate test running with Karma and Karma Web Components

Before we consider the color picker component finished, there is one additional step that should really be taken into account. It’s not a step that everyone puts effort into, but testing can go a long way in terms of how much the component can be trusted and how easy it is to maintain. The same can be said for almost anything you make in software development.

Testing can be broken down in many ways, but one of those ways is functional versus unit testing. The lines between these can get fairly blurry, but unit testing typically involves taking a piece of code that does one single thing, or a unit, and running a series of tests on it to make sure it doesn’t fall down for some edge case that wasn’t considered during development. Functional testing, on the other hand, involves testing a specific piece of functionality that is expected by the user—it’s not making sure the code does the right things, only that the application does.

13.1. Unit testing and TDD

In the color picker component, the color conversion utilities in components/colorpicker/src/color.js are the perfect candidates for unit testing. For example, in the module, a function exists to convert RGB color to a hex value. A single test might be to ensure that an object that looks like { r: 255, g: 0, b: 0 } produces the output of #ff0000. It could work perfectly and in all the right ways, but doesn’t error correct when invalid values (like those over 255 or negative numbers) are passed. The practice of writing unit tests is a great way to think of these edge cases.

Unit tests would throw a wide variety of cases at this function, and, if any of them failed, you as a developer would know you have something to fix. Of course, if you did fix it and made lots of changes to do so, you might want to have confidence that you didn’t break anything else. So, you’d re-run the unit tests. If they all passed, you’d have confidence that this piece of functionality works as it always has.

Test running can also track code coverage. For example, if there was an if/then block in your code, and your unit tests didn’t cover a case that happened over both of those conditions, a report would be generated indicating that you didn’t cover those specific lines of code.

When people normally think of unit testing, especially outside of web development, unit tests don’t often include the UI. Modern web development is where those lines can tend to get blurred. If you think of a component, whether a Web Component or one in React, Vue, Angular, and so on, it will have an API. This API can be thought of as a unit that can be tested. Moreover, lots of JS functionality like this needs to be tested and can’t be run without a DOM.

A recent and popular solution to this problem is to completely virtualize the DOM. JSDOM (https://github.com/jsdom/jsdom) offers a completely virtual DOM that runs without a browser or even a graphical interface right in Node.js or the browser. Unfortunately, Web Components aren’t yet supported in JSDOM, so it’s not a solution that can be used unless you picked apart and tested pieces of your Web Component without actually running it as a component.

Because of this, when testing Web Components specifically, we’ll need to fall back to actually using browsers to run the tests. Despite introducing a browser and UI into testing, we can still test discrete “units” of functionality.

Another point of blurriness comes in when thinking about functional testing. These types of tests can be thought of from the user’s perspective. When the user clicks a button, something happens that is meaningful to the user, and the output can be tested. These tests can sometimes be mixed right into unit testing, if desired, and if the browser is used to run the test, it’s that much easier to do this.

The reason to bring this up is that there are many different testing methodologies and tools. What we’ll be discussing here are tools and methods typically thought of as unit tests, or tests that a developer would typically write from the perspective of Test-Driven Development (TDD). These are tests that a developer writes as they write code. In an ideal world, a developer would create a piece of functionality and would write tests to back up that piece of functionality.

Testing is a broad subject, with many books written about its various aspects. As far as Web Components are concerned, however, I think TDD and unit tests are the most relevant to discuss, given the nuance that we must currently rely on the browser for this, though the expectation might be that a solution like JSDOM could be used.

13.2. Web Component tester

Another reason to explore this type of testing is that Google’s Polymer team created a TDD testing tool of their own explicitly for Web Components. This tool is called the Web Component Tester (WCT) and can be found at https://github.com/Polymer/tools/tree/master/packages/web-component-tester. What’s great about WCT is that a lot of things are built into it by default, and it’s really easy to get up and running.

Testing tools like these are often broken up into a few different parts. For WCT, browser automation is handled by Selenium. Browser automation simply means that the browsers you intend to host your tests in need to be automatically launched from the terminal with the HTML/JS/CSS that runs your tests, and these browsers need to report back to your terminal with the results.

The test framework—Mocha, in WCT’s case—is what you’d use as a developer to organize and write your tests. With Mocha, you’d create suites, or groups of tests in which each group is filled in with the actual singular tests. Mocha provides hooks to set things up before your tests, hooks to tear things down when your tests finish, so you can run the next test from a clean slate, and lots more functionality.

The last major piece of WCT is the assertion library—in this case, Chai. Assertion libraries are a small but central piece of any testing solution. Basically, an assertion is a question that you ask and expect the answer to be true. A plain English example would be “I expect 1 + 2 to equal 3.” That assertion could be paraphrased with Chai by writing

assert.equal(1 + 2, 3);

Of course, 1 + 2 is always 3, so this assertion would never fail. In practice, you won’t see hardcoded values (at least on both sides of the assertion). You’ll likely test that a variable or the result of a function equals another variable or another result. For example, you might have a simple function that doubles numbers. Your doubleNum(num) function could take a value and double it. To know that it works, you’d want to run a number of assertions, such as

assert.equal(doubleNum(2), 4);

More complicated functions can and do fail for various reasons, and testing is a great way to catch these cases.

Chai offers different ways to do assertions, but in a nutshell, Chai does this one thing and does it well. Figure 13.1 shows the entire WCT flow.

Figure 13.1. The WCT running flow

Installing WCT is easy:

npm install --save-dev web-component-tester

This is yet another dev dependency that we’ll want to run locally from the node_modules/bin folder. I should note, however, that because Selenium is a dependency and uses Java, one of the sharp technical reviewers of this book found that on his Windows 10 setup, running WCT wasn’t possible until he downgraded to Java 8. I have a feeling that running different versions of Java will be a moving target on different platforms as multiple dependencies are updated when new versions of WCT and Selenium are released. Ideally, you’ll have luck similar to mine and won’t even need to think about Java when installing WCT—but in case you don’t, your installed Java version is something to pay attention to.

We’ll need to run tests against some files, however, so now is the time to create a test folder with a test HTML file for each component. Figure 13.2 shows the new folder structure with new test files.

Figure 13.2. Project structure with test files

The HTML file in the new test folder will normally be called something like index.html or index.test.html. But throughout this chapter, we’ll be exploring a few different ways to test; so to be clear which is which, I’ve named this first HTML file wct-test.html. Before creating the actual tests in the file, let’s add the script to the package.json file. The following listing shows the latest package.json file after installing WCT and adding the script.

Listing 13.1. Adding WCT testing to our project’s package.json
{
 "name": "wcia",
 "version": "1.0.0",
 "dependencies": {
   "css-vars-ponyfill": "^1.16.2"
 },
 "devDependencies": {
   "@babel/cli": "^7.2.3",
   "@babel/core": "^7.2.2",
   "@babel/preset-env": "^7.2.3",
   "mocha": "^5.2.0",
   "rollup": "^1.0.2",
   "rollup-plugin-babel": "^4.2.0",
   "web-component-tester": "^6.9.2"                        1
 },
 "scripts": {
   "wcttest": "./node_modules/.bin/wct                     2
--npm chapter12and13/components/**/test/wct-test.html",
   "build-slider": "./node_modules/.bin/rollup
    chapter12and13/components/slider/src/slider.js --file
    chapter12and13/components/slider/slider.js --format umd
    --name slider -m",
   "build-coordpicker": "./node_modules/.bin/rollup
    chapter12and13/components/coordpicker/src/coordpicker.js --file
    chapter12and13/components/coordpicker/coordpicker.js --format umd
    --name coordpicker -m",
   "build-colorpicker": "./node_modules/.bin/rollup
    chapter12and13/components/colorpicker/src/colorpicker.js --file
    chapter12and13/components/colorpicker/colorpicker.js --format umd
    --name colorpicker -m",
   "build-rollup": "npm run build-slider && npm run build-coordpicker &&
    npm run build-colorpicker",
   "build-slider-ie": "./node_modules/.bin/babel
    chapter12and13/components/slider/slider.js --out-file
    chapter12and13/components/slider/slider.build.js",
   "build-coordpicker-ie": "./node_modules/.bin/babel
    chapter12and13/components/coordpicker/coordpicker.js --out-file
    chapter12and13/components/coordpicker/coordpicker.build.js",
   "build-colorpicker-ie": "./node_modules/.bin/babel
    chapter12and13/components/colorpicker/colorpicker.js --out-file
    chapter12and13/components/colorpicker/colorpicker.build.js",
   "build-ie": "npm run build-slider-ie && npm run build-coordpicker-ie &&
    npm run build-colorpicker-ie",
   "build": "npm run build-rollup && npm run build-ie"
 }
}

  • 1 WCT package
  • 2 WCT script

WCT is an extremely easy command to run. Simply run the WCT executable with a file path to one or multiple tests. For our test setup, the HTML files are always found at a specific location within each component folder. Since we want to run all the components with one command, we’ll swap the component name with a directory wildcard: components/**/test/wct-test.html. Lastly, since we’re using npm to run, WCT needs the --npm flag.

13.2.1. Writing tests

Each HTML test file will have a very familiar setup. Without tests yet, the setup in the next listing looks no different than any other HTML file. The only dependency beyond our component are the browser.js files, which provide all the features and client-side loading of WCT.

Listing 13.2. WCT test file setup
<html>
<head>
  <script src="../../../../node_modules/web-component-tester/browser.js">
  </script>                                                               1
  <script                                                                 2
       type="module"
       src="../src/slider.js">
  </script>
   <style>
       wcia-slider {
           width: 500px;                                                  3
       }
   </style>
</head>
<body>
<wcia-slider value="50"></wcia-slider>                                    4
<script>
// tests go here                                                          5
</script>
</body>
</html>

  • 1 Required WCT testing scripts
  • 2 Slider component import
  • 3 Gives the slider some width to run size-dependent tests
  • 4 The slider component
  • 5 Placeholder for tests

As we start writing tests, remember we’re being specific to our test framework and assertion library here, Mocha and Chai. Mocha actually has two different styles: TDD and Behavior-Driven (or functional style) Development (BDD). WCT defaults to TDD, which are ideally unit tests you’d write as you create your component. With that said, let’s define a group, or suite of tests for the slider component.

Listing 13.3. A start to a test suite for the slider component
suite('slider value getting/setting', function() {
   const sliderWidth = 500;                                         1
   const thumbCenterOffset = 5/2 + 3; // width/2 + left border      2
   const slider = document.body.querySelector('wcia-slider');

  • 1 Defines the width of the slider in preparation for tests
  • 2 Defines the slider center to aid in future tests

The first parameter passed to Mocha’s suite function is the name of the suite of tests. It’s really handy to be specific here. The better you name a test, the easier you’ll find it when a named test and suite reports a failure in your terminal.

Second is the function containing the tests. While we haven’t gotten to defining a single test yet, there is some light setup to do. This is a good chance to step back and think about what functionality needs testing. A slider component doesn’t do all that much, really. Given that it’s a Web Component, and we spent time to support component reflection, we should be able to set the slider’s value with an attribute or with the JS API. Beyond that, the component really only ties its visual state (the thumbnail position) to the numeric percentage value. We can test this aspect, but the position (in pixels) of the thumbnail will depend on the slider component’s size.

This is what these two variables enable. First, we specify the slider width, which has already been defined in the CSS on the HTML page. Second, we’ll define how much the slider is offset to center it in position by subtracting half its width and the left border size. Lastly, we’ll grab a reference to the slider for the tests.

We’ll label the first test “slider get initial value.” The component, as set up on the page, has a value attribute set to 50:

<wcia-slider value="50"></wcia-slider>

So, with 50% as the initial slider value, the thumbnail should appear in the center of the slider. We can assert three things in this first test, shown in the following listing.

Listing 13.4. A single slider test
test('slider get initial value', function () {
   assert.equal(slider.value, 50);                                        1
   assert.equal(                                                          2
     slider.getAttribute('value'), 50);
   assert.equal(slider.root.querySelector('.thumb').style.left, sliderWidth *
     50/100 - thumbCenterOffset + 'px');                                  3
});

  • 1 Tests that the slider value as observed by JS is 50
  • 2 Tests that the slider value as observed by attribute is 50
  • 3 Tests that the slider thumb is in the middle of the component

First, we’re checking that getting the value with JS returns 50. We also need Chai to assert that we’re getting the same value from the attribute to prove that reflection works. Next, we’ll test the slider’s position. Given the value of 50, we can calculate where the thumbnail should be given the component, thumbnail, and border size. Since we know how the slider internally works, we know that the left property of the style should be 500 * 50/100 – (5 / 2 + 3), or 244.5 pixels.

There is something very interesting to call out here. Recall back to when we learned about the Shadow DOM. There was some discussion of an “open” versus a “closed” shadow root. Remember that with a closed root, the intended functionality was that no matter how hard we tried, we’d never be able to reach into the component and work with the DOM. The open shadow root was a bit more forgiving because we could get in through the component’s shadowRoot property, knowing that this back door wasn’t the component developer’s intention. This back door comes in very handy here. If we couldn’t break through the shadow boundary of a Web Component, we couldn’t query-select the thumbnail and test it.

The next listing continues on through the remaining few tests for this component.

Listing 13.5. Slider component test suite
   suite('slider value getting/setting', function() {
       const sliderWidth = 500;
       const thumbCenterOffset = 5/2 + 3; // width/2 + left border

       const slider = document.body.querySelector('wcia-slider');

       test('slider get initial value',                            1
           function () {
               assert.equal(slider.value, 50);
               assert.equal(slider.getAttribute('value'), 50);
               assert.equal(slider.root.querySelector('.thumb').style.left,
                 sliderWidth * 50/100 - thumbCenterOffset + 'px');
       });

       test('set slider value with JS',                            2
           function () {
               slider.value = 20;
               assert.equal(slider.value, 20);
               assert.equal(slider.getAttribute('value'), 20);
               assert.equal(slider.root.querySelector('.thumb').style.left,
                 sliderWidth * 20/100 - thumbCenterOffset + 'px');
       });

       test('set slider value with attributes',                    3
           function () {
               slider.setAttribute('value', 30);
               assert.equal(slider.value, 30);
               assert.equal(slider.getAttribute('value'), 30);
               assert.equal(slider.root.querySelector('.thumb').style.left,
                 sliderWidth * 30/100 - thumbCenterOffset + 'px');
       });
});

  • 1 Tests the initial slider value
  • 2 Tests setting a new value with the JS API
  • 3 Tests setting a new value with attributes

We can now run these tests with npm run wcttest. Figure 13.3 shows an example of what you’d see in the terminal when running with a few more tests that we’ll add in a bit. Note that the passing tests are nice and green!

Figure 13.3. Passing slider component tests

It’s also helpful to show some failed tests! The practice of writing these tests as you develop forces you to start thinking of weird edge cases to test. The slider component is a simple one, but there are some easy ways to make it fail. Think of what would happen when setting the slider value to more than 100 or less than 0. Doing this makes no sense in terms of the slider’s visual display—so, ideally, we should restrict the slider with max and min values. Let’s add two more tests in the next listing to make it fail, assuming this restriction is in place.

Listing 13.6. Failing slider tests because max and min values aren’t yet implemented
test('set slider value too big', function () {
   slider.setAttribute('value', 110);                1
   assert.equal(slider.value, 100);
   assert.equal(slider.getAttribute('value'), 100);
   assert.equal(slider.root.querySelector('.thumb').style.left, sliderWidth *
     100/100 - thumbCenterOffset + 'px');
});

test('set slider value too small', function () {
   slider.setAttribute('value', -10);                2
   assert.equal(slider.value, 0);
   assert.equal(slider.getAttribute('value'), 0);
   assert.equal(slider.root.querySelector('.thumb').style.left, sliderWidth *
     0/100 - thumbCenterOffset + 'px');
});

  • 1 Slider value is over 100, so it should be coerced back to 100.
  • 2 Slider value is less than 0, so it should be coerced to 0.

With the tests failing, as figure 13.4 shows, we’ve defined some functionality that we need to implement. A good exercise for you to try later is to tweak the slider component in a way that these and the previous tests all pass.

Figure 13.4. Failing slider tests

In addition to the homework I just gave you, there are three other components to get cracking on! I’ve written some tests myself if you get stuck. If so, feel free to visit this book’s GitHub repo.

13.3. Comparing to a standard test setup with Karma

WCT is fairly nice! The setup was extremely minimal and allowed us to focus on writing tests without fumbling over complicated configurations, though a configuration could be added if there were defaults you didn’t care for. More details can be found at https://github.com/Polymer/tools/tree/master/packages/web-component-tester.

The bottom line, though, is that WCT is intended for Web Components, and bundles some key things to test them. For one, the Web Component polyfills are bundled in and automatically included, should they be needed in your HTML test fixtures. WCT also waits for your components to be ready by waiting for the browser’s WebComponentsReady event. Also provided is a helper for using <template> tags in your tests.

WCT is still new, though, and a work in progress. If it works for you, great! If it doesn’t work for you, and you’d rather use a different setup, that’s OK too. What’s great is that with modern browsers now supporting Web Components, there really aren’t any gotchas with simple Web Component tests. They just work like any other web feature.

With this in mind, let’s try swapping out test runners. We’ll replace Selenium with Karma but keep Mocha and Chai. This keeps all our tests the same and gives us all the flexibility and plugins that come with the Karma ecosystem. Figure 13.5 highlights our new test runner flow with Karma.

Figure 13.5. A new test runner flow with Karma

The downside of a Karma-based setup, however, is that we’ll need to deal with a bit of complexity to set it up. For starters, let’s install a few things with npm:

npm install --save-dev karma
npm install --save-dev mocha
npm install --save-dev chai

Mocha and Chai won’t run in Karma without a plugin to bridge the gap, so we’ll install those as well:

npm install --save-dev karma-mocha
npm install --save-dev karma-chai

Karma also needs plugins to launch browsers and run our tests:

npm install --save-dev karma-chrome-launcher
npm install --save-dev karma-firefox-launcher

There are a few other dependencies as we get rolling, but these are the basics. One last thing to do is to again install Karma, but globally, and I’ll explain why:

npm install -g karma

This global install has nothing to do with running your tests. Instead, it provides a command line utility to generate a configuration file. Running karma init from your project root after the install gives a series of prompts and questions, as figure 13.6 shows.

Figure 13.6. Karma init questions

It’s not imperative that you follow what I did exactly, because we’ll be changing some options around as we go. The good thing here is that we have a karma.conf.js baseline file to work with. The next listing shows the initial configuration.

Listing 13.7. Initial Karma config (condensed by removing blank lines and comments)
module.exports = function(config) {
 config.set({
   basePath: '',
   frameworks: ['mocha'],
   files: [],
   exclude: [],
   preprocessors: {},
   reporters: ['progress'],
   port: 9876,
   colors: true,
   logLevel: config.LOG_INFO,
   autoWatch: false,
   browsers: ['Chrome', 'Firefox'],
   singleRun: false,
   concurrency: Infinity
 })
}

The very first thing to worry about is the use of modules in our Web Components. WCT allowed us to ignore this part of setup, but when we roll a testing setup ourselves, it’s our problem now. Modules and imports don’t work easily because Node.js is working for us behind the scenes to handle a lot of test running. Node itself doesn’t support modules yet. So, we’ll need to run a “preprocessing” step before components get loaded on the page and run tests.

Rollup was covered in the last chapter, so let’s use it again! As I write this, Rollup has just moved beyond a 1.0 release. Ordinarily, I’d recommend installing karma-rollup-preprocessor. Unfortunately, we’re at an inconvenient gap in support where this module doesn’t support the latest Rollup version. This can happen every so often when packages fall out of sync with each other, especially with so many working parts. Luckily, I was able to hunt around and find that someone forked this original project and made something that does work with the latest version. Perhaps soon, we won’t have to use this fork, but until then, you can install

npm install --save-dev @metahub/karma-rollup-preprocessor

Because of the @metahub package namespace, the default loading of any plugin with a name starting with “karma-” doesn’t work here. As annoying as this might be, it does walk us through a piece of nonstandard setup, which is par for the course when working on a Karma configuration from scratch. With this in mind, a plugins entry needs to be added to the config file:

plugins: ['@metahub/karma-rollup-preprocessor', 'karma-*']

As we’re overriding defaults here, karma-* needs to be added back into the list as well. We’ll also add an entry to the preprocessors list to map the JS files to Rollup:

preprocessors: {
   './chapter12and13/components/**/*.js': ['rollup']
}

Here, we’re likely rolling up more than necessary, as there are multiple JS files but only one JS entry point for each component. The path could be more exact, but I’m not too worried about shaving a few microseconds off of the preprocessing time.

Rollup, or plugins in general, needs to be configured as well. The following listing shows a Rollup configuration that will work for us in the karma.conf.js file.

Listing 13.8. Rollup plugin configuration in Karma
rollupPreprocessor: {
 options: {
     output: {
         // To include inlined sourcemaps as data URIs
         sourcemap: true,                               1
         format: 'iife',                                2
         name: 'testing'                                3
     }
 }
},

  • 1 Turns on sourcemaps
  • 2 Bundles format
  • 3 Bundles package name

Source maps might not sound necessary here, but that’s only if your tests pass. If they fail and need debugging, you’ll really want to know what line failed in your original, nonbundled source. Bundling as IIFE literally means “immediately invoked function expressions.” Do we want our bundle to immediately invoke after loading and create the Web Component definition right away? Yes, please. This works great for testing and is inclusive of how the component was built previously with Rollup. With UMD-style bundling before, this option and more were allowed (hence the “universal” part of the name). The bundle name doesn’t matter too much here, but it’s required, so “testing” works fine.

Two last simple adds are Chai to the frameworks we need to use,

frameworks: ['mocha', 'chai'],

and also a configuration to tell Mocha to use TDD-style testing:

client: {  mocha: {  ui: 'tdd'  } }

Now, as we get back to not-so-simple stuff, there needs to be a plan for what files Karma will serve. With WCT, it was really nice that tests could run from an HTML file. We’ll loop back around to that in a bit, but as is, Karma only sort of supports HTML tests like this. The problem is that Karma loads HTML files using HTML Imports. Since Chrome is the only browser that supports this deprecated feature (and soon won’t), it’s also the only browser that could run our HTML test pages. With this in mind, if no other plugins are used, we’ll need tests as JS files, and the file patterns to serve will look like this:

files: [
 './chapter12and13/components/**/test/karma-test.js',
 './chapter12and13/components/**/*.js'
],

This file pattern serves all component JS files and also component tests named karma-test.js, which we still need to create. Even though we’re using a different runner, we’re still using Mocha and Chai, so all the tests previously made can be copied over. There is just a bit more setup in the next listing with a JS-only test file, and that is to programmatically attach the component scripts, create the component, and add it all to the page body.

Listing 13.9. A JS-only test file created in the test folder for each component
suite('slider value getting/setting', function() {
   const sliderWidth = 500;
   const thumbCenterOffset = 5/2 + 3; // width/2 + left border
   const container = document.createElement('div');
   container.innerHTML = `<script type="module" src="../src/slider.js">
                          </script>
                          <wcia-slider style="width: ${sliderWidth}px"
                           value="50"></wcia-slider>`;                   1
   document.body.appendChild(container);                                 2
   const slider = container.querySelector('wcia-slider');

   test('slider get initial value', function () {
       assert.equal(slider.value, 50);
       assert.equal(slider.getAttribute('value'), 50);
       assert.equal(slider.root.querySelector('.thumb').style.left, sliderWidth * 50/100 - thumbCenterOffset + 'px');
   });
});

  • 1 Puts the component and component script inside a container
  • 2 Adds everything to the page body for testing

You can refer to this book’s GitHub repo to see all of these new JS tests in place for all the components, but listing 13.9 does highlight the only real differences using the slider component as an example.

Now is a great time to try running the tests! As before, a script called test added to the package.json file would be a more apt name, but since we’re giving a few different types of tests a spin, it can be called karmatest:

"karmatest": "./node_modules/karma/bin/karma start karma.conf.js"

This script simply tells Karma to start test running against the config file we just created. We need to flip one thing in the configuration to get it running normally. Before we do, running npm start karmatest kicks off the browsers in the browsers entry inside the Karma configuration and shows figure 13.7 while the browser is paused in place after running the tests.

Figure 13.7. Karma test runner page

The reason to pause here is that it gives us an opportunity to press the Debug button and see the tests running in context. We can open up the browser’s dev tools like normal and see test output, look at the elements on the page, and debug any errors shown. Figure 13.8 shows this debug mode, though, again, it’s really just the browser with dev tools open.

Figure 13.8. Karma debug page

Assuming everything works, and we don’t need to debug, it’s desirable to have Karma fire up the browsers, run the test, and then quit everything. To do this, we just need to flip the singleRun entry in the Karma configuration from false to true:

singleRun: true

Even better, we have the option to not see the browsers pop up on the screen at all if the “headless” versions are supported by the Karma launchers like Chrome and Firefox are. Note that it’s not Karma alone that supports this. Both normally installed versions of these browsers offer a headless mode, and the Karma launchers are simply tapping into this:

browsers: ['FirefoxHeadless', 'ChromeHeadless'],

The next listing reviews all of the options we changed in the Karma configuration to make the Karma/Mocha/Chai tests possible.

Listing 13.10. Final Karma configuration
module.exports = function(config) {
 config.set({
   basePath: '',
   plugins: [                                               1
       '@metahub/karma-rollup-preprocessor',
       'karma-*'],
   frameworks: ['mocha', 'chai'],                           2
   files: [                                                 3
     './chapter12and13/components/**/test/karma-test.js',
     './chapter12and13/components/**/*.js'
   ],
   exclude: [],

   preprocessors: {                                         4
       './chapter12and13/components/**/*.js': ['rollup']
   },
   rollupPreprocessor: {
     options: {
         output: {
             sourcemap: true,
             format: 'iife',
             name: 'testing'

         }
     }
   },
   reporters: ['progress'],
   port: 9876,
   colors: true,
   logLevel: config.LOG_INFO,
   autoWatch: false,
   browsers: [                                              5
        'FirefoxHeadless',
        'ChromeHeadless'],
   singleRun: true,                                         6
   concurrency: Infinity,
   client: {                                                7
       mocha: {
           ui: 'tdd'
       }
   }
 })
};

  • 1 Added plugins for Rollup and re-added default karma-*
  • 2 Added Chai
  • 3 Added specific files for our setup
  • 4 Added Rollup preprocessor and configuration
  • 5 Changed to headless versions of browsers
  • 6 Changed to true
  • 7 Added TDD testing

Now hopefully when running npm start karmatest, you’ll see all green—successful output in your terminal! There were quite a few moving parts to get right here, and it takes a bit of trial and error when configuring it all by yourself; but the benefit over WCT is that you have a lot more control and a significant number of compatible plugins with a testing setup like this that’s been around for a while.

The only part that’s a little sad in this is the lack of being able to use an HTML test file like in WCT. Personally, that’s my favorite piece of the WCT ecosystem. Fortunately, there’s a Web Component-specific Karma plugin!

13.3.1. Karma Web Components

The karma-web-components plugin does a few things, but mostly it allows us to use HTML test files again like WCT does. We happen to have done well with load-timing issues in the tests run so far; but karma-web-components also listens for your browser’s WebComponentsReady event before tests start, just to ensure everything is in place for tests to succeed.

The first thing to do is install the plugin:

npm install --save-dev karma-web-components

Next, we can add on to each component’s test folder an extra file called karma-wc-test.html. For the slider component, the next listing shows what’s inside.

Listing 13.11. HTML test file for use by the karma-web-components plugin
<html>
<head>
   <script                                                                   1
       type="module"
       src="../src/slider.js">
   </script>
   <script src="../../../../node_modules/karma-web-components/framework.js"> 2
   </script>

   <style>
       wcia-slider {
           width: 500px;
       }
   </style>
</head>
<body>
<wcia-slider value="50"></wcia-slider>                                       3
<script>
// Same exact tests and test suite we've had in the others
</script>
</body>
</html>

  • 1 Component module import
  • 2 Framework provided by karma-web-components
  • 3 Slider component on page

Like WCT, the karma-web-components plugin needs to load client-side. But that one script file is the only thing that needs to change on this page versus the WCT test page. It’s a different library to load, but the entire test setup can remain the same. Figure 13.9 shows the updated test runner flow. It looks a lot like the WCT setup again.

Figure 13.9. Karma Web Components test runner flow

Back in the Karma configuration file, we’ll need to add the plugin to our existing list:

frameworks: ['mocha', 'chai', 'web-components'],

The only other difference is that the files will need to be served in a slightly different way, as the following listing shows.

Listing 13.12. File-serving Karma configuration to use karma-web-components
files: [
 './chapter12and13/components/**/src/*.js',
 './node_modules/karma-web-components/framework.js',
   {

       pattern: './chapter12and13/components/**/test/karma-wc-test.html',
       watched: true,
       included: false
   }
],

Of course, the existing Web Components files need to still be served. Also required is the karma-web-components client-side framework. Lastly, the HTML file containing the tests needs to be served, but we’ll also need to adjust a couple of settings. The included flag should be false, so the HTML files aren’t automatically loaded in the browser. Previous examples have been a bit lazy and inexact in including files that don’t need to be loaded before. The difference here is that, if it’s included, your test run will break.

13.3.2. Multiple tests in the same project

That last karma-web-components example meant modifying the Karma configuration slightly. Instead of modifying the default one, I wanted to leave both Karma configurations in place. In this book’s GitHub repo, you’ll be able to run WCT, the Karma test runner, and the Karma test runner with karma-web-components, all from the same project.

It’s a lot to cover, and while one of these would suffice, there’s no single standard way to set testing up. Every project has different needs and will likely require a bit of work to tweak everything to your liking. Covering a few different methods in this chapter will hopefully get your setup far enough along that you’ll at least be able to research any tweaks you’ll need to make.

That’s why, in my repo, the karma-web-components setup is in a file called karma.conf.webcomponents.js. The npm script to run it has yet another name, while pointing to this new configuration in the parameters:

"karma-wc-test":
  "./node_modules/karma/bin/karma start karma.conf.webcomponents.js",

You won’t have all these similar tests in the same project, but you could have different types of tests running different things. For example, for pure JS unit tests only, with no need to rely on the browser, I like to use Tape and JSDOM. And I might have a Karma/Mocha/Chai setup for the tests that I do need to run in the browser. My point is that while I’ve gone out of my way to include redundant tests in this project, the notion of having several separate test runs in one project is fairly normal.

13.3.3. A note on Safari

There is one last thing to call out here. Windows developers won’t be able to run tests on Safari anyway, but macOS users should expect to. In my examples, I did not install karma-launcher-safari. Typically, “Safari” is another browser you can add to the Karma configuration file. Currently, this Karma launcher is a bit broken when running on Apple’s newest OS, Mojave. Safari will launch, but will require user intervention to give permission to load Karma’s test harness. This is an open issue as I write this chapter (https://github.com/karma-runner/karma-safari-launcher/issues/29). In this book’s GitHub repo, I’m now using karma-safarinative-launcher instead of karma-safari-launcher as a workaround. To use this custom launcher, the only thing necessary to add to the karma.conf.js file is the following:

customLaunchers: {
  Safari: {
      base: 'SafariNative'

  }
},

With this in place, you can now test with Safari in your karma.conf.js browsers list:

browsers: ['FirefoxHeadless', 'ChromeHeadless', 'Safari'],

Ideally, though, this workaround will not be needed as the issue gets resolved in the original launcher package. Until then, we can roll with this!

Summary

In this chapter, you learned

  • What the different styles of testing are and the benefits of TDD when writing components
  • Three different ways of test running to show the diversity of options available
  • Ways to think of your code in units and develop tests for each unit
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset