Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/devtools/client/inspector/animation/test/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 29 kB image not shown  

Quelle  head.js   Sprache: JAVA

 
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */

"use strict";

// Import the inspector's head.js first (which itself imports shared-head.js).
Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
  this
);

const TAB_NAME = "animationinspector";

const ANIMATION_L10N = new LocalizationHelper(
  "devtools/client/locales/animationinspector.properties"
);

// Auto clean-up when a test ends.
// Clean-up all prefs that might have been changed during a test run
// (safer here because if the test fails, then the pref is never reverted)
registerCleanupFunction(() => {
  Services.prefs.clearUserPref("devtools.toolsidebar-width.inspector");
});

/**
 * Open the toolbox, with the inspector tool visible and the animationinspector
 * sidebar selected.
 *
 * @return {Promise} that resolves when the inspector is ready.
 */

const openAnimationInspector = async function () {
  const { inspector, toolbox } = await openInspectorSidebarTab(TAB_NAME);
  await inspector.once("inspector-updated");
  const animationInspector = inspector.getPanel("animationinspector");
  const panel = inspector.panelWin.document.getElementById(
    "animation-container"
  );

  info("Wait for loading first content");
  const count = getDisplayedGraphCount(animationInspector, panel);
  await waitUntil(
    () =>
      panel.querySelectorAll(".animation-summary-graph-path").length >= count &&
      panel.querySelectorAll(".animation-target .objectBox").length >= count
  );

  if (
    animationInspector.state.selectedAnimation &&
    animationInspector.state.detailVisibility
  ) {
    await waitUntil(() => panel.querySelector(".animated-property-list"));
  }

  return { animationInspector, toolbox, inspector, panel };
};

/**
 * Close the toolbox.
 *
 * @return {Promise} that resolves when the toolbox has closed.
 */

const closeAnimationInspector = async function () {
  return gDevTools.closeToolboxForTab(gBrowser.selectedTab);
};

/**
 * Remove animated elements from document except given selectors.
 *
 * @param {Array} selectors
 * @return {Promise}
 */

const removeAnimatedElementsExcept = function (selectors) {
  return SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [selectors],
    selectorsChild => {
      function isRemovableElement(animation, selectorsInner) {
        for (const selector of selectorsInner) {
          if (animation.effect.target.matches(selector)) {
            return false;
          }
        }

        return true;
      }

      for (const animation of content.document.getAnimations()) {
        if (isRemovableElement(animation, selectorsChild)) {
          animation.effect.target.remove();
        }
      }
    }
  );
};

/**
 * Click on an animation in the timeline to select it.
 *
 * @param {AnimationInspector} animationInspector.
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} index
 *        The index of the animation to click on.
 */

const clickOnAnimation = async function (animationInspector, panel, index) {
  info("Click on animation " + index + " in the timeline");
  const animationItemEl = await findAnimationItemByIndex(panel, index);
  const summaryGraphEl = animationItemEl.querySelector(
    ".animation-summary-graph"
  );
  clickOnSummaryGraph(animationInspector, panel, summaryGraphEl);
};

/**
 * Click on an animation by given selector of node which is target element of animation.
 *
 * @param {AnimationInspector} animationInspector.
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {String} selector
 *        Selector of node which is target element of animation.
 */

const clickOnAnimationByTargetSelector = async function (
  animationInspector,
  panel,
  selector
) {
  info(`Click on animation whose selector of target element is '${selector}'`);
  const animationItemEl = await findAnimationItemByTargetSelector(
    panel,
    selector
  );
  const summaryGraphEl = animationItemEl.querySelector(
    ".animation-summary-graph"
  );
  clickOnSummaryGraph(animationInspector, panel, summaryGraphEl);
};

/**
 * Click on close button for animation detail pane.
 *
 * @param {DOMElement} panel
 *        #animation-container element.
 */

const clickOnDetailCloseButton = function (panel) {
  info("Click on close button for animation detail pane");
  const buttonEl = panel.querySelector(".animation-detail-close-button");
  const bounds = buttonEl.getBoundingClientRect();
  const x = bounds.width / 2;
  const y = bounds.height / 2;
  EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
};

/**
 * Click on pause/resume button.
 *
 * @param {AnimationInspector} animationInspector
 * @param {DOMElement} panel
 *        #animation-container element.
 */

const clickOnPauseResumeButton = function (animationInspector, panel) {
  info("Click on pause/resume button");
  const buttonEl = panel.querySelector(".pause-resume-button");
  const bounds = buttonEl.getBoundingClientRect();
  const x = bounds.width / 2;
  const y = bounds.height / 2;
  EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
};

/**
 * Click on rewind button.
 *
 * @param {AnimationInspector} animationInspector
 * @param {DOMElement} panel
 *        #animation-container element.
 */

const clickOnRewindButton = function (animationInspector, panel) {
  info("Click on rewind button");
  const buttonEl = panel.querySelector(".rewind-button");
  const bounds = buttonEl.getBoundingClientRect();
  const x = bounds.width / 2;
  const y = bounds.height / 2;
  EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
};

/**
 * Click on the scrubber controller pane to update the animation current time.
 *
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} mouseDownPosition
 *        rate on scrubber controller pane.
 *        This method calculates
 *        `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
 *        as the clientX of MouseEvent.
 */

const clickOnCurrentTimeScrubberController = function (
  animationInspector,
  panel,
  mouseDownPosition
) {
  const controllerEl = panel.querySelector(".current-time-scrubber-area");
  const bounds = controllerEl.getBoundingClientRect();
  const mousedonwX = bounds.width * mouseDownPosition;

  info(`Click ${mousedonwX} on scrubber controller`);
  EventUtils.synthesizeMouse(
    controllerEl,
    mousedonwX,
    0,
    {},
    controllerEl.ownerGlobal
  );
};

/**
 * Click on the inspect icon for the given AnimationTargetComponent.
 *
 * @param {AnimationInspector} animationInspector.
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} index
 *        The index of the AnimationTargetComponent to click on.
 */

const clickOnInspectIcon = async function (animationInspector, panel, index) {
  info(`Click on an inspect icon in animation target component[${index}]`);
  const animationItemEl = await findAnimationItemByIndex(panel, index);
  const iconEl = animationItemEl.querySelector(
    ".animation-target .objectBox .highlight-node"
  );
  iconEl.scrollIntoView(false);
  // Use click instead of EventUtils.synthesizeMouseAtCenter because the latter
  // seems to trigger additional events (e.g. mouseover) that might interfere with tests
  iconEl.click();
};

/**
 * Change playback rate selector to select given rate.
 *
 * @param {AnimationInspector} animationInspector
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} rate
 */

const changePlaybackRateSelector = async function (
  animationInspector,
  panel,
  rate
) {
  info(`Click on playback rate selector to select ${rate}`);
  const selectEl = panel.querySelector(".playback-rate-selector");
  const optionIndex = [...selectEl.options].findIndex(o => +o.value == rate);

  if (optionIndex == -1) {
    ok(
      false,
      `Could not find an option for rate ${rate} in the rate selector. ` +
        `Values are: ${[...selectEl.options].map(o => o.value)}`
    );
    return;
  }

  selectEl.focus();

  const win = selectEl.ownerGlobal;
  while (selectEl.selectedIndex != optionIndex) {
    const key = selectEl.selectedIndex > optionIndex ? "LEFT" : "RIGHT";
    EventUtils.sendKey(key, win);
  }
};

/**
 * Click on given summary graph element.
 *
 * @param {AnimationInspector} animationInspector
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Element} summaryGraphEl
 */

const clickOnSummaryGraph = function (
  animationInspector,
  panel,
  summaryGraphEl
) {
  // Disable pointer-events of the scrubber in order to avoid to click accidently.
  const scrubberEl = panel.querySelector(".current-time-scrubber");
  scrubberEl.style.pointerEvents = "none";
  // Scroll to show the timeBlock since the element may be out of displayed area.
  summaryGraphEl.scrollIntoView(false);
  EventUtils.synthesizeMouseAtCenter(
    summaryGraphEl,
    {},
    summaryGraphEl.ownerGlobal
  );
  // Restore the scrubber style.
  scrubberEl.style.pointerEvents = "unset";
};

/**
 * Click on the target node for the given AnimationTargetComponent index.
 *
 * @param {AnimationInspector} animationInspector.
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} index
 *        The index of the AnimationTargetComponent to click on.
 */

const clickOnTargetNode = async function (animationInspector, panel, index) {
  const { inspector } = animationInspector;
  const { waitForHighlighterTypeShown } = getHighlighterTestHelpers(inspector);
  info(`Click on a target node in animation target component[${index}]`);

  const animationItemEl = await findAnimationItemByIndex(panel, index);
  const targetEl = animationItemEl.querySelector(
    ".animation-target .objectBox"
  );
  const onHighlight = waitForHighlighterTypeShown(
    inspector.highlighters.TYPES.BOXMODEL
  );
  EventUtils.synthesizeMouseAtCenter(targetEl, {}, targetEl.ownerGlobal);
  await onHighlight;
};

/**
 * Drag on the scrubber to update the animation current time.
 *
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} mouseMovePixel
 *        Dispatch mousemove event with mouseMovePosition after mousedown.
 * @param {Number} mouseYPixel
 *        Y of mouse in pixel.
 */

const dragOnCurrentTimeScrubber = async function (
  animationInspector,
  panel,
  mouseMovePixel,
  mouseYPixel
) {
  const controllerEl = panel.querySelector(".current-time-scrubber");
  info(`Drag scrubber to X ${mouseMovePixel}`);
  EventUtils.synthesizeMouse(
    controllerEl,
    0,
    mouseYPixel,
    { type: "mousedown" },
    controllerEl.ownerGlobal
  );
  await waitUntilAnimationsPlayState(animationInspector, "paused");

  const animation = animationInspector.state.animations[0];
  let currentTime = animation.state.currentTime;
  EventUtils.synthesizeMouse(
    controllerEl,
    mouseMovePixel,
    mouseYPixel,
    { type: "mousemove" },
    controllerEl.ownerGlobal
  );
  await waitUntil(() => animation.state.currentTime !== currentTime);

  currentTime = animation.state.currentTime;
  EventUtils.synthesizeMouse(
    controllerEl,
    mouseMovePixel,
    mouseYPixel,
    { type: "mouseup" },
    controllerEl.ownerGlobal
  );
  await waitUntil(() => animation.state.currentTime !== currentTime);
};

/**
 * Drag on the scrubber controller pane to update the animation current time.
 *
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} mouseDownPosition
 *        rate on scrubber controller pane.
 *        This method calculates
 *        `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
 *        as the clientX of MouseEvent.
 * @param {Number} mouseMovePosition
 *        Dispatch mousemove event with mouseMovePosition after mousedown.
 *        Calculation for clinetX is same to above.
 */

const dragOnCurrentTimeScrubberController = async function (
  animationInspector,
  panel,
  mouseDownPosition,
  mouseMovePosition
) {
  const controllerEl = panel.querySelector(".current-time-scrubber-area");
  const bounds = controllerEl.getBoundingClientRect();
  const mousedonwX = bounds.width * mouseDownPosition;
  const mousemoveX = bounds.width * mouseMovePosition;

  info(`Drag on scrubber controller from ${mousedonwX} to ${mousemoveX}`);
  EventUtils.synthesizeMouse(
    controllerEl,
    mousedonwX,
    0,
    { type: "mousedown" },
    controllerEl.ownerGlobal
  );
  await waitUntilAnimationsPlayState(animationInspector, "paused");

  const animation = animationInspector.state.animations[0];
  let currentTime = animation.state.currentTime;
  EventUtils.synthesizeMouse(
    controllerEl,
    mousemoveX,
    0,
    { type: "mousemove" },
    controllerEl.ownerGlobal
  );
  await waitUntil(() => animation.state.currentTime !== currentTime);

  currentTime = animation.state.currentTime;
  EventUtils.synthesizeMouse(
    controllerEl,
    mousemoveX,
    0,
    { type: "mouseup" },
    controllerEl.ownerGlobal
  );
  await waitUntil(() => animation.state.currentTime !== currentTime);
};

/**
 * Get current animation duration and rate of
 * clickOrDragOnCurrentTimeScrubberController in given pixels.
 *
 * @param {AnimationInspector} animationInspector
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} pixels
 * @return {Object}
 *         {
 *           duration,
 *           rate,
 *         }
 */

const getDurationAndRate = function (animationInspector, panel, pixels) {
  const controllerEl = panel.querySelector(".current-time-scrubber-area");
  const bounds = controllerEl.getBoundingClientRect();
  const duration =
    (animationInspector.state.timeScale.getDuration() / bounds.width) * pixels;
  const rate = (1 / bounds.width) * pixels;
  return { duration, rate };
};

/**
 * Mouse over the target node for the given AnimationTargetComponent index.
 *
 * @param {AnimationInspector} animationInspector.
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} index
 *        The index of the AnimationTargetComponent to click on.
 */

const mouseOverOnTargetNode = function (animationInspector, panel, index) {
  info(`Mouse over on a target node in animation target component[${index}]`);
  const el = panel.querySelectorAll(".animation-target .objectBox")[index];
  el.scrollIntoView(false);
  EventUtils.synthesizeMouse(el, 10, 5, { type: "mouseover" }, el.ownerGlobal);
};

/**
 * Mouse out of the target node for the given AnimationTargetComponent index.
 *
 * @param {AnimationInspector} animationInspector.
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} index
 *        The index of the AnimationTargetComponent to click on.
 */

const mouseOutOnTargetNode = function (animationInspector, panel, index) {
  info(`Mouse out on a target node in animation target component[${index}]`);
  const el = panel.querySelectorAll(".animation-target .objectBox")[index];
  el.scrollIntoView(false);
  EventUtils.synthesizeMouse(el, -1, -1, { type: "mouseout" }, el.ownerGlobal);
};

/**
 * Select animation inspector in sidebar and toolbar.
 *
 * @param {InspectorPanel} inspector
 */

const selectAnimationInspector = async function (inspector) {
  await inspector.toolbox.selectTool("inspector");
  const onDispatched = waitForDispatch(inspector.store, "UPDATE_ANIMATIONS");
  inspector.sidebar.select("animationinspector");
  await onDispatched;
};

/**
 * Send keyboard event of space to given panel.
 *
 * @param {AnimationInspector} animationInspector
 * @param {DOMElement} target element.
 */

const sendSpaceKeyEvent = function (animationInspector, element) {
  element.focus();
  EventUtils.sendKey("SPACE", element.ownerGlobal);
};

/**
 * Set a node class attribute to the given selector.
 *
 * @param {AnimationInspector} animationInspector
 * @param {String} selector
 * @param {String} cls
 *        e.g. ".ball.still"
 */

const setClassAttribute = async function (animationInspector, selector, cls) {
  await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [cls, selector],
    (attributeValue, selectorChild) => {
      const node = content.document.querySelector(selectorChild);
      if (!node) {
        return;
      }

      node.setAttribute("class", attributeValue);
    }
  );
};

/**
 * Set a new style properties to the node for the given selector.
 *
 * @param {AnimationInspector} animationInspector
 * @param {String} selector
 * @param {Object} properties
 *        e.g. {
 *               animationDuration: "1000ms",
 *               animationTimingFunction: "linear",
 *             }
 */

const setEffectTimingAndPlayback = async function (
  animationInspector,
  selector,
  effectTiming,
  playbackRate
) {
  await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [selector, playbackRate, effectTiming],
    (selectorChild, playbackRateChild, effectTimingChild) => {
      let selectedAnimation = null;

      for (const animation of content.document.getAnimations()) {
        if (animation.effect.target.matches(selectorChild)) {
          selectedAnimation = animation;
          break;
        }
      }

      if (!selectedAnimation) {
        return;
      }

      selectedAnimation.playbackRate = playbackRateChild;
      selectedAnimation.effect.updateTiming(effectTimingChild);
    }
  );
};

/**
 * Set the sidebar width by given parameter.
 *
 * @param {String} width
 *        Change sidebar width by given parameter.
 * @param {InspectorPanel} inspector
 *        The instance of InspectorPanel currently loaded in the toolbox
 * @return {Promise} Resolves when the sidebar size changed.
 */

const setSidebarWidth = async function (width, inspector) {
  const onUpdated = inspector.toolbox.once("inspector-sidebar-resized");
  inspector.splitBox.setState({ width });
  await onUpdated;
};

/**
 * Set a new style property declaration to the node for the given selector.
 *
 * @param {AnimationInspector} animationInspector
 * @param {String} selector
 * @param {String} propertyName
 *        e.g. "animationDuration"
 * @param {String} propertyValue
 *        e.g. "5.5s"
 */

const setStyle = async function (
  animationInspector,
  selector,
  propertyName,
  propertyValue
) {
  await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [selector, propertyName, propertyValue],
    (selectorChild, propertyNameChild, propertyValueChild) => {
      const node = content.document.querySelector(selectorChild);
      if (!node) {
        return;
      }

      node.style[propertyNameChild] = propertyValueChild;
    }
  );
};

/**
 * Set a new style properties to the node for the given selector.
 *
 * @param {AnimationInspector} animationInspector
 * @param {String} selector
 * @param {Object} properties
 *        e.g. {
 *               animationDuration: "1000ms",
 *               animationTimingFunction: "linear",
 *             }
 */

const setStyles = async function (animationInspector, selector, properties) {
  await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [properties, selector],
    (propertiesChild, selectorChild) => {
      const node = content.document.querySelector(selectorChild);
      if (!node) {
        return;
      }

      for (const propertyName in propertiesChild) {
        const propertyValue = propertiesChild[propertyName];
        node.style[propertyName] = propertyValue;
      }
    }
  );
};

/**
 * Wait until current time of animations will be changed to given current time.
 *
 * @param {AnimationInspector} animationInspector
 * @param {Number} currentTime
 */

const waitUntilCurrentTimeChangedAt = async function (
  animationInspector,
  currentTime
) {
  info(`Wait until current time will be change to ${currentTime}`);
  await waitUntil(() =>
    animationInspector.state.animations.every(a => {
      return a.state.currentTime === currentTime;
    })
  );
};

/**
 * Wait until animations' play state will be changed to given state.
 *
 * @param {Array} animationInspector
 * @param {String} state
 */

const waitUntilAnimationsPlayState = async function (
  animationInspector,
  state
) {
  info(`Wait until play state will be change to ${state}`);
  await waitUntil(() =>
    animationInspector.state.animations.every(a => a.state.playState === state)
  );
};

/**
 * Return count of graph that animation inspector is displaying.
 *
 * @param {AnimationInspector} animationInspector
 * @param {DOMElement} panel
 * @return {Number} count
 */

const getDisplayedGraphCount = (animationInspector, panel) => {
  const animationLength = animationInspector.state.animations.length;
  if (animationLength === 0) {
    return 0;
  }

  const inspectionPanelEl = panel.querySelector(".progress-inspection-panel");
  const itemEl = panel.querySelector(".animation-item");
  const listEl = panel.querySelector(".animation-list");
  const itemHeight = itemEl.offsetHeight;
  // This calculation should be same as AnimationListContainer.updateDisplayableRange.
  const count = Math.floor(listEl.offsetHeight / itemHeight) + 1;
  const index = Math.floor(inspectionPanelEl.scrollTop / itemHeight);

  return animationLength > index + count ? count : animationLength - index;
};

/**
 * Check whether the animations are pausing.
 *
 * @param {AnimationInspector} animationInspector
 */

function assertAnimationsPausing(animationInspector) {
  assertAnimationsPausingOrRunning(animationInspector, true);
}

/**
 * Check whether the animations are pausing/running.
 *
 * @param {AnimationInspector} animationInspector
 * @param {boolean} shouldPause
 */

function assertAnimationsPausingOrRunning(animationInspector, shouldPause) {
  const hasRunningAnimation = animationInspector.state.animations.some(
    ({ state }) => state.playState === "running"
  );

  if (shouldPause) {
    is(hasRunningAnimation, false"All animations should be paused");
  } else {
    is(hasRunningAnimation, true"Animations should be running at least one");
  }
}

/**
 * Check whether the animations are running.
 *
 * @param {AnimationInspector} animationInspector
 */

function assertAnimationsRunning(animationInspector) {
  assertAnimationsPausingOrRunning(animationInspector, false);
}

/**
 * Check the <stop> element in the given linearGradientEl for the correct offset
 * and color attributes.
 *
 * @param {Element} linearGradientEl
          <linearGradient> element which has <stop> element.
 * @param {Number} offset
 *        float which represents the "offset" attribute of <stop>.
 * @param {String} expectedColor
 *        e.g. rgb(0, 0, 255)
 */

function assertLinearGradient(linearGradientEl, offset, expectedColor) {
  const stopEl = findStopElement(linearGradientEl, offset);
  ok(stopEl, `stop element at offset ${offset} should exist`);
  is(
    stopEl.getAttribute("stop-color"),
    expectedColor,
    `stop-color of stop element at offset ${offset} should be ${expectedColor}`
  );
}

/**
 * SummaryGraph is constructed by <path> element.
 * This function checks the vertex of path segments.
 *
 * @param {Element} pathEl
 *        <path> element.
 * @param {boolean} hasClosePath
 *        Set true if the path shoud be closing.
 * @param {Object} expectedValues
 *        JSON object format. We can test the vertex and color.
 *        e.g.
 *        [
 *          { x: 0, y: 0 },
 *          { x: 0, y: 1 },
 *        ]
 */

function assertPathSegments(pathEl, hasClosePath, expectedValues) {
  const pathData = pathEl.getPathData({ normalize: true });
  ok(
    expectedValues.every(value => isPassingThrough(pathData, value.x, value.y)),
    "unexpected path segment vertices"
  );

  if (hasClosePath) {
    ok(pathData.length, "Close path expected but path is empty");
    const closePathSeg = pathData.at(-1);
    Assert.strictEqual(
      closePathSeg.type.toLowerCase(),
      "z",
      "Close path not found"
    );
  }
}

/**
 * Check whether the given vertex is passing through on the path.
 *
 * @param {pathData} pathData - pathData of <path> element.
 * @param {float} x - x of vertex.
 * @param {float} y - y of vertex.
 * @return {boolean} true: passing through, false: no on the path.
 */

function isPassingThrough(pathData, x, y) {
  let previousX, previousY;
  for (let i = 0; i < pathData.length; i++) {
    const pathSeg = pathData[i];
    if (!pathSeg.values.length) {
      continue;
    }
    let currentX, currentY;
    switch (pathSeg.type) {
      case "M":
      case "L":
        currentX = pathSeg.values[0];
        currentY = pathSeg.values[1];
        break;
      case "C":
        currentX = pathSeg.values[4];
        currentY = pathSeg.values[5];
        break;
    }
    currentX = parseFloat(currentX.toFixed(3));
    currentY = parseFloat(currentY.toFixed(3));
    if (currentX === x && currentY === y) {
      return true;
    }
    if (previousX === undefined && previousY === undefined) {
      previousX = currentX;
      previousY = currentY;
    }
    if (
      previousX <= x &&
      x <= currentX &&
      Math.min(previousY, currentY) <= y &&
      y <= Math.max(previousY, currentY)
    ) {
      return true;
    }
    previousX = currentX;
    previousY = currentY;
  }
  return false;
}

/**
 * Return animation item element by the index.
 *
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {Number} index
 * @return {DOMElement}
 *        Animation item element.
 */

async function findAnimationItemByIndex(panel, index) {
  const itemEls = [...panel.querySelectorAll(".animation-item")];
  const itemEl = itemEls[index];
  itemEl.scrollIntoView(false);

  await waitUntil(
    () =>
      itemEl.querySelector(".animation-target .attrName") &&
      itemEl.querySelector(".animation-computed-timing-path")
  );

  return itemEl;
}

/**
 * Return animation item element by target node selector.
 * This function compares betweem animation-target textContent and given selector.
 * Then returns matched first item.
 *
 * @param {DOMElement} panel
 *        #animation-container element.
 * @param {String} selector
 *        Selector of tested element.
 * @return {DOMElement}
 *        Animation item element.
 */

async function findAnimationItemByTargetSelector(panel, selector) {
  for (const itemEl of panel.querySelectorAll(".animation-item")) {
    itemEl.scrollIntoView(false);

    await waitUntil(
      () =>
        itemEl.querySelector(".animation-target .attrName") &&
        itemEl.querySelector(".animation-computed-timing-path")
    );

    const attrNameEl = itemEl.querySelector(".animation-target .attrName");
    const regexp = new RegExp(`\\${selector}(\\.|$)`, "gi");
    if (regexp.exec(attrNameEl.textContent)) {
      return itemEl;
    }
  }

  return null;
}

/**
 * Find the <stop> element which has the given offset in the given linearGradientEl.
 *
 * @param {Element} linearGradientEl
 *        <linearGradient> element which has <stop> element.
 * @param {Number} offset
 *        Float which represents the "offset" attribute of <stop>.
 * @return {Element}
 *         If can't find suitable element, returns null.
 */

function findStopElement(linearGradientEl, offset) {
  for (const stopEl of linearGradientEl.querySelectorAll("stop")) {
    if (offset <= parseFloat(stopEl.getAttribute("offset"))) {
      return stopEl;
    }
  }

  return null;
}

/**
 * Do test for keyframes-graph_computed-value-path-1/2.
 *
 * @param {Array} testData
 */

async function testKeyframesGraphComputedValuePath(testData) {
  await addTab(URL_ROOT + "doc_multi_keyframes.html");
  await removeAnimatedElementsExcept(testData.map(t => `.${t.targetClass}`));
  const { animationInspector, panel } = await openAnimationInspector();

  for (const { properties, targetClass } of testData) {
    info(`Checking keyframes graph for ${targetClass}`);
    const onDetailRendered = animationInspector.once(
      "animation-keyframes-rendered"
    );
    await clickOnAnimationByTargetSelector(
      animationInspector,
      panel,
      `.${targetClass}`
    );
    await onDetailRendered;

    for (const property of properties) {
      const {
        name,
        computedValuePathClass,
        expectedPathSegments,
        expectedStopColors,
      } = property;

      const testTarget = `${name} in ${targetClass}`;
      info(`Checking keyframes graph for ${testTarget}`);
      info(`Checking keyframes graph path existence for ${testTarget}`);
      const keyframesGraphPathEl = panel.querySelector(`.${name}`);
      ok(
        keyframesGraphPathEl,
        `The keyframes graph path element of ${testTarget} should be existence`
      );

      info(`Checking computed value path existence for ${testTarget}`);
      const computedValuePathEl = keyframesGraphPathEl.querySelector(
        `.${computedValuePathClass}`
      );
      ok(
        computedValuePathEl,
        `The computed value path element of ${testTarget} should be existence`
      );

      info(`Checking path segments for ${testTarget}`);
      const pathEl = computedValuePathEl.querySelector("path");
      ok(pathEl, `The <path> element of ${testTarget} should be existence`);
      assertPathSegments(pathEl, true, expectedPathSegments);

      if (!expectedStopColors) {
        continue;
      }

      info(`Checking linearGradient for ${testTarget}`);
      const linearGradientEl =
        computedValuePathEl.querySelector("linearGradient");
      ok(
        linearGradientEl,
        `The <linearGradientEl> element of ${testTarget} should be existence`
      );

      for (const expectedStopColor of expectedStopColors) {
        const { offset, color } = expectedStopColor;
        assertLinearGradient(linearGradientEl, offset, color);
      }
    }
  }
}

/**
 * Check the adjusted current time and created time from specified two animations.
 *
 * @param {AnimationPlayerFront.state} animation1
 * @param {AnimationPlayerFront.state} animation2
 */

function checkAdjustingTheTime(animation1, animation2) {
  const adjustedCurrentTimeDiff =
    animation2.currentTime / animation2.playbackRate -
    animation1.currentTime / animation1.playbackRate;
  const createdTimeDiff = animation1.createdTime - animation2.createdTime;
  Assert.less(
    Math.abs(adjustedCurrentTimeDiff - createdTimeDiff),
    0.1,
    "Adjusted time is correct"
  );
}

Messung V0.5
C=93 H=86 G=89

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.