OHIF / Viewers

OHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages
https://docs.ohif.org/
MIT License
3.12k stars 3.29k forks source link

Capture canvas renderings in Percy Snapshots #1082

Closed dannyrb closed 4 months ago

dannyrb commented 4 years ago

I reached out to Percy.io support, as their marketing mentions diff-ing of canvas elements. All of our recorded screenshots thus far have blacked out canvas elements. I thought this was related to a Cypress issue where elements can be black if slightly outside the browser's viewport, but Percy's support confirmed the issue is a lack of support on there end:

Hey Danny, We currently don't have support for Canvas out of the box, but soon will. In the mean time, you can run this script before snapshots to serialize those canvas elements into images: https://gist.github.com/Robdel12/d4e42b8d7dd73a145ecde238454df764 The way Percy works is by capturing the state of the DOM when you call cy.percySnapshot . Since canvas elements only have a single DOM node and their state is held in memory, we're not currently capturing those. By running that script you'll be able to see the canvas elements appear in your snapshots. You probably will have to wrap the contents of that gist in a cy.document function:

cy.document().then(cyDoc => {
  // .. insert the canvasToImage function from https://gist.github.com/Robdel12/d4e42b8d7dd73a145ecde238454df764 here
  cyDoc.querySelectorAll('canvas').forEach(selector => canvasToImage(selector))
})

We'll just need to play with the provided workaround to make sure it cleans up after itself so as not to impact our canvas specific interaction tests. I've subscribed to percy.io updates. When canvas support is added, we can remove this workaround.

This would likely be best implemented as a Cypress command.

dannyrb commented 4 years ago

Follow-up from Robert:

I just remembered Cypress is the only SDK that has this "hidden" option that would help with this:

we'll just need to play with it to make sure it cleans up after itself so as not to impact our canvas specific interaction tests.

We have this domTransformation option that can be passed. This is the perfect spot to run the canvas script, it will only mutate the DOM Percy is receiving, and not the DOM in your test suite. You could even create a custom Cypress command over the percySnapshot one, so it always happens:

Cypress.Commands.add('percyCanvasSnapshot', (name, options = {}) => {
convertCanvas(document) {}
cy.percySnapshot(name, { ...options, domTransformation:  convertCanvas });
});

You can see how that's used in the SDKs test suite: https://github.com/percy/percy-cypress/blob/master/cypress/integration/sdk_spec.js#L50

dannyrb commented 4 years ago

The domTransformation option in percySnapshot wasn't doing the trick for us. We instead opted for a workaround where:

  1. You create an image from the canvas
  2. You add the image to the DOM, width: 100%
  3. Hide the canvas element
  4. TAKE SNAPSHOT
  5. Undo steps 2 and 3

This works well, but we had issues capturing some WebGL canvases as you can't generate an image unless you have preserveDrawingBuffer: true enabled, or you generate the snapshot in the same cycle as a triggered render.

So we use a Cypress hook to override the default method for creating a context to make sure preserveDrawingBuffer is set to true for all contexts (while testing only). This works locally, but appears to have issues for OpenLayers WebGL canvases in our test environment.

This could be because OpenLayers may fallback to a non-standard, or vendor-prefixed specific context type in our Linux environment. One would think the only options are 2d, webgl, webgl2, etc. But we've noticed that options like experimental-webgl and others are used depending on the environment. We may need to explicitly test for those in our little polyfill.

damntrecky commented 4 years ago

Follow-up from Robert:

I just remembered Cypress is the only SDK that has this "hidden" option that would help with this: we'll just need to play with it to make sure it cleans up after itself so as not to impact our canvas specific interaction tests. We have this domTransformation option that can be passed. This is the perfect spot to run the canvas script, it will only mutate the DOM Percy is receiving, and not the DOM in your test suite. You could even create a custom Cypress command over the percySnapshot one, so it always happens:

Cypress.Commands.add('percyCanvasSnapshot', (name, options = {}) => {
  convertCanvas(document) {}
  cy.percySnapshot(name, { ...options, domTransformation:  convertCanvas });
});

You can see how that's used in the SDKs test suite: https://github.com/percy/percy-cypress/blob/master/cypress/integration/sdk_spec.js#L50

This workaround from the SDK you posted is brilliant. This Is enough for our team right now. We are testing canvas rendering's from our charting library.

Workaround:

      cy.percySnapshot('With a passed domTransformation function', {
        domTransformation: documentClone => scope(documentClone, 'footer.info')
      })

Here is an example page -> https://app.zingsoft.com/demos/view/OCWWFNWD And we are getting the following output after toggling the legend.

Image 2020-02-06 at 4 52 09 PM

Robdel12 commented 2 years ago

👋🏼 Hey y'all! I was sent this GitHub issue from someone else (they probably pulled it up while googling). Just wanted to drop a note Percy's SDKs capture canvas elements out of the box now (in case anyone happens upon this now). If canvas elements still aren't being captured, this debugging doc should help: https://docs.percy.io/docs/debugging-sdks#canvas-elements-not-capturedbad-state

sedghi commented 4 months ago

Already doing it