6

I am testing a d3 application with Cypress. As of the tests, I'd like to make sure a specific function is called when a circle within an SVG element is clicked. The function is called when I'm clicking manually but the test that I wrote fails, so I assume I made a mistake somewhere in the test. Here's the test code I have right now:

import * as app from "../../app";

describe("Scatter plot", () => {
  before(() => {
    cy.visit("http://localhost:1234");
  });
  it("Triggers the displayMovieInfo on click", () => {
    const displayMovieInfo = cy.spy(app, "displayMovieInfo");
    cy.get("#scatterPlot")
      .get("circle")
      .eq(0)
      .click({ force: true });

    expect(displayMovieInfo).to.be.called;
  });
});

The output I get from Cypress:

expected displayMovieInfo to have been called at least once, but it was never called

Any help will be appreciated!

Update: I believe the click might have not worked before because the circle didn't exist when cypress attempted to click it. By adding "await cy.wait(1000);" before the click action, the function is called (I can see the results and a message logged from inside it). Sadly, the test is still failing.

Update 2: I changed the test to use the window object (see below), but the assertion still fails (the test itself succeeds, which is also not a good thing).

 cy.window()
      .then(window => {
        displayMovieInfoSpy = cy.spy(window, "displayMovieInfo");
        cy.get("#scatterPlot")
          .get("circle")
          .eq(2)
          .click({ force: true })
          .as("clicking");
        expect(displayMovieInfoSpy).to.be.called;
      });

Update 3: It seems that the combination of d3 and parcel.js causes the test to fail. When using d3 alone or parcel.js alone, the test works just fine. Also, the expect statement should be in the then block after the click action.

wicccked
  • 352
  • 1
  • 4
  • 15

2 Answers2

6

Seems you're importing app variable on the test directly. This object is a different one than the one on your browser. You should make a global variable (or method) for getting your app variable directly from your browser

cy.window().its('app').then((app) => { 
   // your test with app var
})

Also you might want to use then() condition to ensure it checks it after. But this may not be necessary.

.click({ force: true }).then(() => {
   expect(displayMovieInfo).to.be.called;
});
Pau Penín
  • 111
  • 1
  • Thanks for the reply Pau! Unfortunately, my window object doesn't have a displayMovieInfo property on it, probably because I'm serving my app using parcel. Is there a workaround for this case? – wicccked Sep 16 '18 at 19:25
  • I bound the displayMovieInfo to the window object manually, now it is a property on the window object. The app property doesn't exist, so I modified your code a little bit (see the result in the update to the original post), but with no luck =( – wicccked Sep 16 '18 at 19:52
  • I think that "expect" method executes before cypress "get" method. You should try this: .click({ force: true }).then(() => { expect(displayMovieInfo).to.be.called; }); (Check second code of my first reply) – Pau Penín Sep 17 '18 at 16:39
  • It just makes the test pass even though the expect fails =( – wicccked Sep 17 '18 at 17:18
5

I think you have mostly a couple concept problems about how cypress is working.

First click only can be on one element and second is about how you use alias.

This code I put works like a charm, hope it helps you a little about the concepts of alias, should, click and spy.

d3test.spec.js

describe("Scatter plot", () => {
before(() => {
  cy.visit("http://localhost/d3test");
});
it("Triggers the displayMovieInfo on click", () => {
    cy.window()
    .then(window => {
      let displayMovieInfoSpy = cy.spy(window, "displayMovieInfo");

      cy.get("#scatterPlot").get("circle").as('circles')

      cy.get('@circles').should('have.length', 1)

      cy.get('@circles').click({ force: true })
        .then(() => {
            expect(displayMovieInfoSpy).to.be.called;
        })
    });
});
});

index.html

<svg id="scatterPlot">
    <circle cx="50%" cy="50%" r="100" fill="blue" onclick="displayMovieInfo()"></circle>
</svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
    window.displayMovieInfo = function(){
        console.log(1);
    }
</script>

If you have more troubles I recomend you trying stuff one by one, using cy.log() and debugger console.

Pau Penín
  • 111
  • 1
  • Thanks Pau! I think I figured out what the problem is (see update to original post). .eq(2) was picking the third circle out of the array, so that part was fine. I did need to move the assertion into the .then block, you were right in that part. Thanks a lot for your help! – wicccked Sep 18 '18 at 16:26