Skip to content
This repository has been archived by the owner on Apr 28, 2023. It is now read-only.

Support Fuzzy DOM comparison (Angular _ngcontent) #79

Closed
dpsthree opened this issue Jun 21, 2019 · 2 comments · Fixed by #80
Closed

Support Fuzzy DOM comparison (Angular _ngcontent) #79

dpsthree opened this issue Jun 21, 2019 · 2 comments · Fixed by #80

Comments

@dpsthree
Copy link
Contributor

Thank you for taking time to open a new issue. Please answer a few questions to help us fix it faster. You can delete text that is irrelevant to the issue.

Is this a bug report or a feature request?

Feature request

If this is a new feature request, please describe it below

Angular applications add random attributes to the DOM to support CSS view encapsulation. As a result, the DOM is always different every time the application is rebuilt. It would be nice to either support this use case directly or add generic support for ignoring particular DOM attributes.

You can see an example of this here:
https://stackoverflow.com/questions/56352825/how-to-make-tomatchsnapshot-from-cypress-work-on-angular

@jansivans
Copy link

jansivans commented Jul 2, 2019

I have found a workaround. You can pre-process DOM node removing unwanted attributes like this:

cy.get('some-element').then(element => {
  let newElementString = element[0].outerHTML;
  const attributesToRemove = ['ng-reflect', '_nghost', '_ngcontent', 'ng-version'];
  for (const attribute of attributesToRemove) {
    const attributeRegExp = new RegExp(`${attribute}[^= ]*="[^"]*"`, 'g');
    newElementString = newElementString.replace(attributeRegExp, '');
  }
  newElementString = newElementString.replace(/<!--[\s\S]*?-->/g, '');
  const newElement = new DOMParser().parseFromString(newElementString, 'text/html');

  return newElement.querySelector('body').childNodes[0];
}).snapshot();

You can also wrap this logic into the custom cypress command if you want.

@alastair-todd
Copy link

alastair-todd commented Jan 13, 2020

Here's a typescript cypress command for the above:

const ngAttributes = ['ng-reflect', '_nghost', '_ngcontent', 'ng-version'];

Cypress.Commands.add(
  'ngSnapshot',
  {
    prevSubject: true
  },
  (subject: any, snapshotOptions?: any): Cypress.Chainable<any> => {
    let html = subject[0].outerHTML;

    for (const attribute of ngAttributes) {
      const expression = new RegExp(`${attribute}[^= ]*="[^"]*"`, 'g');
      html = html.replace(expression, '');
    }
    html = html.replace(/<!--[\s\S]*?-->/g, '');

    const sanitisedBody = new DOMParser().parseFromString(html, 'text/html').querySelector('body') as HTMLBodyElement;

    return cy.wrap(sanitisedBody.firstChild).snapshot(snapshotOptions);
  }
);

Usage

cy.get('an-element').ngSnapshot({ name: 'my-snapshot' });

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants