title: How can I assert that a HTML element does not change its appearance after a stylesheet has been included?
body: <h1>Setting</h1>
  
  <p>I’m writing a plugin for a website. It is going to add elements to the DOM that are styled via the plugin’s CSS. I expect the styling to be limited to the plugin, i.e. no elements outside the plugin should change their appearance once the plugin is included on the web page.</p>
  
  <p>I’m running integration tests using cypress. How can I assert that all the pre-existing elements’ styles stay the same when the plugin is included on the page? I have access to the page before and after the plugin has been loaded.</p>
  
  <h1>Approach</h1>
  
  <p>This is what I thought should work:</p>
  
  <pre class="lang-js prettyprint-override"><code>cy.visit('theURL');
  getStyles().then(oldStyles =&gt; {                      // Get the styles of the elements
      mountPlugin();                                   // Mount the plugin (including CSS)
      getStyles().then(newStyles =&gt; {                  // Get the (possibly changed) styles
          newStyles.forEach((newStyle, i) =&gt;           // Compare each element’s style after
              expect(newStyle).to.equal(oldStyles[i])  //+ mounting to the state before mounting
          );
      });
  });
  </code></pre>
  
  <pre class="lang-js prettyprint-override"><code>function getStyles() {
      return cy.get('.el-on-the-page *').then((elements) =&gt; { // Get all elements below a certain root
          const styles: CSSStyleDeclaration[] = []
          elements.each((_, el) =&gt; {                          // Get each element’s style
              styles.push(window.getComputedStyle(el));       //+ and put them it an array
          });
          return styles;                                      // Return the styles
      });
  }
  </code></pre>
  
  <h1>Problems</h1>
  
  <h2>Numeric keys in the CSSStyleDeclaration</h2>
  
  <p>The line <code>expect(newStyle).to.equal(oldStyles[i])</code> fails because <code>oldStyles[i]</code> contains numeric keys that only list property names. For instance,</p>
  
  <pre class="lang-js prettyprint-override"><code>// oldStyles[i] for some i
  {
      cssText: "animation-delay: 0s; animation-direction: normal; […more]"
      length: 281
      parentRule: null
      cssFloat: "none"
      0: "animation-delay"   // &lt;-- These elements only list property names, not values
  ...                        //+
      280: "line-break"      //+
      alignContent: "normal" // &lt;-- Only here there are actual property values
  ...                        //+
      zoom: "1"              //+
  ...
  }
  </code></pre>
  
  <h3>Workaround</h3>
  
  <p>I fix this by looping through the CSS keys manually and checking if the key is a number. However, these numeric keys only appear in the <code>oldStyles</code>, not in <code>newStyles</code>. I’m writing this because this looks fishy to me, and I assume that the error might already be there.</p>
  
  <pre class="lang-js prettyprint-override"><code>// Instead of newStyles.foreach(…) in the first snippet
  newStyles.forEach((newStyle, i) =&gt; {
      for (const key in newStyle) {
          if(isNaN(Number(key))) {
              expect(newStyle[key]).to.equal(oldStyles[i][key]);
          }
      }
  });
  </code></pre>
  
  <h2>Empty property values</h2>
  
  <p>I’m making the implicit assumption here that the DOM is actually loaded and has applied the styles. From my understanding <code>getLinkListStyles</code>’s call to <code>cy.get</code> should be scheduled to run only after <code>cy.visit</code> has waited for the window to fire the <code>load</code> event.</p>
  
  <p>From the <a href="https://docs.cypress.io/api/commands/visit.html#Visit-a-local-server-running-on-http-localhost-8000" rel="nofollow noreferrer">Cypress documentation</a>:</p>
  
  <blockquote>
    <p><code>cy.visit()</code> resolves when the remote page fires its <code>load</code> event.</p>
  </blockquote>
  
  <p>However, with the above workaround employed, I get an empty string for the CSS rules in <code>oldStyles</code>. For instance:</p>
  
  <pre class="lang-js prettyprint-override"><code>//oldStyles[i] for some i
  {
      cssText: "animation-delay: ; animation-direction: ; animation-duration: ; […more]"
      length: 0
      parentRule: null
      cssFloat: ""
      alignContent: ""
  ...
  }
  </code></pre>
  
  <h3>Attempted solutions</h3>
  
  <p>Note that this behaviour does not change when I explicitly use a callback with <code>cy.visit</code>, i.e.:</p>
  
  <pre class="lang-js prettyprint-override"><code>cy.visit(Cypress.env('theURL')).then(()=&gt;{
      getStyles().then((oldStyles) =&gt; {
          // (rest as above)
  </code></pre>
  
  <p>Neither does <code>cy.wait(15000)</code> at the beginning of <code>getStyles()</code>:</p>
  
  <pre class="lang-js prettyprint-override"><code>function getStyles() {
      cy.wait(15000); // The page has definitely loaded and applied all styles by now
      cy.get('.el-on-the-page *').then((elements) =&gt; {
  ...
  </code></pre>