e2e: remove old toolkit e2e implementation (#23091)
This commit is contained in:
@@ -15,13 +15,7 @@ import { searchTestDataSetupTask } from './tasks/searchTestDataSetup';
|
||||
import { closeMilestoneTask } from './tasks/closeMilestone';
|
||||
import { pluginDevTask } from './tasks/plugin.dev';
|
||||
import { githubPublishTask } from './tasks/plugin.utils';
|
||||
import {
|
||||
ciBuildPluginTask,
|
||||
ciBuildPluginDocsTask,
|
||||
ciPackagePluginTask,
|
||||
ciTestPluginTask,
|
||||
ciPluginReportTask,
|
||||
} from './tasks/plugin.ci';
|
||||
import { ciBuildPluginTask, ciBuildPluginDocsTask, ciPackagePluginTask, ciPluginReportTask } from './tasks/plugin.ci';
|
||||
import { buildPackageTask } from './tasks/package.build';
|
||||
import { pluginCreateTask } from './tasks/plugin.create';
|
||||
|
||||
@@ -191,14 +185,6 @@ export const run = (includeInternalScripts = false) => {
|
||||
await execTask(ciPackagePluginTask)({});
|
||||
});
|
||||
|
||||
program
|
||||
.command('plugin:ci-test')
|
||||
.option('--full', 'run all the tests (even stuff that will break)')
|
||||
.description('end-to-end test using bundle in /artifacts')
|
||||
.action(async cmd => {
|
||||
await execTask(ciTestPluginTask)({});
|
||||
});
|
||||
|
||||
program
|
||||
.command('plugin:ci-report')
|
||||
.description('Build a report for this whole process')
|
||||
|
||||
@@ -3,15 +3,13 @@ import { pluginBuildRunner } from './plugin.build';
|
||||
import { restoreCwd } from '../utils/cwd';
|
||||
import { getPluginJson } from '../../config/utils/pluginValidation';
|
||||
import { getPluginId } from '../../config/utils/getPluginId';
|
||||
import { PluginMeta } from '@grafana/data';
|
||||
|
||||
// @ts-ignore
|
||||
import execa = require('execa');
|
||||
import path = require('path');
|
||||
import fs from 'fs-extra';
|
||||
import { getPackageDetails, findImagesInFolder, getGrafanaVersions, readGitLog } from '../../plugins/utils';
|
||||
import { getPackageDetails, getGrafanaVersions, readGitLog } from '../../plugins/utils';
|
||||
import {
|
||||
job,
|
||||
getJobFolder,
|
||||
writeJobStats,
|
||||
getCiFolder,
|
||||
@@ -20,9 +18,7 @@ import {
|
||||
getCircleDownloadBaseURL,
|
||||
} from '../../plugins/env';
|
||||
import { agregateWorkflowInfo, agregateCoverageInfo, agregateTestInfo } from '../../plugins/workflow';
|
||||
import { PluginPackageDetails, PluginBuildReport, TestResultsInfo } from '../../plugins/types';
|
||||
import { runEndToEndTests } from '../../plugins/e2e/launcher';
|
||||
import { getEndToEndSettings } from '../../plugins/index';
|
||||
import { PluginPackageDetails, PluginBuildReport } from '../../plugins/types';
|
||||
import { manifestTask } from './manifest';
|
||||
import { execTask } from '../utils/execTask';
|
||||
import rimrafCallback from 'rimraf';
|
||||
@@ -235,85 +231,6 @@ const packagePluginRunner: TaskRunner<PluginCIOptions> = async () => {
|
||||
|
||||
export const ciPackagePluginTask = new Task<PluginCIOptions>('Bundle Plugin', packagePluginRunner);
|
||||
|
||||
/**
|
||||
* 3. Test (end-to-end)
|
||||
*
|
||||
* deploy the zip to a running grafana instance
|
||||
*
|
||||
*/
|
||||
const testPluginRunner: TaskRunner<PluginCIOptions> = async ({}) => {
|
||||
const start = Date.now();
|
||||
const workDir = getJobFolder();
|
||||
const results: TestResultsInfo = { job, passed: 0, failed: 0, screenshots: [] };
|
||||
const args = {
|
||||
withCredentials: true,
|
||||
baseURL: process.env.BASE_URL || 'http://localhost:3000/',
|
||||
responseType: 'json',
|
||||
auth: {
|
||||
username: 'admin',
|
||||
password: 'admin',
|
||||
},
|
||||
};
|
||||
|
||||
const settings = getEndToEndSettings();
|
||||
await execa('rimraf', [settings.outputFolder]);
|
||||
fs.mkdirSync(settings.outputFolder);
|
||||
|
||||
const tempDir = path.resolve(process.cwd(), 'e2e-temp');
|
||||
await execa('rimraf', [tempDir]);
|
||||
fs.mkdirSync(tempDir);
|
||||
|
||||
try {
|
||||
const axios = require('axios');
|
||||
const frontendSettings = await axios.get('api/frontend/settings', args);
|
||||
results.grafana = frontendSettings.data.buildInfo;
|
||||
|
||||
console.log('Grafana: ' + JSON.stringify(results.grafana, null, 2));
|
||||
|
||||
const loadedMetaRsp = await axios.get(`api/plugins/${settings.plugin.id}/settings`, args);
|
||||
const loadedMeta: PluginMeta = loadedMetaRsp.data;
|
||||
console.log('Plugin Info: ' + JSON.stringify(loadedMeta, null, 2));
|
||||
if (loadedMeta.info.build) {
|
||||
const currentHash = settings.plugin.info.build!.hash;
|
||||
console.log('Check version: ', settings.plugin.info.build);
|
||||
if (loadedMeta.info.build.hash !== currentHash) {
|
||||
console.warn(`Testing wrong plugin version. Expected: ${currentHash}, found: ${loadedMeta.info.build.hash}`);
|
||||
throw new Error('Wrong plugin version');
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync('e2e-temp')) {
|
||||
fs.mkdirSync(tempDir);
|
||||
}
|
||||
|
||||
await execa('cp', [
|
||||
'node_modules/@grafana/toolkit/src/plugins/e2e/commonPluginTests.ts',
|
||||
path.resolve(tempDir, 'common.test.ts'),
|
||||
]);
|
||||
|
||||
await runEndToEndTests(settings.outputFolder, results);
|
||||
} catch (err) {
|
||||
results.error = err;
|
||||
console.log('Test Error', err);
|
||||
}
|
||||
await execa('rimraf', [tempDir]);
|
||||
|
||||
// Now copy everything to work folder
|
||||
await execa('cp', ['-rv', settings.outputFolder + '/.', workDir]);
|
||||
results.screenshots = findImagesInFolder(workDir);
|
||||
|
||||
const f = path.resolve(workDir, 'results.json');
|
||||
fs.writeFile(f, JSON.stringify(results, null, 2), err => {
|
||||
if (err) {
|
||||
throw new Error('Error saving: ' + f);
|
||||
}
|
||||
});
|
||||
|
||||
writeJobStats(start, workDir);
|
||||
};
|
||||
|
||||
export const ciTestPluginTask = new Task<PluginCIOptions>('Test Plugin (e2e)', testPluginRunner);
|
||||
|
||||
/**
|
||||
* 4. Report
|
||||
*
|
||||
|
||||
@@ -61,9 +61,6 @@ const copyFiles = () => {
|
||||
'src/config/eslint.plugin.json',
|
||||
'src/config/styles.mock.js',
|
||||
'src/config/jest.plugin.config.local.js',
|
||||
|
||||
// plugin test file
|
||||
'src/plugins/e2e/commonPluginTests.ts',
|
||||
];
|
||||
// @ts-ignore
|
||||
return useSpinner<void>(`Moving ${files.join(', ')} files`, async () => {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export const constants = {
|
||||
baseUrl: process.env.BASE_URL || 'http://localhost:3000',
|
||||
chromiumRevision: '650629',
|
||||
screenShotsTruthDir: './public/e2e-test/screenShots/theTruth',
|
||||
screenShotsOutputDir: './public/e2e-test/screenShots/theOutput',
|
||||
};
|
||||
@@ -1,84 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import { PNG } from 'pngjs';
|
||||
import { Page } from 'puppeteer-core';
|
||||
import pixelmatch from 'pixelmatch';
|
||||
|
||||
import { constants } from './constants';
|
||||
|
||||
export const takeScreenShot = async (page: Page, fileName: string) => {
|
||||
const outputFolderExists = fs.existsSync(constants.screenShotsOutputDir);
|
||||
if (!outputFolderExists) {
|
||||
fs.mkdirSync(constants.screenShotsOutputDir);
|
||||
}
|
||||
const path = `${constants.screenShotsOutputDir}/${fileName}.png`;
|
||||
await page.screenshot({ path, type: 'png', fullPage: false });
|
||||
};
|
||||
|
||||
export const compareScreenShots = async (fileName: string) =>
|
||||
new Promise(resolve => {
|
||||
let filesRead = 0;
|
||||
|
||||
const doneReading = () => {
|
||||
if (++filesRead < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (screenShotFromTest.width !== screenShotFromTruth.width) {
|
||||
throw new Error(
|
||||
`The screenshot:[${fileName}] taken during the test has a ` +
|
||||
`width:[${screenShotFromTest.width}] that differs from the ` +
|
||||
`expected: [${screenShotFromTruth.width}].`
|
||||
);
|
||||
}
|
||||
|
||||
if (screenShotFromTest.height !== screenShotFromTruth.height) {
|
||||
throw new Error(
|
||||
`The screenshot:[${fileName}] taken during the test has a ` +
|
||||
`height:[${screenShotFromTest.height}] that differs from the ` +
|
||||
`expected: [${screenShotFromTruth.height}].`
|
||||
);
|
||||
}
|
||||
|
||||
const diff = new PNG({ width: screenShotFromTest.width, height: screenShotFromTruth.height });
|
||||
const numDiffPixels = pixelmatch(
|
||||
screenShotFromTest.data,
|
||||
screenShotFromTruth.data,
|
||||
diff.data,
|
||||
screenShotFromTest.width,
|
||||
screenShotFromTest.height,
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
|
||||
if (numDiffPixels !== 0) {
|
||||
const localMessage =
|
||||
`\nCompare the output from expected:[${constants.screenShotsTruthDir}] ` +
|
||||
`with outcome:[${constants.screenShotsOutputDir}]`;
|
||||
const circleCIMessage = '\nCheck the Artifacts tab in the CircleCi build output for the actual screenshots.';
|
||||
const checkMessage = process.env.CIRCLE_SHA1 ? circleCIMessage : localMessage;
|
||||
let msg =
|
||||
`\nThe screenshot:[${constants.screenShotsOutputDir}/${fileName}.png] ` +
|
||||
`taken during the test differs by:[${numDiffPixels}] pixels from the expected.`;
|
||||
msg += '\n';
|
||||
msg += checkMessage;
|
||||
msg += '\n';
|
||||
msg += '\n If the difference between expected and outcome is NOT acceptable then do the following:';
|
||||
msg += '\n - Check the code for changes that causes this difference, fix that and retry.';
|
||||
msg += '\n';
|
||||
msg += '\n If the difference between expected and outcome is acceptable then do the following:';
|
||||
msg += '\n - Replace the expected image with the outcome and retry.';
|
||||
msg += '\n';
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
resolve();
|
||||
};
|
||||
|
||||
const screenShotFromTest = fs
|
||||
.createReadStream(`${constants.screenShotsOutputDir}/${fileName}.png`)
|
||||
.pipe(new PNG())
|
||||
.on('parsed', doneReading);
|
||||
const screenShotFromTruth = fs
|
||||
.createReadStream(`${constants.screenShotsTruthDir}/${fileName}.png`)
|
||||
.pipe(new PNG())
|
||||
.on('parsed', doneReading);
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
export * from './constants';
|
||||
export * from './images';
|
||||
export * from './install';
|
||||
export * from './launcher';
|
||||
export * from './login';
|
||||
export * from './pageObjects';
|
||||
export * from './pageInfo';
|
||||
export * from './scenario';
|
||||
|
||||
import * as pages from './pages';
|
||||
export { pages };
|
||||
@@ -1,24 +0,0 @@
|
||||
import puppeteer from 'puppeteer-core';
|
||||
import { constants } from './constants';
|
||||
|
||||
export const downloadBrowserIfNeeded = async (): Promise<void> => {
|
||||
const browserFetcher = puppeteer.createBrowserFetcher();
|
||||
const localRevisions = await browserFetcher.localRevisions();
|
||||
if (localRevisions && localRevisions.length > 0) {
|
||||
console.log('Found a local revision for browser, exiting install.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Did not find any local revisions for browser, downloading latest this might take a while.');
|
||||
await browserFetcher.download(constants.chromiumRevision, (downloaded, total) => {
|
||||
if (downloaded === total) {
|
||||
console.log('Chromium successfully downloaded');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
console.log('Checking Chromium');
|
||||
jest.setTimeout(60 * 1000);
|
||||
await downloadBrowserIfNeeded();
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import puppeteer, { Browser } from 'puppeteer-core';
|
||||
|
||||
export const launchBrowser = async (): Promise<Browser> => {
|
||||
const browserFetcher = puppeteer.createBrowserFetcher();
|
||||
const localRevisions = await browserFetcher.localRevisions();
|
||||
if (localRevisions.length === 0) {
|
||||
throw new Error('Could not launch browser because there is no local revisions.');
|
||||
}
|
||||
|
||||
let executablePath = null;
|
||||
executablePath = browserFetcher.revisionInfo(localRevisions[0]).executablePath;
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: process.env.BROWSER ? false : true,
|
||||
slowMo: process.env.SLOWMO ? 100 : 0,
|
||||
defaultViewport: {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
deviceScaleFactor: 1,
|
||||
isMobile: false,
|
||||
hasTouch: false,
|
||||
isLandscape: false,
|
||||
},
|
||||
args: ['--start-fullscreen'],
|
||||
executablePath,
|
||||
});
|
||||
|
||||
return browser;
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
|
||||
import { constants } from './constants';
|
||||
import { loginPage } from './pages/loginPage';
|
||||
|
||||
export const login = async (page: Page) => {
|
||||
await loginPage.init(page);
|
||||
await loginPage.navigateTo();
|
||||
|
||||
await loginPage.pageObjects.username.enter('admin');
|
||||
await loginPage.pageObjects.password.enter('admin');
|
||||
await loginPage.pageObjects.submit.click();
|
||||
await loginPage.waitForResponse();
|
||||
};
|
||||
|
||||
export const ensureLoggedIn = async (page: Page) => {
|
||||
await page.goto(`${constants.baseUrl}`);
|
||||
if (page.url().indexOf('login') > -1) {
|
||||
console.log('Redirected to login page. Logging in...');
|
||||
await login(page);
|
||||
}
|
||||
};
|
||||
@@ -1,123 +0,0 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
import { constants } from './constants';
|
||||
import { PageObject, Selector } from './pageObjects';
|
||||
|
||||
export interface ExpectSelectorConfig {
|
||||
selector: string;
|
||||
containsText?: string;
|
||||
isVisible?: boolean;
|
||||
}
|
||||
|
||||
export interface TestPageType<T> {
|
||||
init: (page: Page) => Promise<void>;
|
||||
getUrl: () => Promise<string>;
|
||||
getUrlWithoutBaseUrl: () => Promise<string>;
|
||||
navigateTo: () => Promise<void>;
|
||||
expectSelector: (config: ExpectSelectorConfig) => Promise<void>;
|
||||
waitForResponse: () => Promise<void>;
|
||||
waitForNavigation: () => Promise<void>;
|
||||
waitFor: (milliseconds: number) => Promise<void>;
|
||||
|
||||
pageObjects?: PageObjects<T>;
|
||||
}
|
||||
|
||||
type PageObjects<T> = { [P in keyof T]: T[P] };
|
||||
type SelectorFunc = () => string;
|
||||
|
||||
export interface TestPageConfig<T> {
|
||||
url?: string;
|
||||
pageObjects: { [P in keyof T]: string | SelectorFunc };
|
||||
}
|
||||
|
||||
export class TestPage<T> implements TestPageType<T> {
|
||||
pageObjects: PageObjects<T>;
|
||||
private page?: Page;
|
||||
private readonly pageUrl?: string;
|
||||
|
||||
constructor(config: TestPageConfig<T>) {
|
||||
if (config.url) {
|
||||
this.pageUrl = `${constants.baseUrl}${config.url}`;
|
||||
}
|
||||
|
||||
this.pageObjects = {} as PageObjects<T>;
|
||||
Object.keys(config.pageObjects).map(async key => {
|
||||
const selector = (config.pageObjects as any)[key];
|
||||
if (typeof selector === 'function') {
|
||||
(this.pageObjects as any)[key] = new PageObject(selector());
|
||||
}
|
||||
|
||||
if (typeof selector === 'string') {
|
||||
(this.pageObjects as any)[key] = new PageObject(Selector.fromAriaLabel(selector));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
init = async (page: Page): Promise<void> => {
|
||||
this.page = page;
|
||||
|
||||
if (!this.pageObjects) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(this.pageObjects).forEach(key => {
|
||||
// @ts-ignore
|
||||
const pageObject: PageObject = this.pageObjects[key];
|
||||
pageObject.init(page);
|
||||
});
|
||||
};
|
||||
|
||||
navigateTo = async (): Promise<void> => {
|
||||
this.throwIfNotInitialized();
|
||||
|
||||
console.log('Trying to navigate to:', this.pageUrl);
|
||||
await this.page!.goto(this.pageUrl!);
|
||||
};
|
||||
|
||||
expectSelector = async (config: ExpectSelectorConfig): Promise<void> => {
|
||||
this.throwIfNotInitialized();
|
||||
|
||||
const { selector, containsText, isVisible } = config;
|
||||
const visible = isVisible || true;
|
||||
const text = containsText;
|
||||
const options = { visible, text } as any;
|
||||
await expect(this.page).toMatchElement(selector, options);
|
||||
};
|
||||
|
||||
waitForResponse = async (): Promise<void> => {
|
||||
this.throwIfNotInitialized();
|
||||
|
||||
await this.page!.waitForResponse(response => response.url() === this.pageUrl && response.status() === 200);
|
||||
};
|
||||
|
||||
waitForNavigation = async (): Promise<void> => {
|
||||
this.throwIfNotInitialized();
|
||||
|
||||
await this.page!.waitForNavigation();
|
||||
};
|
||||
|
||||
getUrl = async (): Promise<string> => {
|
||||
this.throwIfNotInitialized();
|
||||
|
||||
return await this.page!.url();
|
||||
};
|
||||
|
||||
getUrlWithoutBaseUrl = async (): Promise<string> => {
|
||||
this.throwIfNotInitialized();
|
||||
|
||||
const url = await this.getUrl();
|
||||
|
||||
return url.replace(constants.baseUrl, '');
|
||||
};
|
||||
|
||||
waitFor = async (milliseconds: number) => {
|
||||
this.throwIfNotInitialized();
|
||||
|
||||
await this.page!.waitFor(milliseconds);
|
||||
};
|
||||
|
||||
private throwIfNotInitialized = () => {
|
||||
if (!this.page) {
|
||||
throw new Error('pageFactory has not been initilized, did you forget to call init with a page?');
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
|
||||
export class Selector {
|
||||
static fromAriaLabel = (selector: string) => {
|
||||
return `[aria-label="${selector}"]`;
|
||||
};
|
||||
|
||||
static fromSwitchLabel = (selector: string) => {
|
||||
return `${Selector.fromAriaLabel(selector)} .gf-form-switch input`;
|
||||
};
|
||||
|
||||
static fromSelector = (selector: string) => {
|
||||
return selector;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PageObjectType {
|
||||
init: (page: Page) => Promise<void>;
|
||||
exists: () => Promise<void>;
|
||||
containsText: (text: string) => Promise<void>;
|
||||
waitForSelector: (timeoutInMs?: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ClickablePageObjectType extends PageObjectType {
|
||||
click: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface InputPageObjectType extends PageObjectType {
|
||||
enter: (text: string) => Promise<void>;
|
||||
containsPlaceholder: (text: string) => Promise<void>;
|
||||
blur: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface SelectPageObjectType extends PageObjectType {
|
||||
select: (text: string) => Promise<void>;
|
||||
selectedTextIs: (text: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface SwitchPageObjectType extends PageObjectType {
|
||||
toggle: () => Promise<void>;
|
||||
isSwitchedOn: () => Promise<void>;
|
||||
isSwitchedOff: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface ArrayPageObjectType {
|
||||
hasLength: (length: number) => Promise<void>;
|
||||
clickAtPos: (index: number) => Promise<void>;
|
||||
containsTextAtPos: (text: string, index: number) => Promise<void>;
|
||||
waitForSelector: (timeoutInMs?: number) => Promise<void>;
|
||||
}
|
||||
|
||||
export class PageObject
|
||||
implements
|
||||
PageObjectType,
|
||||
ClickablePageObjectType,
|
||||
InputPageObjectType,
|
||||
SelectPageObjectType,
|
||||
SwitchPageObjectType,
|
||||
ArrayPageObjectType {
|
||||
protected page?: Page;
|
||||
|
||||
constructor(protected selector: string) {}
|
||||
|
||||
init = async (page: Page): Promise<void> => {
|
||||
this.page = page;
|
||||
};
|
||||
|
||||
exists = async (): Promise<void> => {
|
||||
console.log('Checking for existence of:', this.selector);
|
||||
const options = { visible: true } as any;
|
||||
await expect(this.page).not.toBeNull();
|
||||
await expect(this.page).toMatchElement(this.selector, options);
|
||||
};
|
||||
|
||||
containsText = async (text: string): Promise<void> => {
|
||||
console.log(`Checking for existence of '${text}' for:`, this.selector);
|
||||
const options = { visible: true, text } as any;
|
||||
await expect(this.page).not.toBeNull();
|
||||
await expect(this.page).toMatchElement(this.selector, options);
|
||||
};
|
||||
|
||||
containsPlaceholder = async (expectedPlaceholder: string): Promise<void> => {
|
||||
console.log(`Checking for placeholder '${expectedPlaceholder}' in:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const placeholder = await this.page!.$eval(this.selector, (input: any) => input.placeholder);
|
||||
await expect(placeholder).toEqual(expectedPlaceholder);
|
||||
};
|
||||
|
||||
hasLength = async (length: number): Promise<void> => {
|
||||
console.log('Checking for length of', this.selector);
|
||||
const result = await this.page!.$$eval(this.selector, elements => elements.length);
|
||||
await expect(result).toEqual(length);
|
||||
};
|
||||
|
||||
containsTextAtPos = async (text: string, index: number): Promise<void> => {
|
||||
console.log(`Checking for text ${text} at position ${index} of`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const result = await this.page!.$$eval(this.selector, elements => elements.map((el: any) => el.innerText));
|
||||
await expect(result[index]!.trim()).toEqual(text);
|
||||
};
|
||||
|
||||
click = async (): Promise<void> => {
|
||||
console.log('Trying to click on:', this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await expect(this.page).toClick(this.selector);
|
||||
};
|
||||
|
||||
clickAtPos = async (index: number): Promise<void> => {
|
||||
console.log(`Trying to clicking at position:${index} on:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const elements = await this.page!.$$(this.selector);
|
||||
const element = await elements[index];
|
||||
await element.click();
|
||||
};
|
||||
|
||||
toggle = async (): Promise<void> => {
|
||||
const switchSelector = this.selector.replace(' .gf-form-switch input', '');
|
||||
console.log('Trying to toggle:', switchSelector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await expect(this.page).toClick(switchSelector);
|
||||
};
|
||||
|
||||
enter = async (text: string): Promise<void> => {
|
||||
console.log(`Trying to enter text:${text} into:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await expect(this.page).toFill(this.selector, text);
|
||||
};
|
||||
|
||||
select = async (text: string): Promise<void> => {
|
||||
console.log(`Trying to select text:${text} in dropdown:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await this.page!.select(this.selector, text);
|
||||
};
|
||||
|
||||
selectedTextIs = async (text: string): Promise<void> => {
|
||||
console.log(`Trying to get selected text from dropdown:`, this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
const selectedText = await this.page!.$eval(this.selector, (select: any) => {
|
||||
if (select.selectedIndex === -1) {
|
||||
return '';
|
||||
}
|
||||
return select.options[select.selectedIndex].innerText;
|
||||
});
|
||||
await expect(selectedText).toEqual(text);
|
||||
};
|
||||
|
||||
waitForSelector = async (timeoutInMs?: number): Promise<void> => {
|
||||
console.log('Waiting for', this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await this.page!.waitForSelector(this.selector, { timeout: timeoutInMs || 1000 });
|
||||
};
|
||||
|
||||
isSwitchedOn = async (): Promise<void> => {
|
||||
const checked = await this.getChecked();
|
||||
await expect(checked).toBe(true);
|
||||
};
|
||||
|
||||
isSwitchedOff = async (): Promise<void> => {
|
||||
const checked = await this.getChecked();
|
||||
await expect(checked).toBe(false);
|
||||
};
|
||||
|
||||
blur = async (): Promise<void> => {
|
||||
console.log('Trying to blur:', this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
await this.page!.$eval(this.selector, (input: any) => input.blur());
|
||||
};
|
||||
|
||||
private getChecked = async (): Promise<boolean> => {
|
||||
console.log('Trying get switch status for:', this.selector);
|
||||
await expect(this.page).not.toBeNull();
|
||||
return await this.page!.$eval(this.selector, (input: any) => input.checked);
|
||||
};
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
import { dashboardPage } from './dashboardPage';
|
||||
import { dashboardSettingsPage } from './dashboardSettingsPage';
|
||||
import { saveDashboardModal } from './saveDashboardModal';
|
||||
import { dashboardsPageFactory } from './dashboardsPage';
|
||||
import { confirmModal } from '../modals/confirmModal';
|
||||
|
||||
export interface CreateDashboardPage {
|
||||
addQuery: ClickablePageObjectType;
|
||||
saveDashboard: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const createDashboardPage = new TestPage<CreateDashboardPage>({
|
||||
url: '/dashboard/new',
|
||||
pageObjects: {
|
||||
addQuery: 'Add Query CTA button',
|
||||
saveDashboard: 'Save dashboard navbar button',
|
||||
},
|
||||
});
|
||||
|
||||
export const createEmptyDashboardPage = async (page: Page, dashboardTitle: string) => {
|
||||
await createDashboardPage.init(page);
|
||||
await createDashboardPage.navigateTo();
|
||||
await createDashboardPage.pageObjects.saveDashboard.click();
|
||||
|
||||
await saveDashboardModal.init(page);
|
||||
await saveDashboardModal.expectSelector({ selector: 'save-dashboard-as-modal' });
|
||||
await saveDashboardModal.pageObjects.name.enter(dashboardTitle);
|
||||
await saveDashboardModal.pageObjects.save.click();
|
||||
await saveDashboardModal.pageObjects.success.exists();
|
||||
|
||||
await dashboardPage.init(page);
|
||||
return dashboardPage;
|
||||
};
|
||||
|
||||
export const cleanDashboard = async (page: Page, dashboardTitle: string) => {
|
||||
const dashboardsPage = dashboardsPageFactory(dashboardTitle);
|
||||
await dashboardsPage.init(page);
|
||||
await dashboardsPage.navigateTo();
|
||||
await dashboardsPage.pageObjects.dashboard.exists();
|
||||
await dashboardsPage.pageObjects.dashboard.click();
|
||||
|
||||
await dashboardPage.init(page);
|
||||
await dashboardPage.pageObjects.settings.click();
|
||||
|
||||
await dashboardSettingsPage.init(page);
|
||||
await dashboardSettingsPage.pageObjects.deleteDashBoard.click();
|
||||
|
||||
await confirmModal.init(page);
|
||||
await confirmModal.pageObjects.delete.click();
|
||||
await confirmModal.pageObjects.success.exists();
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
import { ArrayPageObjectType, ClickablePageObjectType, PageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface DashboardPage {
|
||||
settings: ClickablePageObjectType;
|
||||
submenuItemLabel: ArrayPageObjectType;
|
||||
submenuItemValueDropDownValueLink: ArrayPageObjectType;
|
||||
submenuItemValueDropDownDropDown: PageObjectType;
|
||||
submenuItemValueDropDownSelectedLink: PageObjectType;
|
||||
submenuItemValueDropDownOptionText: ArrayPageObjectType;
|
||||
}
|
||||
|
||||
export const dashboardPage = new TestPage<DashboardPage>({
|
||||
pageObjects: {
|
||||
settings: 'Dashboard settings navbar button',
|
||||
submenuItemLabel: 'Dashboard template variables submenu LabelName label',
|
||||
submenuItemValueDropDownValueLink: 'Dashboard template variables Variable Value DropDown value link',
|
||||
submenuItemValueDropDownDropDown: 'Dashboard template variables Variable Value DropDown DropDown',
|
||||
submenuItemValueDropDownSelectedLink: 'Dashboard template variables Variable Value DropDown Selected link',
|
||||
submenuItemValueDropDownOptionText: 'Dashboard template variables Variable Value DropDown option text',
|
||||
},
|
||||
});
|
||||
|
||||
export interface AssertVariableLabelsAndComponentsArguments {
|
||||
label: string;
|
||||
options: string[];
|
||||
}
|
||||
|
||||
export const assertVariableLabelsAndComponents = async (
|
||||
page: TestPage<DashboardPage>,
|
||||
args: AssertVariableLabelsAndComponentsArguments[]
|
||||
) => {
|
||||
console.log('Asserting variable components and labels');
|
||||
await page.pageObjects.submenuItemLabel.waitForSelector();
|
||||
await page.pageObjects.submenuItemLabel.hasLength(args.length);
|
||||
await page.pageObjects.submenuItemValueDropDownValueLink.hasLength(args.length);
|
||||
|
||||
for (let index = 0; index < args.length; index++) {
|
||||
const { label, options } = args[index];
|
||||
await page.pageObjects.submenuItemLabel.containsTextAtPos(label, index);
|
||||
await page.pageObjects.submenuItemValueDropDownValueLink.containsTextAtPos(options[1], index);
|
||||
await page.pageObjects.submenuItemValueDropDownValueLink.clickAtPos(index);
|
||||
await page.pageObjects.submenuItemValueDropDownOptionText.hasLength(options.length);
|
||||
for (let optionIndex = 0; optionIndex < options.length; optionIndex++) {
|
||||
await page.pageObjects.submenuItemValueDropDownOptionText.containsTextAtPos(options[optionIndex], optionIndex);
|
||||
}
|
||||
}
|
||||
console.log('Asserting variable components and labels, Ok');
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface DashboardSettingsPage {
|
||||
deleteDashBoard: ClickablePageObjectType;
|
||||
variablesSection: ClickablePageObjectType;
|
||||
saveDashBoard: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dashboardSettingsPage = new TestPage<DashboardSettingsPage>({
|
||||
pageObjects: {
|
||||
deleteDashBoard: 'Dashboard settings page delete dashboard button',
|
||||
variablesSection: 'Dashboard settings section Variables',
|
||||
saveDashBoard: 'Dashboard settings aside actions Save button',
|
||||
},
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface DashboardsPage {
|
||||
dashboard: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dashboardsPageFactory = (dashboardTitle: string) =>
|
||||
new TestPage<DashboardsPage>({
|
||||
url: '/dashboards',
|
||||
pageObjects: {
|
||||
dashboard: dashboardTitle,
|
||||
},
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ClickablePageObjectType, PageObject, Selector } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface SaveChangesDashboardModal {
|
||||
save: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const saveChangesDashboardModal = new TestPage<SaveChangesDashboardModal>({
|
||||
pageObjects: {
|
||||
save: 'Dashboard settings Save Dashboard Modal Save button',
|
||||
success: () => Selector.fromSelector('.alert-success'),
|
||||
},
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import { ClickablePageObjectType, InputPageObjectType, PageObject, Selector } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface SaveDashboardModal {
|
||||
name: InputPageObjectType;
|
||||
save: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const saveDashboardModal = new TestPage<SaveDashboardModal>({
|
||||
pageObjects: {
|
||||
name: 'Save dashboard title field',
|
||||
save: 'Save dashboard button',
|
||||
success: () => Selector.fromSelector('.alert-success'),
|
||||
},
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface AddDataSourcePage {
|
||||
testDataDB: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const addDataSourcePage = new TestPage<AddDataSourcePage>({
|
||||
url: '/datasources/new',
|
||||
pageObjects: {
|
||||
testDataDB: 'TestData DB datasource plugin',
|
||||
},
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
import { Page } from 'puppeteer-core';
|
||||
|
||||
import { ClickablePageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
import { editDataSourcePage } from './editDataSourcePage';
|
||||
import { addDataSourcePage } from './addDataSourcePage';
|
||||
import { confirmModal } from '../modals/confirmModal';
|
||||
|
||||
export interface DataSourcesPage {
|
||||
testData: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const dataSourcesPageFactory = (testDataSourceName: string) =>
|
||||
new TestPage<DataSourcesPage>({
|
||||
url: '/datasources',
|
||||
pageObjects: {
|
||||
testData: `Data source list item for ${testDataSourceName}`,
|
||||
},
|
||||
});
|
||||
|
||||
export const addTestDataSourceAndVerify = async (page: Page) => {
|
||||
// Add TestData DB
|
||||
const testDataSourceName = `e2e - TestData-${new Date().getTime()}`;
|
||||
await addDataSourcePage.init(page);
|
||||
await addDataSourcePage.navigateTo();
|
||||
await addDataSourcePage.pageObjects.testDataDB.exists();
|
||||
await addDataSourcePage.pageObjects.testDataDB.click();
|
||||
|
||||
await editDataSourcePage.init(page);
|
||||
await editDataSourcePage.waitForNavigation();
|
||||
await editDataSourcePage.pageObjects.name.enter(testDataSourceName);
|
||||
await editDataSourcePage.pageObjects.saveAndTest.click();
|
||||
await editDataSourcePage.pageObjects.alert.exists();
|
||||
await editDataSourcePage.pageObjects.alertMessage.containsText('Data source is working');
|
||||
|
||||
// Verify that data source is listed
|
||||
const url = await editDataSourcePage.getUrlWithoutBaseUrl();
|
||||
const expectedUrl = url.substring(1, url.length - 1);
|
||||
const selector = `a[href="${expectedUrl}"]`;
|
||||
|
||||
const dataSourcesPage = dataSourcesPageFactory(testDataSourceName);
|
||||
await dataSourcesPage.init(page);
|
||||
await dataSourcesPage.navigateTo();
|
||||
await dataSourcesPage.expectSelector({ selector });
|
||||
|
||||
return testDataSourceName;
|
||||
};
|
||||
|
||||
export const cleanUpTestDataSource = async (page: Page, testDataSourceName: string) => {
|
||||
const dataSourcesPage = dataSourcesPageFactory(testDataSourceName);
|
||||
await dataSourcesPage.init(page);
|
||||
await dataSourcesPage.navigateTo();
|
||||
await dataSourcesPage.pageObjects.testData.click();
|
||||
|
||||
await editDataSourcePage.init(page);
|
||||
await editDataSourcePage.pageObjects.delete.exists();
|
||||
await editDataSourcePage.pageObjects.delete.click();
|
||||
|
||||
await confirmModal.init(page);
|
||||
await confirmModal.pageObjects.delete.click();
|
||||
await confirmModal.pageObjects.success.exists();
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
import { ClickablePageObjectType, InputPageObjectType, PageObjectType } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface EditDataSourcePage {
|
||||
name: InputPageObjectType;
|
||||
delete: ClickablePageObjectType;
|
||||
saveAndTest: ClickablePageObjectType;
|
||||
alert: PageObjectType;
|
||||
alertMessage: PageObjectType;
|
||||
}
|
||||
|
||||
export const editDataSourcePage = new TestPage<EditDataSourcePage>({
|
||||
pageObjects: {
|
||||
name: 'Datasource settings page name input field',
|
||||
delete: 'Delete button',
|
||||
saveAndTest: 'Save and Test button',
|
||||
alert: 'Datasource settings page Alert',
|
||||
alertMessage: 'Datasource settings page Alert message',
|
||||
},
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
export * from './loginPage';
|
||||
export * from './pluginsPage';
|
||||
export * from './dashboards/createDashboardPage';
|
||||
export * from './dashboards/dashboardPage';
|
||||
export * from './dashboards/dashboardSettingsPage';
|
||||
export * from './dashboards/dashboardsPage';
|
||||
export * from './dashboards/saveChangesDashboardModal';
|
||||
export * from './dashboards/saveDashboardModal';
|
||||
export * from './datasources/addDataSourcePage';
|
||||
export * from './datasources/dataSources';
|
||||
export * from './datasources/editDataSourcePage';
|
||||
export * from './modals/confirmModal';
|
||||
@@ -1,17 +0,0 @@
|
||||
import { TestPage } from '../pageInfo';
|
||||
import { ClickablePageObjectType, InputPageObjectType } from '../pageObjects';
|
||||
|
||||
export interface LoginPage {
|
||||
username: InputPageObjectType;
|
||||
password: InputPageObjectType;
|
||||
submit: ClickablePageObjectType;
|
||||
}
|
||||
|
||||
export const loginPage = new TestPage<LoginPage>({
|
||||
url: '/login',
|
||||
pageObjects: {
|
||||
username: 'Username input field',
|
||||
password: 'Password input field',
|
||||
submit: 'Login button',
|
||||
},
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ClickablePageObjectType, PageObject, Selector } from '../../pageObjects';
|
||||
import { TestPage } from '../../pageInfo';
|
||||
|
||||
export interface ConfirmModal {
|
||||
delete: ClickablePageObjectType;
|
||||
success: PageObject;
|
||||
}
|
||||
|
||||
export const confirmModal = new TestPage<ConfirmModal>({
|
||||
pageObjects: {
|
||||
delete: 'Confirm Modal Danger Button',
|
||||
success: () => Selector.fromSelector('.alert-success'),
|
||||
},
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import { TestPage } from '../pageInfo';
|
||||
|
||||
export interface PluginsPage {}
|
||||
|
||||
export const pluginsPage = new TestPage<PluginsPage>({
|
||||
url: '/plugins',
|
||||
pageObjects: {},
|
||||
});
|
||||
|
||||
export function getPluginPage(id: string) {
|
||||
return new TestPage<PluginsPage>({
|
||||
url: `/plugins/${id}/`,
|
||||
pageObjects: {
|
||||
// TODO Find update/enable buttons
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { Browser, Page } from 'puppeteer-core';
|
||||
import { launchBrowser } from './launcher';
|
||||
import { ensureLoggedIn } from './login';
|
||||
import { cleanDashboard, createEmptyDashboardPage } from './pages/dashboards/createDashboardPage';
|
||||
import { DashboardPage } from './pages/dashboards/dashboardPage';
|
||||
import { TestPage } from './pageInfo';
|
||||
import { addTestDataSourceAndVerify, cleanUpTestDataSource } from './pages/datasources/dataSources';
|
||||
|
||||
export interface ScenarioArguments {
|
||||
describeName: string;
|
||||
itName: string;
|
||||
scenario: (browser: Browser, page: Page, datasourceName?: string, dashboardPage?: TestPage<DashboardPage>) => void;
|
||||
skipScenario?: boolean;
|
||||
createTestDataSource?: boolean;
|
||||
createTestDashboard?: boolean;
|
||||
}
|
||||
|
||||
export const e2eScenario = ({
|
||||
describeName,
|
||||
itName,
|
||||
scenario,
|
||||
skipScenario = false,
|
||||
createTestDataSource = false,
|
||||
createTestDashboard = false,
|
||||
}: ScenarioArguments) => {
|
||||
describe(describeName, () => {
|
||||
if (skipScenario) {
|
||||
it.skip(itName, async () => {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let browser: Browser;
|
||||
let page: Page;
|
||||
let testDataSourceName: string;
|
||||
let testDashboardTitle: string;
|
||||
let dashboardPage: TestPage<DashboardPage>;
|
||||
|
||||
beforeAll(async () => {
|
||||
browser = await launchBrowser();
|
||||
page = await browser.newPage();
|
||||
await ensureLoggedIn(page);
|
||||
if (createTestDataSource) {
|
||||
testDataSourceName = await addTestDataSourceAndVerify(page);
|
||||
}
|
||||
if (createTestDashboard) {
|
||||
testDashboardTitle = `e2e - ${new Date().getTime()}`;
|
||||
dashboardPage = await createEmptyDashboardPage(page, testDashboardTitle);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (testDataSourceName) {
|
||||
await cleanUpTestDataSource(page, testDataSourceName);
|
||||
}
|
||||
if (testDashboardTitle && dashboardPage) {
|
||||
await cleanDashboard(page, testDashboardTitle);
|
||||
}
|
||||
if (browser) {
|
||||
await browser.close();
|
||||
}
|
||||
});
|
||||
|
||||
it(itName, async () => {
|
||||
await scenario(browser, page, testDataSourceName, dashboardPage);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Browser, Page } from 'puppeteer-core';
|
||||
|
||||
import { e2eScenario, pages, takeScreenShot } from '@grafana/toolkit/src/e2e';
|
||||
import { getEndToEndSettings } from '@grafana/toolkit/src/plugins';
|
||||
|
||||
// ****************************************************************
|
||||
// NOTE, This file is copied to plugins at runtime, it is not run locally
|
||||
// ****************************************************************
|
||||
|
||||
const sleep = (milliseconds: number) => {
|
||||
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||
};
|
||||
|
||||
e2eScenario({
|
||||
describeName: 'Common Plugin Test',
|
||||
itName: 'should pass',
|
||||
scenario: async (browser: Browser, page: Page) => {
|
||||
const settings = getEndToEndSettings();
|
||||
const pluginPage = pages.getPluginPage(settings.plugin.id);
|
||||
await pluginPage.init(page);
|
||||
await pluginPage.navigateTo();
|
||||
// TODO: find a better way to avoid the 'loading' page
|
||||
await sleep(500);
|
||||
|
||||
const fileName = 'plugin-page';
|
||||
await takeScreenShot(page, fileName);
|
||||
},
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import * as jestCLI from 'jest-cli';
|
||||
import { TestResultsInfo } from '../types';
|
||||
import fs from 'fs';
|
||||
|
||||
export async function runEndToEndTests(outputDirectory: string, results: TestResultsInfo): Promise<void> {
|
||||
const setupPath = 'node_modules/@grafana/toolkit/src/e2e/install';
|
||||
let ext = '.js';
|
||||
if (!fs.existsSync(setupPath + ext)) {
|
||||
ext = '.ts'; // When running yarn link
|
||||
}
|
||||
|
||||
const jestConfig = {
|
||||
preset: 'ts-jest',
|
||||
verbose: false,
|
||||
moduleDirectories: ['node_modules'], // add the plugin somehow?
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
|
||||
setupFilesAfterEnv: [
|
||||
'expect-puppeteer', // Setup Puppeteer
|
||||
'<rootDir>/' + setupPath + ext, // Loads Chromimum
|
||||
],
|
||||
globals: { 'ts-jest': { isolatedModules: true } },
|
||||
testMatch: [
|
||||
'<rootDir>/e2e-temp/**/*.test.ts', // Copied from node_modules
|
||||
'<rootDir>/e2e/test/**/*.test.ts',
|
||||
],
|
||||
reporters: [
|
||||
'default',
|
||||
['jest-junit', { outputDirectory }], // save junit.xml to folder
|
||||
],
|
||||
};
|
||||
|
||||
const cliConfig = {
|
||||
config: JSON.stringify(jestConfig),
|
||||
passWithNoTests: true,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const runJest = () => jestCLI.runCLI(cliConfig, [process.cwd()]);
|
||||
|
||||
const jestOutput = await runJest();
|
||||
results.passed = jestOutput.results.numPassedTests;
|
||||
results.failed = jestOutput.results.numFailedTestSuites;
|
||||
return;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import { PluginMeta } from '@grafana/data';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
import { constants } from '../../e2e/constants';
|
||||
|
||||
export interface Settings {
|
||||
plugin: PluginMeta;
|
||||
outputFolder: string;
|
||||
}
|
||||
|
||||
let env: Settings | null = null;
|
||||
|
||||
export function getEndToEndSettings() {
|
||||
if (env) {
|
||||
return env;
|
||||
}
|
||||
|
||||
let f = path.resolve(process.cwd(), 'ci', 'dist', 'plugin.json');
|
||||
if (!fs.existsSync(f)) {
|
||||
f = path.resolve(process.cwd(), 'dist', 'plugin.json');
|
||||
if (!fs.existsSync(f)) {
|
||||
f = path.resolve(process.cwd(), 'src', 'plugin.json');
|
||||
}
|
||||
}
|
||||
const outputFolder = path.resolve(process.cwd(), 'e2e-results');
|
||||
if (!fs.existsSync(outputFolder)) {
|
||||
fs.mkdirSync(outputFolder, { recursive: true });
|
||||
}
|
||||
constants.screenShotsTruthDir = path.resolve(process.cwd(), 'e2e', 'truth');
|
||||
constants.screenShotsOutputDir = outputFolder;
|
||||
|
||||
return (env = {
|
||||
plugin: require(f) as PluginMeta,
|
||||
outputFolder,
|
||||
});
|
||||
}
|
||||
@@ -2,4 +2,3 @@ export * from './env';
|
||||
export * from './utils';
|
||||
export * from './workflow';
|
||||
export * from './types';
|
||||
export * from './e2e/settings';
|
||||
|
||||
Reference in New Issue
Block a user