Line Breaking in JavaScript

Question
What should you do when a line of code exceeds the max character length of 80?

Answer
You want to break (split) the line into two. The recommended best practice is to break after an operator and indent the next line two levels.

Comments
It’s important to break the line after an operator because JavaScript’s built-in automatic semicolon insertion mechanism won’t close the statement. Be sure to use the same line-breaking pattern for statements as well. There is an exception to the rule. When assigning a value to a variable, wrap lines directly under the first part of the assignment. See the examples below.

Examples

// Correct. Break after an operator and indent two levels.
doWork(argumentOne, argumentTwo, argumentThree, argumentFour, argumentFive, 
        argumentSix);

// Incorrect. Break before operator and indent only one level.
doWork(argumentOne, argumentTwo, argumentThree, argumentFour, argumentFive
    , argumentSix);

// Exception. Assigning a value to a variable.
let result = aValue + anotherValue + anAdditionalValue + yetAnotherValue + 
             someOtherValue;

Statement Termination in JavaScript

Question
Is it okay to omit semicolons when terminating statements?

Answer
Yes, but it’s not recommended.

Comments
Why? It’s easy to introduce errors when relying on a complete understanding of JavaScript’s automatic semicolon insertion (ASI) mechanism. In many cases, developers assume a semicolon will be inserted when it will not.

Example

// Valid but not recommended.
let name = "Jason"
function sayHello() {
    console.log("Hello " + name)    
}
sayHello()

// Recommended.
let name = "Jason";
function sayHello() {
    console.log("Hello " + name);
}
sayHello();

Indentation Levels for JavaScript

Question
What’s the recommended indentation level for JavaScript code?

Answer
There’s no universal agreement, but my recommendation is four spaces per indentation level.

Comments
Here are the indentation rules from notable JavaScript style guides:

• Crockford’s Code Conventions for JavaScript recommends 4 spaces for indents.
• The Dojo Style Guide recommends tabs (set to 4 spaces) for indents.
• The Google JavaScript Style Guide recommends two spaces for indents.
• The jQuery Style Guide recommends indentation with tabs.

Whichever style you adopt, be sure not to mix them.

Tools for JS Style Guidelines

Question
What are some useful tools for enforcing JavaScript style guidelines?

Answer
Two are JSLint and JSHint.

Comments
JSLint is a tool written by famed Douglas Crockford. It’s a tool that detects potential errors and stylistic concerns in your code. JSHint is a fork of JSLint, and it provides a less strict, more customizable experience. Both are excellent options, and integrating one or both into your development process is a great way to enforce JS code conventions. Additionally, Crockford has provided rules on JavaScript style in the following documents:
Part One deals with syntax and basic patterns.
Part Two deals with language idioms.
Code Conventions for the JavaScript Language contains points from the first two, with a small number of style guidelines.

Add Randomness to Automated Tests

Question
How should you automate cases that exercise functionality in a variety of ways?

Answer
Write several workflow-based functions or methods and randomly inject them in your tests.

Comments
It’s important to add an element of randomness in your automated tests. This holds true for both data and operations. For example, use data-driven techniques to switch up the data used by tests.

Example
The example below demonstrates a data-driven approach to testing a login feature.

import { t } from 'testcafe';

fixture `login`
    .page `www.example.com`;

const testCases = [
    {
        name:     'Valid User',
        email:    'valid@example.fake',
        password: 'pass'
    },
    {
        name:     'Invalid User',
        email:    'invalid@example.fake',
        password: 'fail'
    }
];

async function performLogin (email, password) {
    await t
        .typeText('#email', email)
        .typeText('#password', password)
        .click('#submit');
}

for (const user of testCases) {
    test('test ' + user.name, async t => {
        await performLogin(user.email, user.password);

        // Confirm valid user can login.
        await t.expect('#username').eql('Valid User');

        // Confirm invalid user can't login.
        ...
    });
}

Using Static Code Analyzers

Question
What can be done to check for common programming errors and standards violations in automation code?

Answer
Use static analysis tools to analyze the code. For example, jslint for the JavaScript language.

Comments
Static analysis tools help identify common syntax errors and standards violations. Some even raise warnings about poor programming style. For example, functions that are too long or take too many arguments.

Use Coding Standards for Test Code

Question
Should you adhere to coding standards when writing test automation code?

Answer
In short, yes. Follow coding standards for the language and technology used. Do this even if the quality of test automation code isn’t expected to be at the level of application code.

Example
The example below uses the fantastic tool TestCafe.

import { Selector } from 'testcafe';

fixture `example`
    .page `https://google.com`;

// Incorrect. An obscure way to name a selector.
const txtSearch = Selector('#q');

// Correct. Selectors have meaningful names.
const searchField = Selector('#q');
const pageTitle = Selector('title');

test('User can search Google', async t => {
    await t
        .typeText(searchField, 'Jason Riley')
        .expect(pageTitle.innerText).contains('Jason Riley');
});

Using the let Statement to Assign Variables

Problem
You want a variable’s life to be even shorter than the functional scope.

Solution
Use the let keyword when declaring and initializing variables in “block scope”. Block scope is delimited by curly braces {code here}. Each new set of curly braces defines a new block scope.

Examples

"use strict";

// aVariable is declared and assigned the string value Hello.
let aVariable = "Hello";
{
    // aSecondVariable is declared and assigned the string value Robot in block scope.
    let aSecondVariable = "Robot";
    // This prints Robot to the console.
    console.log(aSecondVariable);
}
if (aVariable === "Hello") {
    // A new variable named aVariable has been declared in this block.
    let aVariable = "Aloha";
    // Prints Aloha, not Hello, to the console due to the variable's scope.
    console.log(aVariable);
}
// This statement causes a ReferenceError, because aSecondVariable is out of scope.
console.log(aSecondVariable);
// This statement prints Hello.
console.log(aVariable);

Comments
The let keyword is an ECMAScript 6 (the new JavaScript standard) feature. Older browsers may not support its use.

Resolving Reference Errors and Variable Scope

Problem
You want to know how to troubleshoot the following scope-related errors that may occur at runtime.

ReferenceError: variableName is not defined
ReferenceError: assignment to undeclared variable
ReferenceError: Can't find variable: variableName

Solution
JavaScript uses variable scoping. That is, when a variable is declared, it is given scope. Variables can only be used in the functions they’re declared in. As a result, variables with global scope aren’t contained in any function. To address these potential errors, be sure not to access variables outside their scope. If you need access to a variable outside its enclosing function, the solution is to move it out of the function to a higher scope.

Examples

// Strict mode enabled.
"use strict";

// A function defined with a variable wrapped in it.
function doSomething() {
    var aVariable = 5;
}
// This causes a ReferenceError because the variable is only within the function's scope.
console.log(aVariable);

// Calling the function and then trying to access the variable fails for the same reason.
doSomething();
console.log(aVariable);

// This works since anotherVariable is global in scope and not restricted to the body of the function.
var anotherVariable = "MTV Generation";
function doSomethingElse() {
    anotherVariable = "Millennials";
}
console.log(anotherVariable);

// Prints Millennials because the function that modifies the variable was called first.
doSomethingElse();
console.log(anotherVariable);

// Prints false because yetAnotherVariable is defined globally, outside the function's scope.
var yetAnotherVariable = false;
function turnTruthy() {
    var yetAnotherVariable = true;
    return(yetAnotherVariable);
}
console.log(yetAnotherVariable);

// This still prints false, because the function contains its own variable with the same name. It doesn't modify the global one.
turnTruthy();
console.log(yetAnotherVariable);