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

[Vega] add functional tests for Vega visualization #74097

Merged
merged 16 commits into from
Aug 12, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function VegaActionsMenu({ formatHJson, formatJson }: VegaActionsMenuProps) {

return (
<EuiPopover
id="helpMenu"
id="actionsMenu"
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,13 @@ export const SpecViewer = ({ vegaAdapter, ...rest }: SpecViewerProps) => {
<div className="eui-textRight">
<EuiCopy textToCopy={spec}>
{(copy) => (
<EuiButtonEmpty size="xs" flush="right" iconType="copyClipboard" onClick={copy}>
<EuiButtonEmpty
size="xs"
flush="right"
iconType="copyClipboard"
onClick={copy}
data-test-subj="vegaDataInspectorCopyClipboardButton"
>
{copyToClipboardLabel}
</EuiButtonEmpty>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,21 @@ export const VegaDataInspector = ({ adapters }: VegaDataInspectorProps) => {
id: 'data-viewer--id',
name: dataSetsLabel,
content: <DataViewer vegaAdapter={adapters.vega} />,
'data-test-subj': 'vegaDataInspectorDataViewerButton',
},
{
id: 'signal-viewer--id',
name: signalValuesLabel,
content: <SignalViewer vegaAdapter={adapters.vega} />,
'data-test-subj': 'vegaDataInspectorSignalViewerButton',
},
{
id: 'spec-viewer--id',
name: specLabel,
content: (
<SpecViewer className="vgaVegaDataInspector__specViewer" vegaAdapter={adapters.vega} />
),
'data-test-subj': 'vegaDataInspectorSpecViewerButton',
},
];

Expand Down
193 changes: 192 additions & 1 deletion test/functional/apps/visualize/_vega_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,25 @@
* specific language governing permissions and limitations
* under the License.
*/

import { unzip } from 'lodash';
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';

const getTestSpec = (expression: string) => `
{
config: { "kibana": {"renderer": "svg"} }
$schema: https://vega.github.io/schema/vega/v5.json
marks: [{
type: text
encode: { update: { text: { value: "Test" } } }
}]
signals: [ {
on: [{
events: click
update: ${expression}
}]
}]}`;

export default function ({ getPageObjects, getService }: FtrProviderContext) {
const PageObjects = getPageObjects([
'timePicker',
Expand All @@ -29,7 +44,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
'vegaChart',
]);
const filterBar = getService('filterBar');
const inspector = getService('inspector');
const vegaDebugInspectorView = getService('vegaDebugInspector');
const log = getService('log');
const retry = getService('retry');
const browser = getService('browser');

describe('vega chart in visualize app', () => {
before(async () => {
Expand Down Expand Up @@ -88,5 +107,177 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
});
});

describe('Inspector Panel', () => {
it('should have inspector enabled', async () => {
await inspector.expectIsEnabled();
});

describe('Request Tab', () => {
beforeEach(async () => {
await inspector.open();
});

afterEach(async () => {
await inspector.close();
});

it('should contain "Statistics", "Request", "Response" tabs', async () => {
await inspector.openInspectorRequestsView();

for (const getFn of [
inspector.getOpenRequestDetailRequestButton,
inspector.getOpenRequestDetailResponseButton,
inspector.getOpenRequestStatisticButton,
]) {
await retry.try(async () => {
const requestStatisticTab = await getFn();

expect(await requestStatisticTab.isEnabled()).to.be(true);
});
}
});

it('should set the default query name if not given in the schema', async () => {
const requests = await inspector.getRequestNames();

expect(requests).to.be('Unnamed request #0');
});

it('should log the request statistic', async () => {
await inspector.openInspectorRequestsView();
const rawTableData = await inspector.getTableData();

expect(unzip(rawTableData)[0].join(', ')).to.be(
'Hits, Hits (total), Query time, Request timestamp'
);
});
});

describe('Debug Tab', () => {
beforeEach(async () => {
await inspector.open();
});

afterEach(async () => {
await inspector.close();
});

it('should contain "Data Sets", "Signal Values", "Spec" tabs', async () => {
await vegaDebugInspectorView.openVegaDebugInspectorView();

for (const getFn of [
vegaDebugInspectorView.getOpenDataViewerButton,
vegaDebugInspectorView.getOpenSignalViewerButton,
vegaDebugInspectorView.getOpenSpecViewerButton,
]) {
await retry.try(async () => {
const requestStatisticTab = await getFn();

expect(await requestStatisticTab.isEnabled()).to.be(true);
});
}
});

it('should contain data on "Signal Values" tab', async () => {
await vegaDebugInspectorView.openVegaDebugInspectorView();
await vegaDebugInspectorView.navigateToSignalViewerTab();

const { rows, columns } = await vegaDebugInspectorView.getGridTableData();

expect(columns.join(', ')).to.be('Signal, Value');
expect(rows.length).to.be.greaterThan(0);
expect(rows[0].length).to.be(2);
});

it('should contain data on "Signal Values" tab', async () => {
await vegaDebugInspectorView.openVegaDebugInspectorView();
await vegaDebugInspectorView.navigateToDataViewerTab();

const { rows, columns } = await vegaDebugInspectorView.getGridTableData();

expect(columns.length).to.be.greaterThan(0);
expect(rows.length).to.be.greaterThan(0);
});

it('should be able to copy vega spec to clipboard', async () => {
await vegaDebugInspectorView.openVegaDebugInspectorView();
await vegaDebugInspectorView.navigateToSpecViewerTab();

const copyCopyToClipboardButton = await vegaDebugInspectorView.getCopyClipboardButton();

expect(await copyCopyToClipboardButton.isEnabled()).to.be(true);

// The "clipboard-read" permission of the Permissions API must be granted
if (!(await browser.checkBrowserPermission('clipboard-read'))) {
return;
}
dmlemeshko marked this conversation as resolved.
Show resolved Hide resolved

await copyCopyToClipboardButton.click();

expect(
(await browser.getClipboardValue()).includes(
'"$schema": "https://vega.github.io/schema/vega-lite/'
)
).to.be(true);
});
});
});

describe('Vega extension functions', () => {
beforeEach(async () => {
await filterBar.removeAllFilters();
});

const fillSpecAndGo = async (newSpec: string) => {
await PageObjects.vegaChart.fillSpec(newSpec);
await PageObjects.visEditor.clickGo();

const viewContainer = await PageObjects.vegaChart.getViewContainer();
const textElement = await viewContainer.findByTagName('text');

await textElement.click();
};

it('should update global time range by calling "kibanaSetTimeFilter" expression', async () => {
await fillSpecAndGo(getTestSpec('kibanaSetTimeFilter("2019", "2020")'));

const currentTimeRange = await PageObjects.timePicker.getTimeConfig();

expect(currentTimeRange.start).to.be('Jan 1, 2019 @ 00:00:00.000');
expect(currentTimeRange.end).to.be('Jan 1, 2020 @ 00:00:00.000');
});

it('should set filter by calling "kibanaAddFilter" expression', async () => {
await fillSpecAndGo(
getTestSpec('kibanaAddFilter({ query_string: { query: "response:200" }})')
);

expect(await filterBar.getFilterCount()).to.be(1);
});

it('should remove filter by calling "kibanaRemoveFilter" expression', async () => {
await filterBar.addFilter('response', 'is', '200');

expect(await filterBar.getFilterCount()).to.be(1);

await fillSpecAndGo(
getTestSpec('kibanaRemoveFilter({ match_phrase: { response: "200" }})')
);

expect(await filterBar.getFilterCount()).to.be(0);
});

it('should remove all filters by calling "kibanaRemoveAllFilters" expression', async () => {
await filterBar.addFilter('response', 'is', '200');
await filterBar.addFilter('response', 'is', '500');

expect(await filterBar.getFilterCount()).to.be(2);

await fillSpecAndGo(getTestSpec('kibanaRemoveAllFilters()'));

expect(await filterBar.getFilterCount()).to.be(0);
});
});
});
}
65 changes: 54 additions & 11 deletions test/functional/page_objects/vega_chart_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@
*/

import { Key } from 'selenium-webdriver';
import expect from '@kbn/expect';
import { FtrProviderContext } from '../ftr_provider_context';

const compareSpecs = (first: string, second: string) => {
const normalizeSpec = (spec: string) => spec.replace(/[\n ]/g, '');
return normalizeSpec(first) === normalizeSpec(second);
};

export function VegaChartPageProvider({
getService,
getPageObjects,
Expand All @@ -28,24 +34,57 @@ export function VegaChartPageProvider({
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const { common } = getPageObjects(['common']);
const retry = getService('retry');

class VegaChartPage {
public async getSpec() {
public getEditor() {
return testSubjects.find('vega-editor');
}

public getViewContainer() {
return find.byCssSelector('div.vgaVis__view');
}

public getControlContainer() {
return find.byCssSelector('div.vgaVis__controls');
}

public async getRawSpec() {
// Adapted from console_page.js:getVisibleTextFromAceEditor(). Is there a common utilities file?
const editor = await testSubjects.find('vega-editor');
const editor = await this.getEditor();
const lines = await editor.findAllByClassName('ace_line_group');
const linesText = await Promise.all(

return await Promise.all(
lines.map(async (line) => {
return await line.getVisibleText();
})
);
return linesText.join('\n');
}

public async typeInSpec(text: string) {
const editor = await testSubjects.find('vega-editor');
public async getSpec() {
return (await this.getRawSpec()).join('\n');
}

public async focusEditor() {
const editor = await this.getEditor();
const textarea = await editor.findByClassName('ace_content');

await textarea.click();
}

public async fillSpec(newSpec: string) {
await retry.try(async () => {
await this.cleanSpec();
await this.focusEditor();
await browser.pressKeys(newSpec);

expect(compareSpecs(await this.getSpec(), newSpec)).to.be(true);
});
}

public async typeInSpec(text: string) {
await this.focusEditor();

let repeats = 20;
while (--repeats > 0) {
await browser.pressKeys(Key.ARROW_UP);
Expand All @@ -55,12 +94,16 @@ export function VegaChartPageProvider({
await browser.pressKeys(text);
}

public async getViewContainer() {
return await find.byCssSelector('div.vgaVis__view');
}
public async cleanSpec() {
const editor = await this.getEditor();
const aceGutter = await editor.findByClassName('ace_gutter');

await retry.try(async () => {
await aceGutter.doubleClick();
await browser.pressKeys(Key.BACK_SPACE);

public async getControlContainer() {
return await find.byCssSelector('div.vgaVis__controls');
expect(await this.getSpec()).to.be('');
});
}

public async getYAxisLabels() {
Expand Down
12 changes: 12 additions & 0 deletions test/functional/services/common/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,5 +489,17 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
const _id = idOrElement instanceof WebElementWrapper ? idOrElement._webElement : idOrElement;
await driver.switchTo().frame(_id);
}

public async checkBrowserPermission(permission: string): Promise<boolean> {
const result: any = await driver.executeAsyncScript(
`navigator.permissions.query({name:'${permission}'}).then(arguments[0])`
);

return Boolean(result?.state === 'granted');
}

public getClipboardValue(): Promise<string> {
dmlemeshko marked this conversation as resolved.
Show resolved Hide resolved
return driver.executeAsyncScript('navigator.clipboard.readText().then(arguments[0])');
}
})();
}
Loading