Basic Introduction: Automatic Testing of SharePoint online using Cypress.io

In this article we will look at the Automated Testing tool Cypress.io and look at the basic requirements for being able to set it up to test against a SharePoint online list. There are some interesting nuances to look at but the basic pronciples hold true.

Introduction

Cypress.io is a front end automated testing tool which has been around for a couple of years but has received significant interest from the open source community. It is an electron based testing tool with significant advantages over using the long term front end testing defacto tool Selenium. There are some disadvantages as well (being Chrome only right now) but as modern web browers get closer and closer to parity, that is less of an issue than say 5 years ago. After talking to my react development friends at the Northwest JavaScript meetup I resolved to try and make it work.

There are other recommendations for automated testing including a great article from Elio Struyf which uses a combination of puppeteer and jest – but again I wanted to try Cypress.io. It was challenging !

Update: Sheer coincidence, Elio also published an article on Cypress today!

Authentication and SharePoint online

The Cypress.io engine is based on electron – which means it runs in node. This is very advantageous, in that it has the ability to use open source npm libraries as part of its run time. In this case it allowed me to use the node-sp-auth to be able to generate the necessary headers to access a SharePoint Online site as an authenticated user. For the sake of this example I had the username and password in the config – you probably want to look at encrypting that or soemthing 😉

Cypress tasks

Cypress.io has the ability to create custom tasks which will execute some node code. So I created a custom login capability using the node-sp-auth library. This code should be added to the index.js file in the root of the cypress code.

const spauth = require("node-sp-auth");

let getLogin = async () => {
  const username = "me@mydomain.org";
  const password = "********";
  const pageUrl = "https://mydomain.sharepoint.com/sites/HelloWorld";

  // Connect to SharePoint
  const data = await spauth.getAuth(pageUrl, {
    username: username,
    password: password
  });
  return data;
};

module.exports = (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
  on("task", {
    // deconstruct the individual properties
    getLogin() {
      return getLogin();
    }
  });
};

 

Running the test

This code can then be referenced in the Cypress.io code to create the login headers necessary for authentication, before each of the tests are run. The waits are in here because I have to make sure the DOM is ready. It is a little clunky and I need to figure out how to do it better………

describe("Sample SharePoint List test", function() {
  beforeEach(() => {
    cy.task("getLogin").then(token => {
      cy.visit({
        method: "GET",
        url: "https://mydomain.sharepoint.com/sites/HelloWorld/Lists/CyList",
        headers: token.headers
      });
    });

    cy.wait(2000)
      .get(".od-TopBar-item")
      .wait(1000)
      .find('[name="New"]')
      .wait(1000)
      .click();
  });

  it("Should have a title of Hello World", function() {
    cy.title().should("contain", "Hello World");
  });

  it("Should have a validated Title value", function() {
    cy.get('[aria-label="New item form panel"]')
      .find(".od-TopBar-item")
      .wait(1000)
      .find('[name = "Save"]')
      .wait(1000)
      .click();
    cy.get('[data-automation-id="error-message"]').should(
      "contain",
      "You can't leave this blank."
    );
    cy.get('[aria-label="New item form panel"]')
      .find(".od-TopBar-item")
      .find('[name = "Cancel"]')
      .click();
  });

  it("Should submit the form with a minimum Title field", function() {
    cy.viewport(550, 750);
    cy.get(".ReactFieldEditor")
      .eq(0)
      .type("sample text");
    cy.get('[aria-label="New item form panel"]')
      .find(".od-TopBar-item")
      .find('[name = "Save"]')
      .click();
  });
});

Here’s how it looks

Running the test – we can see that cypress runs through each of the tests, waiting for the DOM to be ready…..(see below) and ultimately automated a Funcational test of the list. One of the REALLY cool things about Cypress is the ability to move back through the test and see what was executed at the time of the test. This allows the web developer to see the DOM state and everything, should their test fail!!

 

What I found about SharePoint

The modern SharePoint Framework, and more specifically the React JavaScript library on which it is based, uses Web Components to render the DOM. What this means is that the webpage itself renders aynchronously and at times under no control of my own, will re-renter itself as the page decides. This means that during the automated test, I came across multiple instances of where Cypress failed because the DOM elements were no longer attached. This is going to need some work on my part to figure this out……

Conclusion

You can use Cypress.io to functional test SharePoint online and SPFx – but there is clearly a lot more to learn 🙂

 

Fixing SPFx node-sass binding error on ADO release pipeline

When trying to run the gulp upload-to-sharepoint  encountered the following issue when creating a release pipeline for an SPFx web-part. There was a problem with no binding available for node-sass

[command]C:\NPM\Modules\gulp.cmd upload-to-sharepoint –gulpfile D:\a\r1\a\build\release\gulpfile.js –ship –username *** –password *** –tenant mckinseyandcompany –cdnsite sites/apps/ –cdnlib ClientSideAssets
2019-06-12T14:51:53.5954467Z [14:51:53] Working directory changed to D:\a\r1\a\build\release
2019-06-12T14:51:54.5490645Z D:\a\r1\a\build\release\node_modules\node-sass\lib\binding.js:15
2019-06-12T14:51:54.5497252Z throw new Error(errors.missingBinary());
2019-06-12T14:51:54.5498022Z ^
2019-06-12T14:51:54.5498662Z
2019-06-12T14:51:54.5499258Z Error: Missing binding D:\a\r1\a\build\release\node_modules\node-sass\vendor\win32-x64-48\binding.node
2019-06-12T14:51:54.5499538Z Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 6.x
2019-06-12T14:51:54.5499731Z
2019-06-12T14:51:54.5499883Z Found bindings for the following environments:
2019-06-12T14:51:54.5500034Z – Windows 64-bit with Node.js 8.x

and the error was actually staring us in the face – “binding available for Node 8″……..

The solution, just like for the build process, you have to add an agent task to ensure the correct version of node is used for the release process.