Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feat/dom/new-package
Browse files Browse the repository at this point in the history
  • Loading branch information
acdvorak committed Sep 5, 2018
2 parents 91a61c1 + f3215e6 commit b4c9249
Show file tree
Hide file tree
Showing 5 changed files with 412 additions and 3 deletions.
5 changes: 2 additions & 3 deletions test/screenshot/diffing.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@
}
},
{
"description": "IE and Edge are extra flaky on Textfield and Typography pages for some reason",
"description": "IE and Edge are extra flaky on Text Field pages",
"browser_regex_patterns": [
"desktop_windows_edge@latest",
"desktop_windows_ie@11"
],
"url_regex_patterns": [
"mdc-textfield",
"mdc-typography"
"mdc-textfield"
],
"custom_config": {
"max_retries": 6,
Expand Down
2 changes: 2 additions & 0 deletions test/screenshot/infra/commands/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ class IndexCommand {
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>${parentDirName}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
font-family: "Roboto Mono", Consolas, monospace;
Expand Down
28 changes: 28 additions & 0 deletions test/screenshot/spec/_mixins.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Copyright 2018 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions://
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// MS Edge only
@mixin test-browser-selector-edge {
@supports (-ms-ime-align: auto) {
@content;
}
}
256 changes: 256 additions & 0 deletions test/screenshot/spec/fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,26 @@
*/

import 'url-search-params-polyfill';
import bel from 'bel';

window.mdc = window.mdc || {};

/**
* @typedef {{
* name: string,
* fromEl: ?Element,
* fromSide: string,
* toEl: ?Element,
* toSide: string,
* specDistancePx: number,
* displayOffsetPx: number,
* displayAlignment: string,
* displayTargetEl: ?Element,
* lineEl: ?Element,
* labelEl: ?Element,
* }} RedlineConfig
*/

class TestFixture {
constructor() {
/**
Expand All @@ -39,6 +56,18 @@ class TestFixture {
*/
this.fontsLoadedReflowDelayMs_ = this.getUrlParamInt_('fonts_loaded_reflow_delay_ms', 100);

/**
* @type {!Array<!RedlineConfig>}
* @private
*/
this.redlineConfigs_ = [];

/**
* @type {!HTMLElement}
* @private
*/
this.redlineContainerEl_ = bel`<div id="test-redlines"></div>`;

/**
* @type {!Promise<void>}
*/
Expand All @@ -48,8 +77,235 @@ class TestFixture {
console.log('Fonts loaded!');
this.measureMobileViewport_();
this.autoFocus_();
this.renderRedlines_();
this.notifyWebDriver_();
});

window.addEventListener('resize', () => {
this.renderRedlines_();
});

window.addEventListener('orientationchange', () => {
this.renderRedlines_();
});
}

/** @param {!RedlineConfig} config */
addRedline(config) {
const {fromEl, toEl, name} = config;
if (!fromEl || !toEl) {
return;
}

const lineEl = bel`
<div class="test-redline" title="${name}">
<div class="test-redline__tick test-redline__tick--start"></div>
<div class="test-redline__tick test-redline__tick--end"></div>
<div class="test-redline__label"></div>
</div>
`;
const labelEl = lineEl.querySelector('.test-redline__label');

this.redlineContainerEl_.appendChild(lineEl);

this.redlineConfigs_.push(Object.assign({
lineEl,
labelEl,
displayOffsetPx: 0,
displayAlignment: 'center',
}, config));

this.renderRedlines_();
}

removeRedlines() {
this.redlineConfigs_.length = 0;
this.redlineContainerEl_.innerHTML = '';
}

/** @private */
renderRedlines_() {
requestAnimationFrame(() => {
if (!this.redlineContainerEl_.parentElement) {
document.body.appendChild(this.redlineContainerEl_);
}

this.redlineConfigs_.forEach((config) => {
const {lineEl, fromSide} = config;
lineEl.classList.remove(
'test-redline--vertical',
'test-redline--horizontal',
'test-redline--pass',
'test-redline--warn',
'test-redline--small',
);
if (fromSide === 'top' || fromSide === 'bottom' ||
fromSide === 'first-baseline' || fromSide === 'last-baseline') {
this.drawVerticalRedline_(config);
} else if (fromSide === 'left' || fromSide === 'right') {
this.drawHorizontalRedline_(config);
} else {
throw new Error(`Unsupported \`fromSide\` value: "${fromSide}"`);
}
});
});
}

/**
* @param {!RedlineConfig} config
* @private
*/
drawVerticalRedline_(config) {
const {lineEl, labelEl, fromEl, fromSide, toEl, toSide, specDistancePx, displayOffsetPx, displayAlignment} = config;
lineEl.classList.add('test-redline--vertical');

const fromRect = fromEl.getBoundingClientRect();
const toRect = toEl.getBoundingClientRect();
const fromViewportY = this.getViewportCoordinate_(fromEl, fromSide);
const toViewportY = this.getViewportCoordinate_(toEl, toSide);

const actualStartY = Math.min(fromViewportY, toViewportY);
const actualEndY = Math.max(fromViewportY, toViewportY);
const actualDistancePx = Math.floor(actualEndY - actualStartY);

const lineStartX = Math.min(fromRect.left, toRect.left);
const lineEndX = Math.min(fromRect.right, toRect.right);

let leftPos;
if (displayAlignment === 'center') {
const leftMost = Math.min(fromRect.left, toRect.left);
const rightMost = Math.max(fromRect.right, toRect.right);
const half = (rightMost - leftMost) / 2;
leftPos = lineStartX + half + displayOffsetPx;
} else if (displayAlignment === 'right') {
leftPos = lineEndX - displayOffsetPx;
} else {
leftPos = lineStartX + displayOffsetPx;
}

lineEl.style.top = `${Math.round(actualStartY)}px`;
lineEl.style.left = `${Math.round(leftPos)}px`;
lineEl.style.height = `${Math.round(actualDistancePx)}px`;

if (actualDistancePx === specDistancePx) {
labelEl.innerHTML = `${actualDistancePx}px`;
lineEl.classList.add('test-redline--pass');
} else if (Math.abs(actualDistancePx - specDistancePx) <= 1) {
labelEl.innerHTML = `Spec: ${specDistancePx}px<br>Actual: ${actualDistancePx}px`;
lineEl.classList.add('test-redline--warn');
} else {
labelEl.innerHTML = `Spec: ${specDistancePx}px<br>Actual: ${actualDistancePx}px`;
lineEl.classList.add('test-redline--fail');
}

if (actualDistancePx < labelEl.offsetHeight + 2) {
lineEl.classList.add('test-redline--small');
}
}

/**
* @param {!RedlineConfig} config
* @private
*/
drawHorizontalRedline_(config) {
const {lineEl, labelEl, fromEl, fromSide, toEl, toSide, specDistancePx, displayOffsetPx, displayAlignment} = config;
const {displayTargetEl} = config;
lineEl.classList.add('test-redline--horizontal');

const fromRect = fromEl.getBoundingClientRect();
const toRect = toEl.getBoundingClientRect();
const fromViewportX = this.getViewportCoordinate_(fromEl, fromSide);
const toViewportX = this.getViewportCoordinate_(toEl, toSide);

const actualStartX = Math.min(fromViewportX, toViewportX);
const actualEndX = Math.max(fromViewportX, toViewportX);
const actualDistancePx = Math.floor(actualEndX - actualStartX);

const lineStartY = Math.min(fromRect.top, toRect.top);
const lineEndY = Math.min(fromRect.bottom, toRect.bottom);

let topPos;
if (displayAlignment === 'center') {
let topMost;
let bottomMost;

if (displayTargetEl) {
topMost = displayTargetEl.getBoundingClientRect().top;
bottomMost = displayTargetEl.getBoundingClientRect().bottom;
} else {
topMost = Math.min(fromRect.top, toRect.top);
bottomMost = Math.max(fromRect.bottom, toRect.bottom);
}
const half = (bottomMost - topMost) / 2;
topPos = lineStartY + half + displayOffsetPx;
} else if (displayAlignment === 'bottom') {
topPos = lineEndY - displayOffsetPx;
} else {
topPos = lineStartY + displayOffsetPx;
}

lineEl.style.top = `${Math.round(topPos)}px`;
lineEl.style.left = `${Math.round(actualStartX)}px`;
lineEl.style.width = `${Math.round(actualDistancePx)}px`;

if (actualDistancePx === specDistancePx) {
labelEl.innerHTML = `${actualDistancePx}px`;
lineEl.classList.add('test-redline--pass');
} else if (Math.abs(actualDistancePx - specDistancePx) <= 1) {
labelEl.innerHTML = `Spec: ${specDistancePx}px<br>Actual: ${actualDistancePx}px`;
lineEl.classList.add('test-redline--warn');
} else {
labelEl.innerHTML = `Spec: ${specDistancePx}px<br>Actual: ${actualDistancePx}px`;
lineEl.classList.add('test-redline--fail');
}

if (actualDistancePx < labelEl.offsetWidth + 2) {
lineEl.classList.add('test-redline--small');
}
}

/**
* @param {!Element} el
* @param {string} side
* @return {number}
* @private
*/
getViewportCoordinate_(el, side) {
const rect = el.getBoundingClientRect();
const borderBottomWidth = parseInt(getComputedStyle(el).borderBottomWidth, 10);
const borderTopWidth = parseInt(getComputedStyle(el).borderTopWidth, 10);
const borderLeftWidth = parseInt(getComputedStyle(el).borderLeftWidth, 10);
const borderRightWidth = parseInt(getComputedStyle(el).borderRightWidth, 10);

if (side === 'top') {
return rect.top + borderTopWidth;
}
if (side === 'bottom') {
return rect.bottom - borderBottomWidth;
}
if (side === 'left') {
return rect.left + borderLeftWidth;
}
if (side === 'right') {
return rect.right - borderRightWidth;
}

if (side === 'first-baseline' || side === 'last-baseline') {
const bl = document.createElement('span');
bl.classList.add('test-baseline-probe');

if (side === 'last-baseline') {
el.appendChild(bl);
} else {
el.insertBefore(bl, el.firstChild);
}

const pos = bl.getBoundingClientRect().top;
el.removeChild(bl);
return pos - borderTopWidth;
}

throw new Error(`Unsupported \`side\` value: "${side}"`);
}

/**
Expand Down
Loading

0 comments on commit b4c9249

Please sign in to comment.