unit testing cookie or localstorage code with jest

unit testing cookie or localstorage code with jest

Imagine - you enter a website and first thing you see is a cookie banner telling you the website is heavily built on cookies and is not able to run without it and you have to accept these cookies… (PS: raoulkramer.de uses no cookies - mouse drop)

You write a Cookie Component, called CookieBanner and in my case it is done in vue.js. The component shall appear when your visitor drives by the first time and will store in a cookie (or maybe localStorage) his choice.
Everything is fine in the browser, then you want to write a unit test, to make this component secure against regressions and wonder "Where is my document.cookie?".

the problem

In unit testing context, you do not have a browser around your components and accessing `window` or `document` will not work. You can not test code that depends on the browser context.

the solution

You need to mock a `document` object, providing a cookie getter and setter function. And your component needs a wrapping access to the document.

ok, what?

In vue.js components can have external properties and own data properties. The CookieBanner shall work standalone and be embeddable everywhere, an external property is unhandy.
In your component you set a `local` data property `document`:

…,
data() {
  return {
    …,
    document: window.document,
    …,
  };
},
…

In your cookie handling/manipulating you need to add one line:

…,
methods: {
  cookieValue(name) {
    const { document } = this; // add this line to your existing code
    document.cookie…
    …
  },
  cookieExists(name) {
    const { document } = this; // add this line to your existing code
    …
  },
},
…

No other code changes in your component code are needed.

In your test file, you need to create a fakeDocumentCookie object, with a cookie interface, like document.cookie has (gist):

// idea  from here @see https://stackoverflow.com/questions/6456429/is-it-possible-to-mock-document-cookie-in-javascript
const fakeDocumentCookie = {
  cookies: '',
  
  get cookie() {
    return this.cookies;
  },
  
  set cookie(cookieValue) {
    const cookies = this.cookies.split(' ');
    const cookieName = cookieValue.split('=').shift();
    const cookieNameLength = cookieName.length;
    let cookieIndex = -1;
    cookies.forEach((value, index) => {
      if (`${value.substr(0, cookieNameLength)}=` === `${cookieName}=`) {
        cookieIndex = index;
      }
    });
    if (cookieIndex > -1) {
      cookies[cookieIndex] = `${cookieValue};`;
    } else {
      cookies.push(`${cookieValue};`);
    }
    this.cookies = cookies.join(' ').trim();
  },
};

And put this object in your component via setData():

const wrapper = mount(CookieBanner);
wrapper.setData({ document: fakeDocumentCookie });

And now you can unit test your cookie functions and your component too.

and for window.localStorage?

For localStorage you need to wrap your `window` object in your component, and in your test file create a fakeWindowLocalStorage, which you inject into your component via setData() (gist):

const fakeWindowLocalStorage = {
  localStorage: {
    data: {},
    length: 0,
    setItem(name, value) {
      this.data[name] = value.toString();
      this.length = Object.keys(this.data).length;
    },
    getItem(name) {
      return this.data[name];
    },
    removeItem(name) {
      delete this.data[name];
      this.length = Object.keys(this.data).length;
    },
  },
};

And inject this in your component as your window object:

const wrapper = mount(CookieBanner);
wrapper.setData({ window: fakeWindowLocalStorage });

Happy document.cookie / window.localStorage unit-testing.

Article Image from Emily Morter via unsplash and ghost ❤.