Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.5] Add ability to whitelist visible elements in percy (#45733) #49881

Merged
merged 1 commit into from
Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'],
});
});

});
});
}