docstr: percy_snapshot - visual test api shortcut to `percy_runner.snapshot`.
  It also combines the snapshot `name` with the Python version,
  args:
  - name: combined with the python version to give the final snapshot name
  - wait_for_callbacks: default False, whether to wait for Dash callbacks,
      after an extra second to ensure that any relevant callbacks have
      been initiated
  - convert_canvases: default False, whether to convert all canvas elements
      in the DOM into static images for percy to see. They will be restored
      after the snapshot is complete.
  - widths: a list of pixel widths for percy to render the page with. Note
      that this does not change the browser in which the DOM is constructed,
      so the width will only affect CSS, not JS-driven layout.
      Defaults to [1280]
code: def percy_snapshot(
      self,
      name="",
      wait_for_callbacks=False,
      convert_canvases=False,
      widths=None,
  ):
      """percy_snapshot - visual test api shortcut to `percy_runner.snapshot`.
      It also combines the snapshot `name` with the Python version,
      args:
      - name: combined with the python version to give the final snapshot name
      - wait_for_callbacks: default False, whether to wait for Dash callbacks,
          after an extra second to ensure that any relevant callbacks have
          been initiated
      - convert_canvases: default False, whether to convert all canvas elements
          in the DOM into static images for percy to see. They will be restored
          after the snapshot is complete.
      - widths: a list of pixel widths for percy to render the page with. Note
          that this does not change the browser in which the DOM is constructed,
          so the width will only affect CSS, not JS-driven layout.
          Defaults to [1280]
      """
      if widths is None:
          widths = [1280]
      snapshot_name = f"{name} - py{sys.version_info.major}.{sys.version_info.minor}"
      logger.info("taking snapshot name => %s", snapshot_name)
      try:
          if wait_for_callbacks:
              # the extra one second sleep adds safe margin in the context
              # of wait_for_callbacks
              time.sleep(1)
              until(self._wait_for_callbacks, timeout=40, poll=0.3)
      except TestingTimeoutError:
          # API will log the error but this TimeoutError should not block
          # the test execution to continue and it will still do a snapshot
          # as diff reference for the build run.
          logger.error(
              "wait_for_callbacks failed => status of invalid rqs %s",
              self.redux_state_rqs,
          )
  
      if convert_canvases:
          self.driver.execute_script(
              """
                  const stash = window._canvasStash = [];
                  Array.from(document.querySelectorAll('canvas')).forEach(c => {
                      const i = document.createElement('img');
                      i.src = c.toDataURL();
                      i.width = c.width;
                      i.height = c.height;
                      i.setAttribute('style', c.getAttribute('style'));
                      i.className = c.className;
                      i.setAttribute('data-canvasnum', stash.length);
                      stash.push(c);
                      c.parentElement.insertBefore(i, c);
                      c.parentElement.removeChild(c);
                  });
              """
          )
  
      try:
          self.percy_runner.snapshot(name=snapshot_name, widths=widths)
      except requests.HTTPError as err:
          # Ignore retries.
          if err.request.status_code != 400:
              raise err
  
      if convert_canvases:
          self.driver.execute_script(
              """
                  const stash = window._canvasStash;
                  Array.from(
                      document.querySelectorAll('img[data-canvasnum]')
                  ).forEach(i => {
                      const c = stash[+i.getAttribute('data-canvasnum')];
                      i.parentElement.insertBefore(c, i);
                      i.parentElement.removeChild(i);
                  });
                  delete window._canvasStash;
              """
          )
  
imports: import os
  import sys
  import time
  import logging
  import warnings
  import percy
  import requests
  from selenium import webdriver
  from selenium.webdriver.support import expected_conditions
  from selenium.webdriver.common.by import By
  from selenium.webdriver.support.wait import WebDriverWait
  from selenium.webdriver.common.keys import Keys
  from selenium.webdriver.common.action_chains import ActionChains
  from selenium.common.exceptions import WebDriverException, TimeoutException, MoveTargetOutOfBoundsException
  from dash.testing.wait import text_to_equal, style_to_equal, class_to_equal, contains_text, contains_class, until
  from dash.testing.dash_page import DashPageMixin
  from dash.testing.errors import DashAppLoadingError, BrowserError, TestingTimeoutError
  from dash.testing.consts import SELENIUM_GRID_DEFAULT
label_desc: Use image node_id 5 for calling the Browser obj's underlying member method code with example usage: obj.percy_snapshot(name, wait_for_callbacks, convert_canvases, widths) without return types