Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

screenshot a particular element #56

Closed
bahmutov opened this issue Mar 5, 2019 · 24 comments
Closed

screenshot a particular element #56

bahmutov opened this issue Mar 5, 2019 · 24 comments

Comments

@bahmutov
Copy link

bahmutov commented Mar 5, 2019

Could we screenshot just a particular element?

// as a child command
cy.get('.header').percySnapshot('header')
@ctrlplusb
Copy link

ctrlplusb commented Mar 5, 2019

Not a particularly useful comment, but just gotta say that this feature would be awesome.

@Robdel12
Copy link
Contributor

Robdel12 commented Mar 6, 2019

This is totally possible & something we have in the Ember SDK (but not any of the others).

--
@djones / @anaulin I took some time last night exploring what this would take to implement and we'd probably need to make a couple changes to @percy/agent. I think the syntax above would be possible, but I was thinking (for all SDK support) something like cy.percySnapshot('header', { scope: '.header' }) since we already have the entire resolved DOM at the time of the snapshot. Of course for the cypress SDK we can abstract over that and probably provide the child command (cy.get('.header').percySnapshot('header'))

We'll need to do that after asset discovery since we're not sure what CSS / JS needs to be setup to snapshot a specific piece of the page. I tried a very hacky solution last night that uses our domTransformation (which happens before asset discovery) and it resulted in broken snapshots:

cy.percySnapshot('Pass scoped selector?', {
  domTransformation: (documentClone) => {
    let bioContainer = documentClone.querySelector('.bio-container');
    documentClone.querySelector('body').innerHTML = bioContainer.innerHTML;

    return documentClone;
  }
})

Mostly because it was missing CSS needed to style properly. Also, that snippet makes the assumption that all resources / css is within the <head>, which isn't always true.

@mmcculloch15
Copy link

Tossing in that I'd love this as well! Being able to screenshot specific components would be really helpful

@nickofthyme
Copy link

Just to put this out there....you could also use a whitelisting technique rather than the blacklisting they suggest in the docs. Something like this global scss 👇

@media only percy {
  * {
    visibility: hidden;
  }

  .show-in-percy * {
    visibility: visible;
  }
}

This would override any elements that use visibility but could be a viable solution for most use cases in the meantime

@joby-aydin
Copy link

joby-aydin commented Dec 12, 2019

@Robdel12
I like the idea, however, I got this result;
image

@Robdel12
Copy link
Contributor

Hey @eyup-aydin! Any kind of mutation that happens to the DOM there will be what is rendered in Percy. So, if there are CSS selectors that dependent on DOM ordering, they could be broken in isolation. Or if a stylesheet is scoped out of the DOM, it won't be captured as an asset.

The DOM that is returned from domTransformation must be able to be rendered by itself (including all of the needed asset links, etc). I wouldn't recommend using domTransformation unless you're sure that can be achieved.

@wawagit
Copy link

wawagit commented Apr 8, 2020

Hi, is there any news about specific element screenshot?

@Robdel12
Copy link
Contributor

Robdel12 commented Apr 8, 2020

Hey @wawagit! Nothing new here -- you would either use Percy CSS, domTransformation option, or you would need to render that part of the page in isolation and snapshot that

@wawagit
Copy link

wawagit commented Apr 8, 2020

Thanks @Robdel12 for your response. I'll give a try to the domTransformation option or isolate my component like you said.

@ar5had
Copy link

ar5had commented Jun 22, 2020

@Robdel12 domtransformation option is not listed in SnapshotOptions at https://github.com/percy/percy-cypress/blob/master/index.d.ts#L2 that's why I typescript error/warning while using it.

@samuelint
Copy link

Hi here a command (in typescript) I've created that easily make a screenshot on a particular element :)

// cypress/integration/Test.test.ts
it('MyTest', () => {
  cy.visit('/');

  cy.get(`[data-cy=myData]`).percySnapshotElement('Snapshot Name');
});
// cypress/support/commands.ts

Cypress.Commands.add('percySnapshotElement', { prevSubject: true }, (subject, name, options) => {
  cy.percySnapshot(name, {
    domTransformation: (documentClone) => scope(documentClone, subject.selector),
    ...options,
  });
});

function scope(documentClone: Document, selector: string): Document {
  const element = documentClone.querySelector(selector);
  documentClone.querySelector('body').innerHTML = element.outerHTML;

  return documentClone;
}
// cypress/types.d.ts

declare namespace Cypress {
  // Precy.io ------------------------------------------------------
  interface SnapshotOptions {
    domTransformation: (documentClone: Document) => void;
  }

  interface Chainable {
    percySnapshotElement(name?: string, options?: SnapshotOptions);
  }
  // ---------------------------------------------------------------
}

@Santas
Copy link

Santas commented Apr 9, 2021

@Robdel12 Any updates on this? The workarounds provided here are helpful for some scenarios, but not all.

@Robdel12
Copy link
Contributor

Hey everyone! Sadly, this isn't something that is going to be implemented in the near future. It's also not an SDK specific issue (its a product issue) so I'm going to close this (& related issues). Maybe we create a meta repo with a discussion topic for product issues? Not sure (cc @djones)

With that said, this needs to be implemented in the rendering environment, where browsers easily can isolate & capture screenshots of elements (and/or the differ crops to the bounding box of the element). It's a huge rabbit hole trying to capture a subset of the DOM to send to the API for capture. You're going to miss parent>child CSS relationships, have missing assets, collapsed box model issues (when elements rely on sibling/parents for padding/margin), etc. I'm sure if anyone has attempted to use domTransformation, you've noticed it's not cut and dry as plucking a DOM node out of the tree.

@Ognengineer
Copy link

Hi here a command (in typescript) I've created that easily make a screenshot on a particular element :)

// cypress/integration/Test.test.ts
it('MyTest', () => {
  cy.visit('/');

  cy.get(`[data-cy=myData]`).percySnapshotElement('Snapshot Name');
});
// cypress/support/commands.ts

Cypress.Commands.add('percySnapshotElement', { prevSubject: true }, (subject, name, options) => {
  cy.percySnapshot(name, {
    domTransformation: (documentClone) => scope(documentClone, subject.selector),
    ...options,
  });
});

function scope(documentClone: Document, selector: string): Document {
  const element = documentClone.querySelector(selector);
  documentClone.querySelector('body').innerHTML = element.outerHTML;

  return documentClone;
}
// cypress/types.d.ts

declare namespace Cypress {
  // Precy.io ------------------------------------------------------
  interface SnapshotOptions {
    domTransformation: (documentClone: Document) => void;
  }

  interface Chainable {
    percySnapshotElement(name?: string, options?: SnapshotOptions);
  }
  // ---------------------------------------------------------------
}

Hey, how can I import this in JS instead of TS.

@CyrilKrylatov
Copy link

CyrilKrylatov commented Dec 23, 2021

Hey, how can I import this in JS instead of TS.

import '@percy/cypress'

/**
 * Create percySnapshopElement
 * @see {@link https://github.com/percy/percy-cypress/issues/56#issuecomment-792860231}
 */

Cypress.Commands.add('percySnapshotElement', { prevSubject: true }, (subject, name, options) => {
  cy.percySnapshot(name, {
    domTransformation: (documentClone) => scope(documentClone, subject.selector),
    ...options
  })
})

function scope (documentClone, selector) {
  const element = documentClone.querySelector(selector)
  documentClone.querySelector('body').innerHTML = element.outerHTML

  return documentClone
}

@lacroixdavid1
Copy link

lacroixdavid1 commented Jan 11, 2022

This is a show-stopper to us. We might decide to go with Applitools instead of percy.io just because of this. I honestly liked Percy better but it's not supported and not even in your plans.

@p00rni
Copy link

p00rni commented Apr 26, 2022

Just to put this out there....you could also use a whitelisting technique rather than the blacklisting they suggest in the docs. Something like this global scss 👇

@media only percy {
  * {
    visibility: hidden;
  }

  .show-in-percy * {
    visibility: visible;
  }
}

This would override any elements that use visibility but could be a viable solution for most use cases in the meantime

Hi, could you please advice where will we add this and how is this code utilised please?

@Robdel12
Copy link
Contributor

Hey @p00rni! You can pass that as a snapshot option or in the percy config file: https://docs.percy.io/docs/percy-specific-css#snapshot-options--sdk-options

@Robdel12
Copy link
Contributor

Jusssst wanted to come by and drop a little hint that this is starting to be worked on. Don't wanna give an ETA on it, but it's going to ship within this quarter 😃 (its mostly infrastructure/differ work). I'll give another update here when it does ship!

@Robdel12
Copy link
Contributor

👋🏼 Upgrade to the latest CLI and you can now use the scope option 🎉 https://docs.percy.io/docs/cli-configuration#snapshot For example as a snapshot option:

cy.percySnapshot({ scope: '.cookie-banner' });

Or in your Percy config file:

version: 2
snapshot:
  scope: '.cookie-banner'

@OgnenATvaltech
Copy link

Hey, @Robdel12 great news, I will give it a try in the next few days as I'm overbusy at this moment.
Just out of curiosity and being me not able not to comment on what's on top of my head, this feature means that Percy forces me to create separate visual test suites as I cannot chain already selected elements and need to add the selector in the scope (double work for me as an Automation engineer)..
Possible issues that I see for e.g. I use react-selector and Percy presumably will not be able to process it.

@Phonesis
Copy link

@Robdel12 I am trying to use this but is not available in the latest percy cypress lib? (3.1.2)

@Robdel12
Copy link
Contributor

@Phonesis I would make sure you're on the latest version of @percy/cli. There were no changes to the client SDKs (like @percy/cypress). https://docs.percy.io/docs/screenshot-a-single-element

@Phonesis
Copy link

Phonesis commented Nov 16, 2022

@Robdel12 There is an unfortunate compatibility issue with the Percy CLI lib and usage of the Cypress Orb in CircleCI. As a result, you cannot have the Percy/CLI lib installed in your repo (just the percy/cypress one)

Is this something your team are aware of? See percy/cli#1047

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

No branches or pull requests