diff --git a/test/functional/services/find.ts b/test/functional/services/find.ts index bdcc5ba95e9fb..09fc32115f683 100644 --- a/test/functional/services/find.ts +++ b/test/functional/services/find.ts @@ -198,7 +198,7 @@ export async function FindProvider({ getService }: FtrProviderContext) { if (isDisplayed) { return descendant; } else { - throw new Error('Element is not displayed'); + throw new Error(`Element "${selector}" is not displayed`); } } diff --git a/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts b/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts new file mode 100644 index 0000000000000..b66f9d2baeb36 --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/reporting/download_csv.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { REPO_ROOT } from '@kbn/dev-utils'; +import expect from '@kbn/expect'; +import fs from 'fs'; +import path from 'path'; +import * as Rx from 'rxjs'; +import { filter, first, map, timeout } from 'rxjs/operators'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const csvPath = path.resolve(REPO_ROOT, 'target/functional-tests/downloads/Ecommerce Data.csv'); + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const dashboardPanelActions = getService('dashboardPanelActions'); + const log = getService('log'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']); + + describe('Reporting Download CSV', () => { + before('initialize tests', async () => { + log.debug('ReportingPage:initTests'); + await esArchiver.loadIfNeeded('reporting/ecommerce'); + await esArchiver.loadIfNeeded('reporting/ecommerce_kibana'); + await browser.setWindowSize(1600, 850); + }); + + after('clean up archives and previous file download', async () => { + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + try { + fs.unlinkSync(csvPath); + } catch (e) { + // nothing to worry + } + }); + + it('Downloads a CSV export of a saved search panel', async function() { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); + const savedSearchPanel = await testSubjects.find('embeddablePanelHeading-EcommerceData'); + await dashboardPanelActions.toggleContextMenu(savedSearchPanel); + + await testSubjects.existOrFail('embeddablePanelAction-downloadCsvReport'); // wait for the full panel to display or else the test runner could click the wrong option! + await testSubjects.click('embeddablePanelAction-downloadCsvReport'); + await testSubjects.existOrFail('csvDownloadStarted'); // validate toast panel + + // check every 100ms for the file to exist in the download dir + // just wait up to 5 seconds + const success$ = Rx.interval(100).pipe( + map(() => fs.existsSync(csvPath)), + filter(value => value === true), + first(), + timeout(5000) + ); + + const fileExists = await success$.toPromise(); + expect(fileExists).to.be(true); + + // no need to validate download contents, API Integration tests do that some different variations + }); + }); +} diff --git a/x-pack/test/functional/apps/dashboard/reporting/index.ts b/x-pack/test/functional/apps/dashboard/reporting/index.ts index 796e15b4e270f..1dc2a958e3dd5 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/index.ts +++ b/x-pack/test/functional/apps/dashboard/reporting/index.ts @@ -3,125 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import expect from '@kbn/expect'; -import fs from 'fs'; -import path from 'path'; -import { promisify } from 'util'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { checkIfPngsMatch } from './lib/compare_pngs'; - -const writeFileAsync = promisify(fs.writeFile); -const mkdirAsync = promisify(fs.mkdir); - -const REPORTS_FOLDER = path.resolve(__dirname, 'reports'); - -export default function({ getService, getPageObjects }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const browser = getService('browser'); - const log = getService('log'); - const config = getService('config'); - const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']); - - describe('Reporting', () => { - before('initialize tests', async () => { - log.debug('ReportingPage:initTests'); - await esArchiver.loadIfNeeded('reporting/ecommerce'); - await esArchiver.loadIfNeeded('reporting/ecommerce_kibana'); - await browser.setWindowSize(1600, 850); - }); - after('clean up archives', async () => { - await esArchiver.unload('reporting/ecommerce'); - await esArchiver.unload('reporting/ecommerce_kibana'); - }); - - describe('Print PDF button', () => { - it('is not available if new', async () => { - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.reporting.openPdfReportingPanel(); - expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true'); - }); - - it('becomes available when saved', async () => { - await PageObjects.dashboard.saveDashboard('My PDF Dashboard'); - await PageObjects.reporting.openPdfReportingPanel(); - expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); - }); - }); - - describe('Print Layout', () => { - it('downloads a PDF file', async function() { - // Generating and then comparing reports can take longer than the default 60s timeout because the comparePngs - // function is taking about 15 seconds per comparison in jenkins. - this.timeout(300000); - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); - await PageObjects.reporting.openPdfReportingPanel(); - await PageObjects.reporting.checkUsePrintLayout(); - await PageObjects.reporting.clickGenerateReportButton(); - - const url = await PageObjects.reporting.getReportURL(60000); - const res = await PageObjects.reporting.getResponse(url); - - expect(res.statusCode).to.equal(200); - expect(res.headers['content-type']).to.equal('application/pdf'); - }); - }); - - describe('Print PNG button', () => { - it('is not available if new', async () => { - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.reporting.openPngReportingPanel(); - expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true'); - }); - - it('becomes available when saved', async () => { - await PageObjects.dashboard.saveDashboard('My PNG Dash'); - await PageObjects.reporting.openPngReportingPanel(); - expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); - }); - }); - - describe('Preserve Layout', () => { - it('matches baseline report', async function() { - const writeSessionReport = async (name: string, rawPdf: Buffer, reportExt: string) => { - const sessionDirectory = path.resolve(REPORTS_FOLDER, 'session'); - await mkdirAsync(sessionDirectory, { recursive: true }); - const sessionReportPath = path.resolve(sessionDirectory, `${name}.${reportExt}`); - await writeFileAsync(sessionReportPath, rawPdf); - return sessionReportPath; - }; - const getBaselineReportPath = (fileName: string, reportExt: string) => { - const baselineFolder = path.resolve(REPORTS_FOLDER, 'baseline'); - const fullPath = path.resolve(baselineFolder, `${fileName}.${reportExt}`); - log.debug(`getBaselineReportPath (${fullPath})`); - return fullPath; - }; - - this.timeout(300000); - - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); - await PageObjects.reporting.openPngReportingPanel(); - await PageObjects.reporting.forceSharedItemsContainerSize({ width: 1405 }); - await PageObjects.reporting.clickGenerateReportButton(); - await PageObjects.reporting.removeForceSharedItemsContainerSize(); - - const url = await PageObjects.reporting.getReportURL(60000); - const reportData = await PageObjects.reporting.getRawPdfReportData(url); - const reportFileName = 'dashboard_preserve_layout'; - const sessionReportPath = await writeSessionReport(reportFileName, reportData, 'png'); - const percentSimilar = await checkIfPngsMatch( - sessionReportPath, - getBaselineReportPath(reportFileName, 'png'), - config.get('screenshots.directory'), - log - ); - expect(percentSimilar).to.be.lessThan(0.1); - }); - }); +export default function({ loadTestFile }: FtrProviderContext) { + describe('Reporting', function() { + loadTestFile(require.resolve('./screenshots')); + loadTestFile(require.resolve('./download_csv')); }); } diff --git a/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts new file mode 100644 index 0000000000000..2cc1686b8c7ca --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/reporting/screenshots.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import fs from 'fs'; +import path from 'path'; +import { promisify } from 'util'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { checkIfPngsMatch } from './lib/compare_pngs'; + +const writeFileAsync = promisify(fs.writeFile); +const mkdirAsync = promisify(fs.mkdir); + +const REPORTS_FOLDER = path.resolve(__dirname, 'reports'); + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const log = getService('log'); + const config = getService('config'); + const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']); + + describe('Screenshots', () => { + before('initialize tests', async () => { + log.debug('ReportingPage:initTests'); + await esArchiver.loadIfNeeded('reporting/ecommerce'); + await esArchiver.loadIfNeeded('reporting/ecommerce_kibana'); + await browser.setWindowSize(1600, 850); + }); + after('clean up archives', async () => { + await esArchiver.unload('reporting/ecommerce'); + await esArchiver.unload('reporting/ecommerce_kibana'); + }); + + describe('Print PDF button', () => { + it('is not available if new', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.reporting.openPdfReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true'); + }); + + it('becomes available when saved', async () => { + await PageObjects.dashboard.saveDashboard('My PDF Dashboard'); + await PageObjects.reporting.openPdfReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); + }); + }); + + describe('Print Layout', () => { + it('downloads a PDF file', async function() { + // Generating and then comparing reports can take longer than the default 60s timeout because the comparePngs + // function is taking about 15 seconds per comparison in jenkins. + this.timeout(300000); + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); + await PageObjects.reporting.openPdfReportingPanel(); + await PageObjects.reporting.checkUsePrintLayout(); + await PageObjects.reporting.clickGenerateReportButton(); + + const url = await PageObjects.reporting.getReportURL(60000); + const res = await PageObjects.reporting.getResponse(url); + + expect(res.statusCode).to.equal(200); + expect(res.headers['content-type']).to.equal('application/pdf'); + }); + }); + + describe('Print PNG button', () => { + it('is not available if new', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.reporting.openPngReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true'); + }); + + it('becomes available when saved', async () => { + await PageObjects.dashboard.saveDashboard('My PNG Dash'); + await PageObjects.reporting.openPngReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); + }); + }); + + describe('Preserve Layout', () => { + it('matches baseline report', async function() { + const writeSessionReport = async (name: string, rawPdf: Buffer, reportExt: string) => { + const sessionDirectory = path.resolve(REPORTS_FOLDER, 'session'); + await mkdirAsync(sessionDirectory, { recursive: true }); + const sessionReportPath = path.resolve(sessionDirectory, `${name}.${reportExt}`); + await writeFileAsync(sessionReportPath, rawPdf); + return sessionReportPath; + }; + const getBaselineReportPath = (fileName: string, reportExt: string) => { + const baselineFolder = path.resolve(REPORTS_FOLDER, 'baseline'); + const fullPath = path.resolve(baselineFolder, `${fileName}.${reportExt}`); + log.debug(`getBaselineReportPath (${fullPath})`); + return fullPath; + }; + + this.timeout(300000); + + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard'); + await PageObjects.reporting.openPngReportingPanel(); + await PageObjects.reporting.forceSharedItemsContainerSize({ width: 1405 }); + await PageObjects.reporting.clickGenerateReportButton(); + await PageObjects.reporting.removeForceSharedItemsContainerSize(); + + const url = await PageObjects.reporting.getReportURL(60000); + const reportData = await PageObjects.reporting.getRawPdfReportData(url); + const reportFileName = 'dashboard_preserve_layout'; + const sessionReportPath = await writeSessionReport(reportFileName, reportData, 'png'); + const percentSimilar = await checkIfPngsMatch( + sessionReportPath, + getBaselineReportPath(reportFileName, 'png'), + config.get('screenshots.directory'), + log + ); + + expect(percentSimilar).to.be.lessThan(0.1); + }); + }); + }); +} diff --git a/x-pack/test/functional/page_objects/reporting_page.ts b/x-pack/test/functional/page_objects/reporting_page.ts index b8cb68edda1c6..93d811f1c84d6 100644 --- a/x-pack/test/functional/page_objects/reporting_page.ts +++ b/x-pack/test/functional/page_objects/reporting_page.ts @@ -9,10 +9,11 @@ import { FtrProviderContext } from 'test/functional/ftr_provider_context'; import { parse } from 'url'; export function ReportingPageProvider({ getService, getPageObjects }: FtrProviderContext) { - const retry = getService('retry'); + const browser = getService('browser'); const log = getService('log'); + const retry = getService('retry'); const testSubjects = getService('testSubjects'); - const browser = getService('browser'); + const PageObjects = getPageObjects(['common', 'security' as any, 'share', 'timePicker']); // FIXME: Security PageObject is not Typescript class ReportingPage {