Categories
JavaScript Vue

Vue.js Mixins — Global Mixins and Custom Merge Strategies

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at global mixins and custom merging strategies for merging mixins into components.

Global Mixin

We can create a mixin that’s applied globally. These mixins will affect every Vue instance created afterward. Therefore, we should be careful when using them.

We can use this to inject custom processing logic that applies to all components.

For example, we can define a global mixin as follows:

Vue.mixin({  
  created() {  
    const foo = this.$options.foo;  
    console.log(foo);  
  }  
});

new Vue({  
  el: "#app",  
  foo: "foo"  
});

Then we’ll see foo logged since the foo option is retrieved from this.$options.foo in the created hook.

When the created hook is called, we’ll see the console.log output since it’s called.

The above example shows how it should be used. Global mixins should be used infrequently since we want to minimize the chance of bugs created by conflicts from global mixins.

Custom Option Merge Strategies

We can adjust the strategies for merging mixin code into components.

To do this, we assign a function to Vue.config.optionMergeStrategies .

For example, we can write the following:

Vue.config.optionMergeStrategies.myOption = (toVal, fromVal) => {  
  return {  
    ...toVal,  
    ...fromVal  
  };  
};

Vue.mixin({  
  myOption: { foo: 1 }  
});

const vm = Vue.extend({  
  myOption: { foo: 2 }  
});

console.dir(vm.options.myOption);

We see that toVal is { foo: 1 } from the mixin and fromVal is { foo: 2 } from the component we created with Vue.extend .

Therefore, we merged myOption from the mixin into the component by letting the component’s myOption taking precedence.

This means that vm.options.myOption has the value { foo: 2 } .

We can write the same thing with new Vue as follows:

Vue.config.optionMergeStrategies.myOption = (toVal, fromVal) => {  
  return {  
    ...toVal,  
    ...fromVal  
  };  
};

const mixin = {  
  myOption: { foo: 1 }  
};

const vm = new Vue({  
  el: "#app",  
  myOption: { foo: 2 },  
  mixins: [mixin]  
});

console.dir(vm.$options.myOption);

We see that vm.options.myOption has the value { foo: 2 } again.

We can also flip them around, which means the mixin’s option has precedence over the component’s option if they have the same name by writing:

Vue.config.optionMergeStrategies.myOption = (toVal, fromVal) => {  
  return {  
    ...fromVal,  
    ...toVal  
  };  
};

const mixin = {  
  myOption: { foo: 1 }  
};

const vm = new Vue({  
  el: "#app",  
  myOption: { foo: 2 },  
  mixins: [mixin]  
});

console.dir(vm.$options.myOption);

Then we get { foo: 1 } from the console.log , which means the mixin ‘s myOption took precedence over the component’s myOption .

We can get various strategies for merging options by using Vue.config.optionMergeStrategies .

There are strategies for common option items like the lifecycle hook methods, watch, computed , methods , directives, etc.

Pretty much any option that we can put into Vue that aren’t our own custom options are in the Vue.config.optionMergeStrategies object.

We can use these preset strategies to set the merging strategies for our own options.

For example, if we want the methods strategy for merging myOption , we can write the following:

const strategies = Vue.config.optionMergeStrategies;  
strategies.myOption = strategies.methods;

Then the merging strategy for merging myOption will be the same as the strategy which we use for methods, which is the component’s methods have precedence over the mixin’s methods if they have the same name.

Conclusion

We can define global mixin to merge the option from the mixin to all components.

This shouldn’t be used frequently since it creates conflicts that are hard to trace.

It should only be used for our own custom-defined options.

We can also create our own mixin merging strategy by adding a property to Vue.config.optionMergeStrategies.optionName and setting a function that takes the option objects from the mixin and component respectively and returns the merged object.

Finally, we can access a full list of strategies by using the Vue.config.optionMergeStrategies object.

Categories
Express JavaScript Nodejs

Excluding Routes from Calling Express Middleware with Express-Unless

To conditionally skip a middleware when a condition is met, we can use the express-unless package.

In this article, we’ll take a look at how to use it.

Installation

We can install it by running:

npm i express-unless --save

Usage

After installing it, we can assign the unless function exposed by the express-unless and assign it to the unless property of our middleware functions.

For example, we can write:

const express = require('express');  
const unless = require('express-unless');  
   
const static = express.static(__dirname + '/public');  
static.unless = unless;const app = express();  
app.use(static.unless({ method: 'OPTIONS' }));  
app.listen(3000, () => console.log('server started'));

In the code above, we set the unless property of static to the unless function of the express-unless package.

Then we write:

app.use(static.unless({ method: 'OPTIONS' }));

to exclude the static route from running on OPTIONS requests.

If we’re writing our own middleware, we can assign the unless property of our middleware function as follows:

const express = require('express');  
const unless = require('express-unless');  
   
const static = express.static(__dirname + '/public');
const logHostname = (req, res, next) =>{  
  console.log(req.hostname);  
  next();  
}  
logHostname.unless = unless;const app = express();  
app.use(logHostname.unless({ method: 'OPTIONS' }));  
app.listen(3000, () => console.log('server started'));

In the code above, we defined the logHostman middleware to log the hostname. Then we set the unless property of the function to the unless function exposed in the express-unless package.

Options

In the object that we pass into unless , we can pass in the following options:

  • method — this can be a string or an array of strings. If the request method matches then the middleware won’t run.
  • path — this can be a string, regex or an array of string or regex. It can also be an array of object which is URL and method key-value pairs. If the request path or path and method match, then the middleware won’t run.
  • ext — this is a string or array of strings. If the path ends with those extensions then a middleware won’t run.
  • custom — a function that accepts req and returns a boolean. If the function returns true for the given request, then the middleware won’t run.
  • useOriginalUrl — this should be a boolean. Defaults to true . If it’s false , the path will match against req.url instead of req.originalUrl .

Advanced Examples

We can exclude calling the express.static middleware when we have the jpg , html , css or .js extensions as follows:

const express = require('express');  
const unless = require('express-unless');  
const url = require('url');  
const static = express.static(__dirname + '/public');
static.unless = unless;
const app = express();  
app.use(static.unless((req) => {  
  const ext = url.parse(req.originalUrl).pathname.substr(-4);  
  return !['.jpg', '.html', '.css', '.js'].includes(ext);  
}));  
app.listen(3000, () => console.log('server started'));

In the code above, we check the extensions of the req.originalUrl property to see if they the request have the extensions listed in the array and skip the static middleware if they’re there.

We can also mix paths and request methods together as follows:

const express = require('express');  
const unless = require('express-unless');
const static = express.static(__dirname + '/public');  
const logHostname = (req, res, next) => {  
  console.log(req.hostname);  
  next();  
}  
logHostname.unless = unless;  
const app = express();  
app.use(logHostname.unless({  
  path: [  
    '/index.html',  
    { url: '/', methods: ['OPTIONS', 'PUT'] }  
  ]  
}));  
app.listen(3000, () => console.log('server started'));

Then we stop the logHostname middleware from running when we go to index.html or / with the PUT or OPTION request methods.

Conclusion

We can stop middleware from running on certain routes or request methods by using the express-unless package.

To use it, we just set the unless function exposed from the package and set it to the unless property of our middleware function.

Then we can exclude routes by using various conditions by checking the URL and/or request method.

Categories
JavaScript Vue

Introduction to Vue.js Testing

With apps getting more complex than ever, it’s important to test them automatically. We can do this with unit tests, and then we don’t have to test everything by hand.

In this article, we’ll look at how to test Vue.js apps by writing a simple app and testing it.

Getting Started

To get started, we create an app that gets a joke from the Chuck Norris Jokes API.

We start by creating an empty folder, going into it, and running the Vue CLI by running:

npx vue create .

In the wizard, we select Unit Tests, then choose Jest and then proceed.

Now that we have the files generated, we can change some code. We can delete the components folder and replace the code in App.vue with:

<template>  
  <div id="app">  
    <button @click='toggleJoke()'>{{jokeHidden ? 'Show' : 'Hide'}} Joke</button>  
    <p v-if="!jokeHidden">{{data.value.joke}}</p>  
  </div>  
</template><script>  
export default {  
  name: "app",  
  data() {  
    return {  
      jokeHidden: false,  
      data: { value: {} }  
    };  
  },  
  beforeMount() {  
    this.getJoke();  
  },  
  methods: {  
    async getJoke() {  
      const res = await fetch("http://api.icndb.com/jokes/random");  
      this.data = await res.json();  
    }, toggleJoke() {  
      this.jokeHidden = !this.jokeHidden;  
    }  
  }  
};  
</script><style>  
#app {  
  font-family: "Avenir", Helvetica, Arial, sans-serif;  
  -webkit-font-smoothing: antialiased;  
  -moz-osx-font-smoothing: grayscale;  
  text-align: center;  
  color: #2c3e50;  
  margin-top: 60px;  
}  
</style>

The code just gets a joke from the API and then display it. Also, it has a button to show and hide the joke.

Our app looks something like the following:

https://thewebdev.info/wp-content/uploads/2020/04/test.png

Creating the Tests

Now that we have something to test, we can actually write the tests.

In the tests/unit folder, we delete what we have then create app.spec.js in that folder.

Then we open the file we created and add:

import { mount } from @vue/test-utils';  
import App from '@/App.vue'
const mockResponse = {  
  "type": "success",  
  "value": {  
    "id": 178,  
    "joke": "In an act of great philanthropy, Chuck made a generous donation to the American Cancer Society. He donated 6,000 dead bodies for scientific research.",  
    "categories": []  
  }  
}

To import the component that we’ll test, the mount function to let the Vue Test Utils build and render the component for testing, and the mockResponse object that we’ll use to set the mock data.

Then we add the skeleton for our test by writing:

describe('App.vue', () => {  
  beforeEach(() => {  
    jest.clearAllMocks()  
  })  
})

We have the string description for our test suite and a callback which we add out tests to.

Inside the callback, we have the beforeEach hook to clear all the mocks by running jest.clearAllMocks() .

We need this because we’ll mock some of the functions in our component later.

Adding our First Test

Next, we write our first test. This test will simulate getting the data from the API and then displaying the joke on the screen.

It won’t actually get the joke from the server since we want our test to run anywhere and at any time. Getting it from the server won’t let us do that.

The API returns something different every time we call it and also it might not always be available.

With that in mind, we write:

it('renders joke', async () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke)  
  })

in the callback we passed into the describe function after the beforeEach call.

The test above calls mount on our App component to build and render the component and returns a Wrapper object to let us access it.

In the second argument, we pass in the options with the methods property so that we can mock the getJoke method with Jest with jest.fn(). We want to mock it so that our test doesn’t call the API.

Once we have the wrapper then we run:

wrapper.vm.data = mockResponse;

to set the mockResponse data to the data property of our component instance.

Once we did that, we check that we get the joke in our mockResponse rendered by writing:

expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke)

since we put our joke in the p tag in our App component.

The expect method and toMatch are from Jest.

Writing Test that Interacts with UI Elements

Writing a test that does something to UI elements like buttons isn’t that much more work.

To test the button that we added to our app actually shows and hides the joke, we write:

it('toggles joke', () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Show Joke');  
    expect(wrapper.find('p').exists()).toBe(false); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);  
  })

The first part:

const wrapper = mount(App, {  
  methods: {  
    getJoke: jest.fn()  
  }  
});  
wrapper.vm.data = mockResponse;

is the same as before. We mock the getJoke function with jest.fn() so that our test won’t call the API. Then set the mock data.

Next, we check the button text by writing:

expect(wrapper.find('button').text()).toMatch('Hide Joke');

and that our mocked joke is shown in the p element:

expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);

Then we click our button by running:

wrapper.find('button').trigger('click');

And then check for the text of the button and whether the p element is removed by our v-if directive:

expect(wrapper.find('button').text()).toMatch('Show Joke');  
expect(wrapper.find('p').exists()).toBe(false);

Finally, we can do the click again and check if the joke is shown again as follows:

wrapper.find('button').trigger('click');  
expect(wrapper.find('button').text()).toMatch('Hide Joke');  
expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);

Running the Tests

Together, we have the following test code in app.test.js :

import { mount } from '@vue/test-utils';  
import App from '@/App.vue'
const mockResponse = {  
  "type": "success",  
  "value": {  
    "id": 178,  
    "joke": "In an act of great philanthropy, Chuck made a generous donation to the American Cancer Society. He donated 6,000 dead bodies for scientific research.",  
    "categories": []  
  }  
}

describe('App.vue', () => {  
  beforeEach(() => {  
    jest.clearAllMocks()  
  }) 

  it('renders joke', async () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke)  
  }) 

  it('toggles joke', () => {  
    const wrapper = mount(App, {  
      methods: {  
        getJoke: jest.fn()  
      }  
    });  
    wrapper.vm.data = mockResponse;  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
      expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Show Joke');  
    expect(wrapper.find('p').exists()).toBe(false); wrapper.find('button').trigger('click');  
    expect(wrapper.find('button').text()).toMatch('Hide Joke');  
    expect(wrapper.find('p').text()).toMatch(mockResponse.value.joke);  
  })  
})

Then we run the tests by npm run test:unit .

We should get:

PASS  tests/unit/app.spec.js  
  App.vue  
    √ renders joke (19ms)  
    √ toggles joke (11ms)Test Suites: 1 passed, 1 total  
Tests:       2 passed, 2 total  
Snapshots:   0 total  
Time:        2.102s  
Ran all test suites.

every time that we run our tests since we mocked the data.

Conclusion

Vue CLI creates a project that has unit testing built-in if we choose to include it. This saves us lots of work.

Jest is an easy test runner with lots of features like mocking and expect matchers that we can use.

To test UI components, we use the wrapper object returned by mount , which has the rendered component. Then we can use find to search the DOM for what we want to look for.

If the element exists, we can also trigger events on it by calling the trigger method with the event that we want to fire.

Finally, we have the exists method to check if the element we look for actually exists.

Categories
Flow JavaScript

JavaScript Type Checking with Flow — Union Types

Flow is a type checker made by Facebook for checking JavaScript data types. It has many built-in data types we can use to annotate the types of variables and function parameters.

In this article, we’ll look at how to use union types to create variables that accept several different types.

Union Type Syntax

The general syntax for defining a union type is as follows:

Type1 | Type2 | ... | TypeN

We separate each type with the vertical bar | .

Also, we can break them into multiple lines by using leading | :

type Bar =  
  | Type1  
  | Type2  
  | ...  
  | TypeN

We can create union types out of other union types:

type Numbers = 1 | 2;  
type Fruit = 'apple' | 'orange';type Foo = Numbers | Fruit;

Functions

If we use union types in functions, we have to handle every possible type of the union type of the parameter. For example, we have to write something like:

function foo(value: number | boolean | string):  number {   
  if (typeof value === 'number') {  
    return 1;  
  } else if (typeof value === 'boolean') {  
    return 2;  
  }  
  return Number(value);  
}

If we skip the handling of any type in the union type of the parameter like the following code:

function foo(value: number | boolean | string):  number {   
  if (typeof value === 'number') {  
    return 1;  
  } else if (typeof value === 'boolean') {  
    return 2;  
  }    
}

then we’ll get an error.

Unions & Refinements

With union type parameters, it’s useful to handle each type individually. We can do this with the typeof operator to check for the type and handle each type as follows:

function foo(value: number | boolean | string): number {   
  if (typeof value === 'number') {  
    return 1;  
  }   
  else if (typeof value === 'boolean') {  
    return 2;  
  }   
  else if (typeof value === 'string') {  
    return Number(value);  
  }  
  return Number(value);  
}

Disjoint Unions

We can create a union type out of different object types. For example, if we have the following objects:

{ foo: true, bar: 'bar' }  
{ foo: false, bar: 'baz' }

We can create a union type out of these 2 objects by creating a type alias for each and then union them. So we can write:

type Foo1 = { foo: true, bar: 'bar' };  
type Foo2 = { foo: false, bar: 'baz' };
type Foo = Foo1 | Foo2;

Then we can create variables of type Foo as follows:

let foo1: Foo = { foo: true, bar: 'bar' };  
let foo2: Foo = { foo: false, bar: 'baz' };

The union of 2 object types with fixed values will allow our variables to take on either of the 2 types in the union.

Union of object types with properties that aren’t exact will create a union of the 2 types for the properties.

For example, if we have:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { foo: true, bar: number };  
type Foo = Foo1 | Foo2;

Then Foo will allow bar to be either a string or a number, while foo will always be true . So we can create variables like the following:

let foo1: Foo = { foo: true, bar: 'bar' };  
let foo2: Foo = { foo: true, bar: 2 };

If we have 2 types with different properties, then the union of 2 types let us include all the properties of the object if we have a variable with the union of the 2 types.

For example, if we have:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { baz: false, a: number };
type Foo = Foo1 | Foo2;

Then we can define a variable with all the properties as follows:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1 };

With union types, we can also pass in extra properties that aren’t included in the types that form the union. So given:

type Foo1 = { foo: true, bar: string };  
type Foo2 = { baz: false, a: number };
type Foo = Foo1 | Foo2;

So we can write:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1, b: 2 };

Disjoint Union with Exact Types

We can restrict the addition of new properties by creating a union out of exact types as follows:

type Foo1 = {| foo: true, bar: string |};  
type Foo2 = {| baz: false, a: number |};
type Foo = Foo1 | Foo2;

Then writing:

let foo: Foo = { foo: true, bar: 'bar', baz: false, a: 1, b: 2 };

will give us an error.

With unions of exact types, we can either have properties from one or the other. So given these types:

type Foo1 = {| foo: true, bar: string |};  
type Foo2 = {| baz: false, a: number |};
type Foo = Foo1 | Foo2;

We can either have:

let foo: Foo = { foo: true, bar: 'bar' };

or:

let foo: Foo = {  baz: false, a: 1 };

With union types, we can create variables that can take on the value of several types. The types that form a union can primitive or object types.

We can also form unions of exact types to prevent adding additional properties that aren’t listed in the types or including properties from all the types.

Categories
Express JavaScript

Guide to the Express Response Object

The Express response object lets us send a response to the client.

Various kinds of responses like strings, JSON, and files can be sent. Also, we can send various headers and status code to the client in addition to the body.

In this article, we’ll look at how to set headers and send status codes with other items, including the set method for setting headers, status for setting response status code, type for setting the Content-Type header value, and the vary method for setting the Vary header value.

Methods

res.set(field [, value])

We can use the set method to set response headers before sending a response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res, next) => {  
  res.set({  
    'Content-Type': 'text/plain',  
    'Content-Length': '100'  
  })  
    .send();  
})  
app.listen(3000);

Then when we make a request to / , we get that the Content-Length header is set to 100 and the Content-Type is set to text/plain .

Note that we have to call send to send the response.

We can verify this with an HTTP client like Postman.

res.status(code)

We can call the status method to add a status code before sending the response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res, next) => {  
  res.status(400).send('Bad Request');  
})
app.listen(3000);

Then we get Bad Request displayed and the 400 response code.

res.type(type)

The type method sets the Content-Type HTTP header to the MIME type determined by the mime.lookup() method from the node-mime package for the specified type.

If type contains the / character, then it sets the Content-Type to type .

This method doesn’t send the response.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.type('.html').send();  
})  
app.listen(3000);

Then we get that the Content-Type response header has the value text/html; charset=utf-8 .

If we have:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.type('.png').send();  
})  
app.listen(3000);

Then we get image/png instead of text/html for the value of Content-Type .

res.vary(field)

The vary method adds the field to the Vary response header if it’s not already there.

For example, we can use it as follows:

const express = require('express');  
const path = require('path');  
const app = express()  
app.use(express.json())  
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {  
  res.vary('User-Agent').send();  
})  
app.listen(3000);

Then we have the Vary response header set to User-Agent .

The Vary header lets us to content negotiation, which is a mechanism for serving different kinds of data with the same URL.

The user agent can specify what’s best for the server.

The Vary header indicates which headers it’s used for content negotiation.

Since we have the Vary header set to User-Agent , our app uses the User-Agent header to serve different kinds of content according to the user agent of the browser.

Conclusion

The set method lets us set headers before sending our response. It takes an object with the response headers that we want to send.

status method is for setting a response status code before sending it. We can chain it with the response body.

The type method is for setting the Content-Type header value. The returned MIME-type is determined by the mime.lookup() method from node-mime .

The vary method for setting the Vary header value. The Vary header is used for content negotiation, which is serving different content according to the field specified by the Vary header’s value with the same URL.