lazyd3v blog

⚙️ How I tested website plugin with cypress

Posted at — Dec 14, 2019

Backstory

One day my friend asked me to help him with some javascript for his new project about next-generation analytics. He wanted to create some script that needs to be injected to the webpage and then it listens to user’s behaviour and sends some information to the backend.

At first, we needed to create a script that works locally and chrome-extension user javascript and css came to the rescue. This extension allows to inject some custom javascript code into the webpage and for the very first PoC it worked really good!

Then we deployed this script to production, started to receive A LOT of data and we realized we missed a lot of things in the script. The more script was in production, the more features were implemented in it. At some point, manual testing became very boring, so we decided to spend some time on writing automated E2E tests.

Configure cypress

First thing we needed to find out is how to replace deployed script with the local one, because running tests after deployment sounds stupid, huh?

Within Cypress, you have the ability to stub responses. But if you go to docs, you could see a very important note at the top:

Please be aware that Cypress only currently supports intercepting XMLHttpRequests. Requests using the Fetch API and other types of network requests like page loads and <script> tags will not be intercepted or visible in the Command Log. See #95 for more details and temporary workarounds.

Because of these limitations, we have two options:

  1. Set up some proxy server that replaces deployed script with the local one on-the-fly
  2. Before running tests, remove deployed script and then inject local script

Solution #1 is quite difficult to setup and configure, and if your script is deployed to https CDN it becomes even more difficult. So we’ll focus on the second solution

Configuration steps

  1. Start static server with js bundle that you would like to test
    I recommend you to use serve or if your module bundler comes with such a server, you can use it as well (e.g. webpack-dev-server)

  2. Blacklist CDN your script has been deployed to
    To prevent execution of deployed script, we can use blacklistHosts option. If your script is deployed on the same domain as your website, I guess you can still use this option because minmatch library is used according to docs.

    // cypress.json
    {
     ...
     "blacklistHosts": ["awesomescript.cdn.com"]
     ...
    }
  3. Rewrite visit method
    To simplify the process of testing, we can modify visit method to inject our script every time we visit a page. Of course we can skip this step and execute script injection in each test, but I think it’s very boring.

   // cypress/support/commands.js

   // Replace with yours
   const SCRIPT_SRC = "http://localhost:9005/my-awesome-script.js";

   Cypress.Commands.overwrite("visit", (originalFn, url, options) => {
     originalFn(url, options).then(contentWindow => {
       const doc = contentWindow.document;
       const el = doc.createElement("script");
       el.src = SCRIPT_SRC;
       el.type = "application/javascript";
       el.async = true;
       doc.body.appendChild(el);
     });
   });

Conclusion

In this article we have seen how to configure Cypress for testing javascript that’s not a part of application itself, rather plugin or extension for existing app. Having automated E2E test in the pipeline, we can now implement new features, do refactoring without fear of breaking existing functionality. Also if developers from targeting website change something, we will be able to notice it and make a fix very quickly.

comments powered by Disqus