5
votes

I'm using cypress to test my VueJS application. The one thing I'm having trouble with is mocking an image to be displayed on the page. For my use case, I'm simply loading a user profile with the following code:

describe('Test Login', () => {
  it('Can Login', () => {
    cy.server();
    cy.route({
      method: 'GET',
      url: '/api/account/',
      response: 'fx:profile.json',
    });
    cy.route('**/media/demo1.png', 'fx:demo1.png');
  });
});

fixtures/profile.json

{
    "avatar": "http://localhost:8080/media/demo1.png",
    "username": "cypress",
    "email": "[email protected]",
    "pk": 1,
    "is_staff": true,
    "is_superuser": true,
    "is_active": true
}

The profile fixture data is loading correctly in the test. In my fixtures folder, I also have a demo1.png file. I am expecting this image to be loaded and displayed on the page during my test, but it is being displayed as a broken image.

In the network tab, it shows demo1.png as a broken image with a 200 response code and type of text/html.

The cypress documentation mostly discusses images in the context of uploading images, but I haven't been able to find an example of how I can mock an image that is loaded through a <img> tag. Is there an easier way of doing this?

1
cy.route is for XHR, it won't interfere with the browser fetching images (or scripts, stylesheets, ...). Why do you need to mock the image?jonrsharpe
@jonrsharpe OK, thanks for clarifying. It's not particularly important that I mock the image, but I was surprised that I couldn't find an easy way to do this given that cypress seems to be able to do just about everything else. Is what I'm trying to do not possible with cypress?briancaffey
Agree with above, cy.route will only wait for the network request you're looking for.. you're not really mocking anything here - you could always take a look at cy.stub? or cy.request if you're wanting to interact with an API directly.mvoase
@briancaffey Hi! Did you find the way how to do this?Yuriy
@briancaffey It is really sad. How to do screenshot testing for cases when part of the screenshot can contain some image that can be changed on server side..Yuriy

1 Answers

3
votes

I am not sure if this answer can help you. But at least it is a workaround for this problem ;-)

Say we have a HTML like this:

<html>
    <body>
        <button id="button">load</button>
        <div id="profile">

        </div>

        <script>
            function httpGetAsync(theUrl, callback)
            {
                var xmlHttp = new XMLHttpRequest();
                xmlHttp.onreadystatechange = function() { 
                    if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
                        callback(JSON.parse(xmlHttp.responseText));
                }
                xmlHttp.open("GET", theUrl, true); // true for asynchronous 
                xmlHttp.send(null);
            }

            document.getElementById("button").addEventListener("click", () => {

                httpGetAsync("/api/account/", (result) => {
                    var div = document.querySelector("#profile");
                    var img = document.createElement("img");
                    img.src = result.avatar;
                    div.appendChild(img)
                })
            })

        </script>
    </body>
</html>

source: HTTP GET request in JavaScript?

And you want to load the profile after the click was done. Then you can use MutationObserver to replace the img.src.

First, write the MutationObserver:

var observeDOM = (function(){
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

    return function( obj, callback ){
      if( !obj || !obj.nodeType === 1 ) return; // validation

      if( MutationObserver ){
        // define a new observer
        var obs = new MutationObserver(function(mutations, observer){
            callback(mutations);
        })
        // have the observer observe foo for changes in children
        obs.observe( obj, { childList:true, subtree:true });
      }

      else if( window.addEventListener ){
        obj.addEventListener('DOMNodeInserted', callback, false);
        obj.addEventListener('DOMNodeRemoved', callback, false);
      }
    }
  })();

(heavily copy & pasted from Detect changes in the DOM)

Now you are able to do this:

describe('Test Login', () => {
    it('Can Login', () => {
        var win = null;
      cy.server();
      cy.route({
        method: 'GET',
        url: '/api/account/',
        response: 'fx:profile.json'
      });
      cy.visit("index.html").then(w => { 
          cy.get("#profile").then(pro => {
              var e = pro[0];
              observeDOM(e, (m) => {
                // add a red dot image
                m[0].addedNodes[0].src = "data:image/png;base64,"+
                "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP"+
                "C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA"+
                "AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J"+
                "REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq"+
                "ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0"+
                "vr4MkhoXe0rZigAAAABJRU5ErkJggg=="
            })
          })
          cy.get("button").click()
      })
    });
  });

(yeah at least some lines of code are written on my own ;-P)

You can read the image from the img.src attribute from the fixtures folder. For the sake of simplicity I have used a static base64 string here.

And the result: enter image description here

We are not using this kind of stuff in our aurelia app but I tried similar things in a private project some time ago.