Skip to content

Commit

Permalink
Add ability to whitelist visible elements in percy (#45733) (#49881)
Browse files Browse the repository at this point in the history
* add ability to whitelist visible elements in percy

* allow white and blacklisting elements in visual tests

* remove unnecessary webElement methods

* refactor snapshot options to use show and hide

* refactor add/remove and visual test helpers

* [percy] rework css rules to allow hiding inside shown elements

* [percy] adjust logic to support showing inside hiding

* attach styles to hide percy when capturing the snapshot

* refactor in order to make sure all logic is executed if snapshot fails

* remove sleeps

* add back skipFirefox tag
  • Loading branch information
liza-mae authored Oct 31, 2019
1 parent 11c1518 commit 5d6cf13
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 46 deletions.
1 change: 1 addition & 0 deletions src/legacy/core_plugins/kibana/public/discover/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ <h1 class="euiScreenReaderOnly">{{screenTitle}}</h1>
chart-data="histogramData"
timefilter-update-handler="timefilterUpdateHandler"
watch-depth="reference"
data-test-subj="discoverChart"
></discover-histogram>
</section>

Expand Down
7 changes: 0 additions & 7 deletions src/legacy/core_plugins/kibana/public/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,3 @@
// Dashboard styles
// MUST STAY AT THE BOTTOM BECAUSE OF DARK THEME IMPORTS
@import './dashboard/index';

// helper class that hides elements when rendered in percy
@media only percy {
.hideInPercy {
visibility: hidden;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,88 @@
import { readFileSync } from 'fs';
import { agentJsFilename } from '@percy/agent/dist/utils/sdk-utils';

export function takePercySnapshot() {
export function takePercySnapshot(show, hide) {
if (!window.PercyAgent) {
return false;
}

const agent = new window.PercyAgent({
handleAgentCommunication: false
});
// add percy styles to hide/show specific elements
const styleElement = document.createElement('style');
styleElement.appendChild(document.createTextNode(`
.hideInPercy {
visibility: hidden;
const queryAll = selector => [
...document.querySelectorAll(selector)
];
.showInPercy {
visibility: visible;
}
}
// array of canvas/image replacements
const replacements = [];
.showInPercy {
visibility: visible;
.hideInPercy {
visibility: hidden;
}
}
`));
document.head.appendChild(styleElement);

const add = (selectors, className) => {
for (const selector of selectors) {
for (const element of document.querySelectorAll(selector)) {
element.classList.add(className);
}
}
};

const remove = (selectors, className) => {
for (const selector of selectors) {
for (const element of document.querySelectorAll(selector)) {
element.classList.remove(className);
}
}
};

// set Percy visibility on elements
add(hide, 'hideInPercy');
if (show.length > 0) {
// hide the body by default
add(['body'], 'hideInPercy');
add(show, 'showInPercy');
}

// convert canvas elements into static images
for (const canvas of queryAll('canvas')) {
const replacements = [];
for (const canvas of document.querySelectorAll('canvas')) {
const image = document.createElement('img');
image.classList.value = canvas.classList.value;
image.src = canvas.toDataURL();
image.style.cssText = window.getComputedStyle(canvas).cssText;
canvas.parentElement.replaceChild(image, canvas);
replacements.push({ canvas, image });
}

// cache the dom snapshot containing the images
const snapshot = agent.snapshot(document, {
widths: [document.documentElement.clientWidth]
});
try {
const agent = new window.PercyAgent({
handleAgentCommunication: false
});

// restore replaced canvases
for (const { image, canvas } of replacements) {
image.parentElement.replaceChild(canvas, image);
}
// cache the dom snapshot containing the images
return agent.snapshot(document, {
widths: [document.documentElement.clientWidth]
});
} finally {
// restore replaced canvases
for (const { image, canvas } of replacements) {
image.parentElement.replaceChild(canvas, image);
}

return snapshot;
// restore element visibility
document.head.removeChild(styleElement);
remove(['body'], 'hideInPercy');
remove(show, 'showInPercy');
remove(hide, 'hideInPercy');
}
}

export const takePercySnapshotWithAgent = `
Expand Down
44 changes: 37 additions & 7 deletions test/visual_regression/services/visual_testing/visual_testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
*/

import { postSnapshot } from '@percy/agent/dist/utils/sdk-utils';

import { Test } from 'mocha';
import _ from 'lodash';

import testSubjSelector from '@kbn/test-subj-selector';

import { pkg } from '../../../../src/legacy/utils/package_json';
import { FtrProviderContext } from '../../ftr_provider_context';
Expand All @@ -31,6 +33,21 @@ export const DEFAULT_OPTIONS = {
widths: [1200],
};

export interface SnapshotOptions {
/**
* name to append to visual test name
*/
name?: string;
/**
* test subject selectiors to __show__ in screenshot
*/
show?: string[];
/**
* test subject selectiors to __hide__ in screenshot
*/
hide?: string[];
}

export async function VisualTestingProvider({ getService }: FtrProviderContext) {
const browser = getService('browser');
const log = getService('log');
Expand All @@ -53,18 +70,21 @@ export async function VisualTestingProvider({ getService }: FtrProviderContext)
}

return new (class VisualTesting {
public async snapshot(name?: string) {
public async snapshot(options: SnapshotOptions = {}) {
log.debug('Capturing percy snapshot');

if (!currentTest) {
throw new Error('unable to determine current test');
}

const [domSnapshot, url] = await Promise.all([this.getSnapshot(), browser.getCurrentUrl()]);

const [domSnapshot, url] = await Promise.all([
this.getSnapshot(options.show, options.hide),
browser.getCurrentUrl(),
]);
const stats = getStats(currentTest);
stats.snapshotCount += 1;

const { name } = options;
const success = await postSnapshot({
name: `${currentTest.fullTitle()} [${name ? name : stats.snapshotCount}]`,
url,
Expand All @@ -78,11 +98,21 @@ export async function VisualTestingProvider({ getService }: FtrProviderContext)
}
}

private async getSnapshot() {
const snapshot = await browser.execute<[], string | false>(takePercySnapshot);
private async getSnapshot(show: string[] = [], hide: string[] = []) {
const showSelectors = show.map(testSubjSelector);
const hideSelectors = hide.map(testSubjSelector);
const snapshot = await browser.execute<[string[], string[]], string | false>(
takePercySnapshot,
showSelectors,
hideSelectors
);
return snapshot !== false
? snapshot
: await browser.execute<[], string>(takePercySnapshotWithAgent);
: await browser.execute<[string[], string[]], string>(
takePercySnapshotWithAgent,
showSelectors,
hideSelectors
);
}
})();
}
46 changes: 33 additions & 13 deletions test/visual_regression/tests/discover/chart_visualization.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,36 @@ export default function ({ getService, getPageObjects }) {

it('should show bars in the correct time zone', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await visualTesting.snapshot();
await PageObjects.discover.waitUntilSearchingHasFinished();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

it('should show correct data for chart interval Hourly', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.setChartInterval('Hourly');
await visualTesting.snapshot();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

it('should show correct data for chart interval Daily', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.setChartInterval('Daily');
await retry.try(async () => {
await visualTesting.snapshot();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

it('should show correct data for chart interval Weekly', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.setChartInterval('Weekly');
await retry.try(async () => {
await visualTesting.snapshot();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

Expand All @@ -84,25 +92,37 @@ export default function ({ getService, getPageObjects }) {
expect(actualInterval).to.be('Daily');
});
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await visualTesting.snapshot();
await PageObjects.discover.waitUntilSearchingHasFinished();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

it('should show correct data for chart interval Monthly', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.setChartInterval('Monthly');
await visualTesting.snapshot();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

it('should show correct data for chart interval Yearly', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.setChartInterval('Yearly');
await visualTesting.snapshot();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

it('should show correct data for chart interval Auto', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.discover.setChartInterval('Auto');
await visualTesting.snapshot();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});
});

Expand All @@ -113,11 +133,11 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.header.awaitKibanaChrome();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await retry.try(async function () {
await visualTesting.snapshot();
await PageObjects.discover.waitUntilSearchingHasFinished();
await visualTesting.snapshot({
show: ['discoverChart'],
});
});

});
});
}

0 comments on commit 5d6cf13

Please sign in to comment.