Deliverables - Hybrid Assignments

Static Testing

If you have not done so already, please read the JavaScript Testing Backgrounder in the hybrid activities overview.

Due Date

Due before 6:00 pm on Friday Sept 24, 2021.

Back to Hybrid main page

# Background

Static testing is the base of the testing trophy. It is all about catching potential errors and ensuring consistent formatting and code quality – while you are writing it. The core goals are:

  • Increase readability through consistent code style and formatting
  • Catch common mistakes and possible logic errors
  • Reduce code complexity
  • Catch variable type mismatches
  • Reduce security risks
  • Ensure that third-party dependencies are updated

In the JavaScript world, there are many tools that try to address one or more of these objectives. Most of these can be configured to run before you make a commit (using git pre-commit hooks (opens new window)), so that you always commit clean code. It is also common to run these checks before you deploy to production with CI/CD processes.

To gain a good basic understanding of how client-side hooks work, read these two pages - Atlassian Git Hooks Tutorial (opens new window) and Digital Ocean Git Hooks Tutorial (opens new window)

Many of these tools integrate directly with your code editor or IDE. For example integrating ESLint (opens new window) with VS Code means that you will get hints about potential errors as you type your code.

Adding JSDoc (opens new window) style code comments will enhance readability of your code, but also enable VS Code to display more accurate type hint messages and alert you to potential mismatches.

The Prettier (opens new window) plugin will automatically format your code on save. It follows an opinionated list of rules for consistent formatting. These rules can be tweaked to work with your preferred style guide. Not only does this vastly improve the readability of your code and guide you in following industry best practices, but if everyone on your team is using the same formatting rules, it will avoid many unnecessary git merge conflicts.

Prettier-Standard

This is my preferred VS Code plugin. It combines the convenience of Prettier and enforces the Standard JS (opens new window) style guide rules.

# Assignment Objective

During this assignment you will install and practice working with each of these tools. Learn them early and use them often to build good habits and ship clean code.

# Tools

  • JS Docs
  • ES Lint
  • Standard JS
  • Prettier

# Instructions

# Setup

  • create a new project folder called h1-static-testing
  • create an index.html file in the project folder with this content
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>H1 - Static Testing</title>
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
  • create an index.js file
echo "'use strict'" >> index.js
1
  • add a .gitignore file with this content
.DS_Store
node_modules
dist
1
2
3
  • initialize git in the project folder
git init
1
  • initialize NPM in the project folder
npm init --yes
1
  • stage the initial files and make a first git commit
git add -A
git commit -m "initial commit"
1
2

# Sample JavaScript

This starting JavaScript file has several common small errors. These errors are a little harder to find because it is not formatted in a consistent manner. Type this code yourself, adding it to the index.js file. Leave any errors that you spot as they are.

screenshot of source code

# Intro to ESLint

Let's look at how ESLint (opens new window) can help identify these problems. Navigate to the Demo page on the ESLint website. This demonstration tool will let you see how different configurations of ESLint will see your code.

Copy and past the sample code above into the ESLint demo page. It will parse the code and then show a list of potential errors on the right-hand side.

On the first run, it will likely show you 1:9 - Parsing error: Unexpected token foo. This is because the default settings use the ES5 language spec and it doesn't understand let. Click on the Rules Configuration heading to expand the settings panel, and change the ECMA Version to ES2020 and enable the browser environment.

Now you should see a list of 6 probable errors, each with a short description and a link to the full documentation for the related ESLint rule.

If you scroll further down the page, you can see all of the possible rules that you can choose to have ESLint check for you. Once you have chosen all the rules that you want to consistently apply in your project, you can click the purple download link at the bottom of the page to save the configuration in an .eslintrc.json file.

We could use that configuration file to run ESLint right in your project folder.

# Install ESLint

ESLint is highly configurable, and has an overwhelming number of plugins. It is now a recommended best practice to use ESLint to catch language rule bugs, but not to enforce styling. We will use another tool for that.

OK, add ESLint to your project folder as a development dependency.

Do not install ESLint globally

The plugins and configuration rules will often be different from project to project. So, it is recommended that you install ESLint locally on a per project basis.

npm install eslint --save-dev
1

Now initialize the configuration file. Remember that npx can load a node package into memory and run it. If you have it installed in node_modules, then that version will be used. If it is not installed locally then npx will download the package into memory and run it, without actually adding it to node_modules.

npx eslint --init
1

This will ask you several configuration questions. Use the following answers.

Question Choice
How you want to use ESLint? To check syntax and find problems
What type of modules does your project use? JavaScript modules (import/export)
TypeScript? No
Where does your code run? Browser and Node
Format for config file? JSON

This will generate a new .eslintrc.json configuration file at the top level of your project.

{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": "eslint:recommended",
  "parserOptions": {
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "rules": {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Notice that the rules object above is empty. This configuration is setup to follow a pre-packaged set of rules called eslint:recommended. There are many similar rule sets depending on the type of project that you are working on, e.g. plugin:react/recommended - we don't need this for today.

Using npx, you can now run ESLint to check the sample file.

npx eslint index.js
1

You should see output similar to this.

/Users/rlm/Code/algonquin/mad9135/f20/02-hybrid/01-static-testing/index.js
   3:11  error  'foo' is assigned a value but never used  no-unused-vars
   3:15  error  'bar' is not defined                      no-undef
   4:1   error  'baz' is not defined                      no-undef
   5:16  error  'getMaxNum' is defined but never used     no-unused-vars
  12:53  error  'baz' is not defined                      no-undef
  14:13  error  'getMaxnum' is not defined                no-undef

✖ 6 problems (6 errors, 0 warnings)

1
2
3
4
5
6
7
8
9
10

This is very informative, but it would be a little inconvenient if we had to manually check all of our files every time we make a change. Fortunately, there are a couple more pieces to this puzzle.

# Install ESLint extension for VS Code

Go to the extensions tab in VS Code and search for eslint. The first one on the list is likely the one you are looking for - author, Dirk Baeumer. It has over 10 million downloads.

Once this extension is installed and enabled, go back and look at the index.js file. It should now have red squiggly underlines for the places that ESLint has found probable errors. Cool! Now you can see potential issues right away while you are creating your code instead of finding them the hard way when your app crashes.

You can review and fix these errors two ways:

  1. Hover the cursor over the red squiggles and VS Code will pop-up an information box that has the same ESLint message that you saw in the terminal.

  2. Open the "problems" tab of the bottom panel (where the embedded terminal is displayed). It will show the full list of ESLint errors.

TASK 1

Take a screen shot of VS Code showing the index.js file with both the inline red squiggles and the "problems" list in the bottom panel.

# Prettier and Standard JS

Fixing the problems highlighted by ESLint would be a lot easier if the code followed a standard formatting style guide. Fortunately, this is a pretty easy fix.

# Style Guides

Every team you work with will likely have their own preferred JavaScript style guide (opens new window) to follow. By using auto-formatting tools, you don't have to worry about remembering all of the rules for any given project.

If you are working on your own projects, there are many ready made "rule sets" that you can adopt:

Both Airbnb and Standard JS are great. Standard JS is used by companies like node.js, NPM, GitHub, Express, mongoDB, and many many more. Kent C Dodds and lots of other well-known companies use Airbnb.

# Prettier

In the last few years, Prettier (opens new window) has become the defacto standard tool for auto formatting web app code (JavaScript, JSX, TypeScript, CSS, Less, SCSS, HTML, etc.). It is an opinionated code formatter, with few configurable options, and has plugins for the most commonly used code editors.

Once installed, it will automatically format your code every time you hit [save] and that will be one less silly thing to distract your team during code reviews.

The OSS project prettier-standard (opens new window) combines the goodness of Prettier with the Standard JS rules.

# Install prettier-standard
npm install --save-dev prettier-standard
1

You can now run it to clean up that messy index.js file.

npx prettier-standard --format
1

It can also run the ESLint rules after formatting, by using the --lint flag.

npx prettier-standard --lint
1

Of course, you would really rather that all of this happened automatically in VS Code. So ...

TASK 2 - INSTALL Prettier-Standard

Install the VS Code Extension for Prettier-Standard (opens new window).

Make sure that the VS Code setting editor.formatOnSave is enabled.

Take a screenshot showing the extension is installed and enabled.

# Clean up those linting errors

Now that the code has been formatted and is more readable, it is time to fix those linter errors.

  1. Line 15 has a typo in the name of the function. Fixing that also clears the warning about the function getMaxNum being defined but never called.

  2. bar is not properly declared. Add let bar on line 2.

  3. There are two warnings about baz not being defined. It is spelled wrong. Change baz to bar on lines 4 and 13.

  4. foo is assigned but never uses. You can delete line 3.

  5. bar is never reassigned, so redeclare it with const instead of let and combine the assignment ... const bar = '7'

The cleaned up code should now look like this.

'use strict';

const bar = '7';

function getMaxNum(numbers) {
  let max = numbers[0];
  numbers.forEach((num) => {
    if (num > max) max = num;
  });
  return max;
}

const sourceArray = [10, 12, 8, 4, 6, 15, 0, -3, 7, bar];

console.log(getMaxNum(sourceArray));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Type checking with JSDoc

JSDoc (opens new window) is a standard for annotating your JavaScript code with comment blocks - sometimes called "doc blocks". Using standardized block tags like @param (opens new window) for function parameters and @returns (opens new window) for return values, yields significant benefits:

  1. Improves readability of the code
  2. Enables static type checking in VS Code
  3. Enables the automatic generation of a documentation website

Update the earlier code example, index.js with a doc block for the getMaxNum function.

/**
 * Find the highest value in an array of integers
 * @param {number[]} numbers
 * @returns {number} Maximum value
 */
function getMaxNum(numbers) {
  let max = numbers[0];
  numbers.forEach((num) => {
    if (num > max) max = num;
  });
  return max;
}
1
2
3
4
5
6
7
8
9
10
11
12

Now hover over the call to the function in the last line of the script — VS Code pops up an intellisense code usage hint for your custom function.

Add a @type (opens new window) comment above the declaration of the sourceArray to remind your future self that this variable should hold an array of numbers.

/** @type {number[]} Values should be integers */
const sourceArray = [10, 12, 8, 4, 6, 15, 0, -3, 7, bar];
1
2

This is already very helpful, but it can be even better!

One extra comment tag - //@ts-check - at the top of the file will tell VS Code to use the built-in TypeScript compiler + the JSDoc annotations to flag potential type mismatches.

You should now see a red squiggly line under the variable bar in the sourceArray. Hover over it and see VS Code is warning that ...

const bar: "7"
--------------
Type 'string' is not assignable to type 'number'.
1
2
3

This is huge! Catching this kind of error while writing your code will save you endless hours of debugging a misbehaving application with runtime errors.

TASK 3 - Add doc blocks

Add the relevant JSDoc comments to this example module, including a @module block with @example usage instructions. Assume that a user object includes: firstName, lastName, email, password, and lastLoginDate.

Submit the annotated code.

const baseURL = 'https://madd.app/api/v1/';
const apiKey = process.env.MADD_API_KEY;

async function fetchData(resource, method = 'GET', payload) {
  try {
    const response = await fetch(`${baseURL}${resource}`, {
      method,
      headers: {
        'Content-Type': 'application/json',
        'X-MADD-API-KEY': apiKey,
      },
      body: JSON.stringify(payload),
    });

    if (!response.ok) throw Error(response.statusText);

    return response.json();
  } catch (error) {
    return Promise.reject(new Error('Problem connecting to server'));
  }
}

export async function getAllUsers(filters) {
  let resourceURL = '/users/';

  // TODO: extract filter criteria from filters object and
  //       construct the querystring params.
  resourceURL += '';
  return fetchData(resourceURL);
}

export async function getUserById(id) {
  return fetchData(`/users/${id}`);
}

export async function createUser(user) {
  return fetchData('/users', 'POST', user);
}

export async function updateUser(user) {
  return fetchData(`/users/${user.id}`, 'PATCH', user);
}

export async function deleteUser(user) {
  return fetchData(`/users/${user.id}`, 'DELETE', user);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# Submission

Please submit all three files from the above tasks in a single ZIP file on Brightspace.

Last Updated: : 9/21/2021, 9:29:37 AM