Computed Property Names in JavaScript

Computed Property Names is an ES6 feature which allows the names of object properties in JavaScript object literal notation to be determined dynamically, i.e. computed.

JavaScript objects are really dictionaries, so it was always possible to dynamically create a string and use it as a key with the syntax object[‘property’] = value.

However, ES6 Computed Property Names allow us to use dynamically generated names within object literals. Example:

const myPropertyName = 'c'

const myObject = {
  a: 5,
  b: 10,
  [myPropertyName]: 15
} 

console.log(myObject.c) // prints 15

To stress that expressions can be used directly as computed property names, another example:

const fieldNumber = 3

const myObject = {
  field1: 5,
  field2: 10,
  ['field' + fieldNumber]: 15
}

console.log(myObject.field3) // prints 15

This can be very handy.

One more variation is to use template literals (string interpolation) for the computed property names — note that this still requires the square bracket syntax, however:

const fieldNumber = 3

const myObject = {
  field1: 5,
  field2: 10,
  [`field${fieldNumber}`]: 15
}

console.log(myObject.field3) // prints 15

 

Access the Clipboard Through the Terminal in macOS

macOS provides a very handy way to share data between graphical (GUI) applications and command-line (terminal) tools.

This is especially useful for working with APIs using JSON (or XML) which require large payloads.

The two binary commands are pbcopy and pbpaste: short for Pasteboard Copy and Pasteboard Paste respectively (the macOS term for Clipboard).

  • pbcopy takes the standard input and copies it to the pasteboard
  • pbpaste takes the pasteboard data and copies it to the standard output

Suppose we have a sample payload in file.json. We can send its contents to the clipboard by piping the data to pbcopy:

$ cat file.json | pbcopy

Now, we simply press Command+v in a GUI app like Postman or Insomnia.

In reverse, we can press Command+c in a GUI app and then in the terminal:

$ pbpaste
{
  "data": "test"
}

Or, redirect the standard output to a file:

$ pbpaste > file.json

Or, pipe the data straight into a another command, like the super-handy JSON viewing tool fx:

$ pbpaste | fx

Note that these commands are macOS specific; they do not exist in Linux.

Variations and Gotchas of the Arrow Function Syntax in JavaScript

ES6 introduced into JavaScript the extremely useful syntax for defining functions known as arrow functions. This refers to the notation:

argument => result

 

Arrow function notation can be used for both named and unnamed (anonymous) functions. Arrow functions allow for very compact definitions.

Example: define the successor function:

const f = ( x => x + 1 )

The parentheses around the function can be dropped:

const f = x => x + 1

It is slightly unclear when seen for the first time but soon becomes very natural.

Note that the above does not require a return; the expression on the right hand side of the arrow is the returned value.

If more than one statement is needed to define the function, braces must be introduced. The above example can also be written as:

const f = x => {
  const result = x + 1
  return result
}

Note that the following is problematic:

const f = x => { x + 1 } // does not return

This is very important: if braces are used, a return statement is necessary.

In short, if braces are present, a return statement is needed; if more than one statement is used in the function, braces must be used.

Another valid form:

const f = x => { 
  return x + 1
}

The argument list can be made explicit with parentheses:

const f = (x) => {
  return x + 1
}

This is required for functions of  multiple arguments, as in the following example:

const sum = (x, y) => x + y

The parentheses around the argument list cannot be dropped in this case.

A function with zero arguments also requires the parentheses around the (empty) argument list as follows:

const f = () => value

 

Use git grep to search for strings across all branches

Git includes a grep command to search through commits to a repo as well as the local files in the repo directory: git grep. Sometimes it is useful to search for a string throughout an entire repo, e.g. to find where an error message is produced.

By itself, git grep searches the currently checked out branch and local files for a regular expression. Sometimes the text we want to find is on another branch, which may actually be deployed and running in some environment. Therefore, it is useful to search through all branches of a repository to be certain where some string of interest is generated, a file is written, etc.

To search through all branches of a git repo for a string, use:

$ git grep str $(git rev-list --all)

To search through all branches ignoring string case:

$ git grep -i str $(git rev-list --all)

This can also be done with regular expressions:

$ git grep -i RegEx $(git rev-list --all)

Jest: Expect a String Result to be Numeric

JavaScript unit tests in Jest may need to assert that a string result from a function is numeric, i.e. a string containing only numbers.

Since we do not have a convenient matcher like toBeNumeric(), none of the ways currently looks quite perfect, but several possible ways are below, some more readable than others.

First, suppose we have a simple function to return a numeric string:

func.js:

const func = () => {
  return "12345"
}

module.exports = func

Then our unit test in the first form: using toBeNaN(), negated, combined together with the Number constructor:

func.spec.js:

const func = require('./func')

describe('numeric string result', () => {
  it('should be numeric', () => {
    expect(Number(func())).not.toBeNaN() // numeric
  })
})

 

The above is somewhat clear, but the following may be more readable to some:

const func = require('./func')

describe('numeric string result', () => {
  it('should be numeric', () => {
    expect(Number.isNaN(func())).toBe(false)
  })
})

 

Another way is to use a Regular Expression (regex) to expect a numeric string, however, this is most readable for integers:

const func = require('./func')

describe('numeric string result', () => {
  it('should be numeric', () => {
    expect(func()).toMatch(/[0-9]+/) // string of integers
  })
})

The regex would become more complex if we wanted to accept any kind of numeric notation, including real numbers or large numbers with exponential notation, for example.

Perhaps the highest readability is achieved by breaking out the numeric check into its own convenience function, as below:

const func = require('./func')

const isNumeric = (string) => {
  return !Number.isNaN(string)
} 

describe('numeric string result', () => {
  it('should be numeric', () => {
    expect(isNumeric(func())).toBe(true)
  })
})

Expect a function to throw an exception in Jest

Unit testing functions in JavaScript using Jest sometimes requires checking if an exception was thrown, or testing the specific type of exception thrown.

Suppose we want to test the following function using Node.js and assert that it indeed throws an error:

func.js:

const func = () => {
  throw new Error('my error')
}

module.exports = func

func.test.js:

const func = require('./func')

describe('exception test', () => {
  it('should throw an error', () => {
    expect(func).toThrow()
  })
})

Note that func should not be called within the expect; calling the function will cause the error to be thrown unexpectedly.

We can also assert that an error is not thrown using:

expect(func).not.toThrow()

If we need to assert the specific name of the thrown error, we can use the following form:

it('should throw an error', () => {
  expect(func).toThrowError('my error')
})

If no exceptions are thrown, Jest will report:

Expected the function to throw an error. 
But it didn't throw anything.
Expecting Async Functions to Throw Exceptions

 

Writing a unit test to expect an async function to throw an exception can be done as follows. First we define the async function in a module, then in the test code we use the rejects property to test for any thrown errors. Essentially, we are asserting that our function causes a promise rejection.

async-func.js:

const func = async () => {
  throw new Error('my error')
} 

module.exports = func

async-func.test.js:

const func = require('./async-func.js')

describe('exception test', () => {
  it('should throw an error', async () => {
    await expect(func()).rejects.toThrow()
  })
})

Note that the function passed to it() is async itself so that we can use await inside.

Note also that func is called inside expect in this case; this will cause a promise rejection which can then be tested within the rejects property.

The case of testing for a specific error to be thrown inside the async function is similar and looks like this:

it('should throw an error', async () => { 
  await expect(func()).rejects.toThrowError('my error')
})
Expect a Function with Parameters to Throw an Exception

If we want to expect a function to throw an exception for certain input parameters, the key point is that we must pass in a function definition and not call our function inside the expect. The code is below for an example of a function which should throw an exception for negative integer inputs:

func.js:

const func = (n) => {
  if (n < 0) {
    throw new Error("negative input")
  }
  return n
}

module.exports = func

func.spec.js:

const func = require('./func.js')

describe('exception test', () => {
  it('should throw an exception', () => {
    expect(
    () => func(-1) // Do not simply call func
    ).toThrow()
  })
})

We pass an anonymous function to expect, which will throw for the input. Expect can then determine that this function will throw.

Running a Single Test Only Using Jest

Jest provides a convenient way to run only one unit test at a time.

First, run only the single test file which contains the single test to run, similar to the following:

jest my-test-suite.spec.js

The single test in the suite that we wish to run should be changed to use it.only() as in the example below:

describe('My test suite', () => {
  it('should meet condition 1', () => {
    expect(condition1).toEqual(true)
  })

  it.only('should meet condition 2', () => {
    expect(condition2).toEqual(true)
  })

  it('should meet condition 3', () => {
    expect(condition3).toEqual(true)
  })
})

Jest will run only the second test, and will show the others as skipped.

Note that it.only() can be used multiple times in the file, so that we can run only two or only several jest tests as desired.

Another way to run several tests only is to select the tests that should not be run, and use it.skip() for these. The specific tests only will be skipped.

There is also a way to run a single test suite at a time inside a file, which is handy for running a group of related tests: describe.only().

Convenient Ways to Print Out Entire Objects in JavaScript

Quick debugging in JavaScript often requires one to print out or log a full object to see what values it contains.

The usual console log can often print “[Object object]”, which is not useful.

Some convenient ways to see the entire object in logs are below.

Using the %o format specifier, which means “formatted as an object”:

console.log("%o", myObject)

Using the dir function:

console.dir(myObject)

The “dir” in console.dir() comes from “directory”, as in a directory of all the properties of the object. This always outputs a full hierarchical representation of the object.

Another way is to fully serialize the object into a JSON string:

console.log(JSON.stringify(myObject))

This is very useful especially in the context of loggers, so if logger is an instance of a logging object (such as a Winston logger), then we can log a complete JSON object using:

logger.info(JSON.stringify(myObject))

The Fields Pattern in REST API Design

The Fields Pattern or Field Selection Pattern for REST API design is a useful approach whenever the representations returned from the API become quite large and the API can be used by many clients in different contexts. For example, a native app client may only require a select few fields from the usual resource representation.

Suppose we have a resource employees, with employee representations containing a large number of fields. With the fields pattern, a client could make a request of the form:

GET /employees?fields=name,role,salary

The API will respond to this request with only these fields in addition to id:

{
   "id": "12345",
   "name": "John Smith",
   "role": "Developer",
   "salary": "100,000K"
}

A different client can request a different selection of fields:

GET /employees?fields=projectId,address
{
   "id": "12345",
   "projectId": "6789",
   "address": {
      "streetName": "REST Avenue",
      "postalCode": "J6V 9M2",
      "city": "Toronto"
   }
}

It is also possible to omit the id field, though arguably a resource representation should always have its primary identifier present, even when selecting fields.

If the fields parameter is omitted, the API responds with all fields of the employee resource available.

What if we want to be able to select fields of sub-objects, such as the address object? There are several options, for example:

GET /employees?fields=address:city

GET /employees?fields=address.city

GET /employees?addressFields=city

However, none of these are fully satisfying and venture into inventing syntax. Almost certainly, no two API designs will select the same syntax, and so none of these are ideal for a natural, simple design.

Selection of fields on arbitrarily complex objects is one of the many elegant, standard solutions provided by GraphQL, so a GraphQL API design may be preferable in cases where complex multi-level field selection is desired.

apiVersion field missing error in OpenShift

If you are running into the following error message in OpenShift when processing a YAML template:

error: unable to get type info from the object “*unstructured.Unstructured”: Object ‘apiVersion’ is missing in ‘object has no apiVersion field’

The cause is a problem with the apiVersion field in the template — even if it is present; the error message is misleading.

  ...
-kind: ImageStream
  apiVersion: 1 # Incorrect, must be v1
  metadata: 
    ....

The apiVersion value must be v1 instead of 1.