e2e: Uses Cypress instead of Puppeteer (#20753)

* WIP: intial commit

* Tests: Runs e2e tests

* Refactor: Adds BASE_URL support

* Refactor: Adds namespacing

* Refactor: Cleans up the Page api

* Build: Adds to build-branches-and-prs job for testing

* Build: Hardcoded image for now

* Refactor: Uses Selectors in App

* Refactor: Adds addDataSource flow

* WIP

* Refactor: Adds e2eScenario

* Refactor: Adds add and delete scenarios

* Refactor: Adds logging

* Refactor: Adds ability to for Selectors with variables

* Refactor: Using variable selectors instead

* Refactor: Adds flow until Share Panel

* Refactor: Adds clicking on rendered image link

* Refactor: Deletes log output

* Refactor: Updates snapshots

* Chore: Reverts changes

* Refactor: Removes log plugin because maybe it breaks yarn build

* Refactor: Adds rendered image download

* Refactor: Adds image comparison

* Refactor: Removes uncaught errors override

* Refactor: Changes order of images to compare

* Refactor: Updates truth image

* Build: Updates path to CI artifacts

* Refactor: Cleaning up types and config

* wip

* Refactor: Cleans up external api

* Refactor: More cleanup

* Refactor: More cleanup

* Refactor: Removes usages of Pages and Flows

* Refactor: Removes last traces of Cypress in spec

* Refactor: Adds comments
This commit is contained in:
Hugo Häggmark
2019-12-09 00:14:25 -08:00
committed by GitHub
parent 10d36b282b
commit 58cffde0f2
91 changed files with 1955 additions and 95 deletions

View File

@@ -1,4 +1,6 @@
import React, { PureComponent, SyntheticEvent, ChangeEvent } from 'react';
import React, { ChangeEvent, PureComponent, SyntheticEvent } from 'react';
import { e2e } from '@grafana/e2e';
import { FormModel } from './LoginCtrl';
interface Props {
@@ -72,7 +74,7 @@ export class LoginForm extends PureComponent<Props, State> {
className="gf-form-input login-form-input"
required
placeholder={this.props.loginHint}
aria-label="Username input field"
aria-label={e2e.pages.Login.selectors.username}
onChange={this.onChangeUsername}
/>
</div>
@@ -85,7 +87,7 @@ export class LoginForm extends PureComponent<Props, State> {
ng-model="formModel.password"
id="inputPassword"
placeholder={this.props.passwordHint}
aria-label="Password input field"
aria-label={e2e.pages.Login.selectors.password}
onChange={this.onChangePassword}
/>
</div>
@@ -93,7 +95,7 @@ export class LoginForm extends PureComponent<Props, State> {
{!this.props.isLoggingIn ? (
<button
type="submit"
aria-label="Login button"
aria-label={e2e.pages.Login.selectors.submit}
className={`btn btn-large p-x-2 ${this.state.valid ? 'btn-primary' : 'btn-inverse'}`}
onClick={this.onSubmit}
disabled={!this.state.valid}

View File

@@ -20,7 +20,7 @@
<div class="search-section__header" ng-show="section.hideHeader"></div>
<div ng-if="section.expanded">
<a ng-repeat="item in section.items" class="search-item search-item--indent" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" aria-label="{{::item.title}}">
<a ng-repeat="item in section.items" class="search-item search-item--indent" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" aria-label={{ctrl.selectors.dashboards(item.title)}}>
<div ng-click="ctrl.toggleSelection(item, $event)" class="center-vh">
<gf-form-checkbox
ng-show="ctrl.editable"

View File

@@ -1,4 +1,6 @@
import _ from 'lodash';
import { e2e } from '@grafana/e2e';
import coreModule from '../../core_module';
import appEvents from 'app/core/app_events';
import { CoreEvents } from 'app/types';
@@ -9,9 +11,12 @@ export class SearchResultsCtrl {
onTagSelected: any;
onFolderExpanding: any;
editable: boolean;
selectors: typeof e2e.pages.Dashboards.selectors;
/** @ngInject */
constructor(private $location: any) {}
constructor(private $location: any) {
this.selectors = e2e.pages.Dashboards.selectors;
}
toggleFolderExpand(section: any) {
if (section.toggle) {

View File

@@ -1,3 +1,5 @@
import { e2e } from '@grafana/e2e';
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
import { CoreEvents } from 'app/types';
@@ -71,6 +73,7 @@ export class UtilSrv {
scope.yesText = payload.yesText || 'Yes';
scope.noText = payload.noText || 'Cancel';
scope.confirmTextValid = scope.confirmText ? false : true;
scope.selectors = e2e.pages.ConfirmModal.selectors;
appEvents.emit(CoreEvents.showModal, {
src: 'public/app/partials/confirm_modal.html',

View File

@@ -1,20 +1,17 @@
// Libraries
import React from 'react';
import _ from 'lodash';
import { LocationUpdate } from '@grafana/runtime';
import { e2e } from '@grafana/e2e';
// Utils
import config from 'app/core/config';
import store from 'app/core/store';
// Store
import { store as reduxStore } from 'app/store/store';
import { updateLocation } from 'app/core/actions';
// Types
import { PanelModel } from '../../state';
import { DashboardModel } from '../../state';
import { DashboardModel, PanelModel } from '../../state';
import { LS_PANEL_COPY_KEY } from 'app/core/constants';
import { LocationUpdate } from '@grafana/runtime';
export type PanelPluginInfo = { id: any; defaults: { gridPos: { w: any; h: any }; title: any } };
@@ -141,7 +138,7 @@ export class AddPanelWidget extends React.Component<Props, State> {
href="#"
onClick={onClick}
className="add-panel-widget__link btn btn-inverse"
aria-label={`${text} CTA button`}
aria-label={e2e.pages.AddDashboard.selectors.ctaButtons(text)}
>
<div className="add-panel-widget__icon">
<i className={`gicon gicon-${icon}`} />

View File

@@ -35,7 +35,7 @@ exports[`Render should render component 1`] = `
>
<div>
<a
aria-label="Add Query CTA button"
aria-label="Add Panel Widget CTA Button Add Query"
className="add-panel-widget__link btn btn-inverse"
href="#"
onClick={[Function]}
@@ -54,7 +54,7 @@ exports[`Render should render component 1`] = `
</div>
<div>
<a
aria-label="Choose Visualization CTA button"
aria-label="Add Panel Widget CTA Button Choose Visualization"
className="add-panel-widget__link btn btn-inverse"
href="#"
onClick={[Function]}

View File

@@ -1,6 +1,7 @@
// Libaries
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { e2e } from '@grafana/e2e';
// Utils & Services
import { appEvents } from 'app/core/app_events';
import { PlaylistSrv } from 'app/features/playlist/playlist_srv';
@@ -159,7 +160,7 @@ export class DashNav extends PureComponent<Props> {
<button
className="navbar-edit__back-btn"
onClick={this.onClose}
aria-label="Dashboard settings Go Back button"
aria-label={e2e.pages.Dashboard.selectors.backArrow}
>
<i className="fa fa-arrow-left" />
</button>

View File

@@ -1,8 +1,8 @@
// Libraries
import React, { FunctionComponent } from 'react';
// Components
import { Tooltip } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
interface Props {
icon: string;
@@ -19,7 +19,7 @@ export const DashNavButton: FunctionComponent<Props> = ({ icon, tooltip, classSu
<button
className={`btn navbar-button navbar-button--${classSuffix}`}
onClick={onClick}
aria-label={`${tooltip} navbar button`}
aria-label={e2e.pages.Dashboard.selectors.toolbarItems(tooltip)}
>
<i className={icon} />
</button>

View File

@@ -1,4 +1,4 @@
import { coreModule, appEvents, contextSrv } from 'app/core/core';
import { appEvents, contextSrv, coreModule } from 'app/core/core';
import { DashboardModel } from '../../state/DashboardModel';
import $ from 'jquery';
import _ from 'lodash';
@@ -9,6 +9,7 @@ import { DashboardSrv } from '../../services/DashboardSrv';
import { CoreEvents } from 'app/types';
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
import { AppEvents } from '@grafana/data';
import { e2e } from '@grafana/e2e';
export class SettingsCtrl {
dashboard: DashboardModel;
@@ -21,6 +22,7 @@ export class SettingsCtrl {
canDelete: boolean;
sections: any[];
hasUnsavedFolderChange: boolean;
selectors: typeof e2e.pages.DashboardSettings.selectors;
/** @ngInject */
constructor(
@@ -53,6 +55,7 @@ export class SettingsCtrl {
this.$rootScope.onAppEvent(CoreEvents.routeUpdated, this.onRouteUpdated.bind(this), $scope);
this.$rootScope.appEvent(CoreEvents.dashScroll, { animate: false, pos: 0 });
this.$rootScope.onAppEvent(CoreEvents.dashboardSaved, this.onPostSave.bind(this), $scope);
this.selectors = e2e.pages.DashboardSettings.selectors;
}
buildSectionList() {

View File

@@ -1,5 +1,5 @@
<aside class="dashboard-settings__aside">
<a href="{{::section.url}}" class="dashboard-settings__nav-item" ng-class="{active: ctrl.viewId === section.id}" ng-repeat="section in ctrl.sections" aria-label="{{'Dashboard settings section ' + section.title}}">
<a href="{{::section.url}}" class="dashboard-settings__nav-item" ng-class="{active: ctrl.viewId === section.id}" ng-repeat="section in ctrl.sections" aria-label={{ctrl.selectors.sectionItems}}>
<i class="{{::section.icon}}"></i>
{{::section.title}}
</a>
@@ -9,7 +9,7 @@
class="btn btn-primary"
ng-click="ctrl.saveDashboard()"
ng-show="ctrl.canSave"
aria-label="Dashboard settings aside actions Save button"
aria-label={{ctrl.selectors.saveDashBoard}}
>
Save
</button>
@@ -17,7 +17,7 @@
class="btn btn-inverse"
ng-click="ctrl.openSaveAsModal()"
ng-show="ctrl.canSaveAs"
aria-label="Dashboard settings aside actions Save As button"
aria-label={{ctrl.selectors.saveAsDashBoard}}
>
Save As...
</button>

View File

@@ -1,3 +1,5 @@
import { e2e } from '@grafana/e2e';
import coreModule from 'app/core/core_module';
import { DashboardSrv } from '../../services/DashboardSrv';
import { CloneOptions } from '../../state/DashboardModel';
@@ -57,7 +59,7 @@ const template = `
class="btn btn-primary"
ng-class="{'btn-primary--processing': ctrl.isSaving}"
ng-disabled="ctrl.saveForm.$invalid || ctrl.isSaving"
aria-label="Dashboard settings Save Dashboard Modal Save button"
aria-label={{ctrl.selectors.save}}
>
<span ng-if="!ctrl.isSaving">Save</span>
<span ng-if="ctrl.isSaving === true">Saving...</span>
@@ -82,6 +84,7 @@ export class SaveDashboardModalCtrl {
dismiss: () => void;
timeChange = false;
variableValueChange = false;
selectors: typeof e2e.pages.SaveDashboardModal.selectors;
/** @ngInject */
constructor(private dashboardSrv: DashboardSrv) {
@@ -90,6 +93,7 @@ export class SaveDashboardModalCtrl {
this.isSaving = false;
this.timeChange = this.dashboardSrv.getCurrent().hasTimeChanged();
this.variableValueChange = this.dashboardSrv.getCurrent().hasVariableValuesChanged();
this.selectors = e2e.pages.SaveDashboardModal.selectors;
}
save() {

View File

@@ -1,6 +1,8 @@
import angular, { ILocationService } from 'angular';
import config from 'app/core/config';
import { dateTime } from '@grafana/data';
import { e2e } from '@grafana/e2e';
import config from 'app/core/config';
import { appendQueryToUrl, toUrlParams } from 'app/core/utils/url';
import { TimeSrv } from '../../services/TimeSrv';
import { TemplateSrv } from 'app/features/templating/template_srv';
@@ -23,6 +25,7 @@ export function ShareModalCtrl(
theme: 'current',
};
$scope.editor = { index: $scope.tabIndex || 0 };
$scope.selectors = e2e.pages.SharePanelModal.selectors;
$scope.init = () => {
$scope.panel = $scope.model && $scope.model.panel ? $scope.model.panel : $scope.panel; // React pass panel and dashboard in the "model" property

View File

@@ -92,7 +92,7 @@
</div>
</div>
<div class="gf-form" ng-show="modeSharePanel">
<a href="{{imageUrl}}" target="_blank" aria-label="Link to rendered image"><i class="fa fa-camera"></i> Direct link rendered image</a>
<a href="{{imageUrl}}" target="_blank" aria-label={{selectors.linkToRenderedImage}}><i class="fa fa-camera"></i> Direct link rendered image</a>
</div>
</div>
</script>

View File

@@ -1,7 +1,9 @@
import React, { Component } from 'react';
import classNames from 'classnames';
import { isEqual } from 'lodash';
import { ScopedVars } from '@grafana/data';
import { DataLink, ScopedVars } from '@grafana/data';
import { ClickOutsideWrapper } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import PanelHeaderCorner from './PanelHeaderCorner';
import { PanelHeaderMenu } from './PanelHeaderMenu';
@@ -9,8 +11,6 @@ import templateSrv from 'app/features/templating/template_srv';
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { ClickOutsideWrapper } from '@grafana/ui';
import { DataLink } from '@grafana/data';
import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
export interface Props {
@@ -96,7 +96,7 @@ export class PanelHeader extends Component<Props, State> {
className="panel-title-container"
onClick={this.onMenuToggle}
onMouseDown={this.onMouseDown}
aria-label="Panel Title"
aria-label={e2e.pages.Panels.Panel.selectors.title(title)}
>
<div className="panel-title">
<span className="icon-gf panel-alert-icon" />

View File

@@ -1,5 +1,6 @@
import React, { FC } from 'react';
import { PanelMenuItem } from '@grafana/data';
import { e2e } from '@grafana/e2e';
interface Props {
children: any;
@@ -14,7 +15,7 @@ export const PanelHeaderMenuItem: FC<Props & PanelMenuItem> = props => {
<li className={isSubMenu ? 'dropdown-submenu' : null}>
<a onClick={props.onClick}>
{props.iconClassName && <i className={props.iconClassName} />}
<span className="dropdown-item-text" aria-label={`${props.text} panel menu item`}>
<span className="dropdown-item-text" aria-label={e2e.pages.Panels.Panel.selectors.headerItems(props.text)}>
{props.text}
</span>
{props.shortcut && <span className="dropdown-menu-item-shortcut">{props.shortcut}</span>}

View File

@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
import { Tooltip } from '@grafana/ui';
import { PanelPlugin, PanelPluginMeta } from '@grafana/data';
import { AngularComponent, config } from '@grafana/runtime';
import { e2e } from '@grafana/e2e';
import { QueriesTab } from './QueriesTab';
import VisualizationTab from './VisualizationTab';
@@ -130,7 +131,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) {
return (
<div className="panel-editor-tabs__item" onClick={() => onClick(tab)}>
<a className={tabClasses} aria-label={`${tab.text} tab button`}>
<a className={tabClasses} aria-label={e2e.pages.Panels.EditPanel.selectors.tabItems(tab.text)}>
<Tooltip content={`${tab.text}`} placement="auto">
<i className={`gicon gicon-${tab.id}${activeTab === tab.id ? '-active' : ''}`} />
</Tooltip>

View File

@@ -1,5 +1,6 @@
import React, { PureComponent } from 'react';
import { DataSourceSettings } from '@grafana/data';
import { e2e } from '@grafana/e2e';
export interface Props {
dataSource: DataSourceSettings;
@@ -19,7 +20,7 @@ export class DataSourcesListItem extends PureComponent<Props> {
<img src={dataSource.typeLogoUrl} alt={dataSource.name} />
</figure>
<div className="card-item-details">
<div className="card-item-name" aria-label={`Data source list item for ${dataSource.name}`}>
<div className="card-item-name" aria-label={e2e.pages.DataSources.selectors.dataSources(dataSource.name)}>
{dataSource.name}
{dataSource.isDefault && <span className="btn btn-secondary btn-small card-item-label">default</span>}
</div>

View File

@@ -1,13 +1,15 @@
import React, { PureComponent, FC } from 'react';
import React, { FC, PureComponent } from 'react';
import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import { DataSourcePluginMeta, NavModel, PluginType } from '@grafana/data';
import { List } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
import Page from 'app/core/components/Page/Page';
import { StoreState } from 'app/types';
import { addDataSource, loadDataSourceTypes, setDataSourceTypeSearchQuery } from './state/actions';
import { getDataSourceTypes } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
import { List } from '@grafana/ui';
import { DataSourcePluginMeta, NavModel, PluginType } from '@grafana/data';
export interface Props {
navModel: NavModel;
@@ -184,7 +186,11 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
const learnMoreLink = plugin.info.links && plugin.info.links.length > 0 ? plugin.info.links[0].url : null;
return (
<div className="add-data-source-item" onClick={onClick} aria-label={`${plugin.name} datasource plugin`}>
<div
className="add-data-source-item"
onClick={onClick}
aria-label={e2e.pages.AddDataSource.selectors.dataSourcePlugins(plugin.name)}
>
<img className="add-data-source-item-logo" src={plugin.info.logos.small} />
<div className="add-data-source-item-text-wrapper">
<span className="add-data-source-item-text">{plugin.name}</span>

View File

@@ -32,7 +32,7 @@ exports[`Render should render component 1`] = `
className="card-item-details"
>
<div
aria-label="Data source list item for gdev-cloudwatch"
aria-label="Data source list item gdev-cloudwatch"
className="card-item-name"
>
gdev-cloudwatch

View File

@@ -1,5 +1,6 @@
import React, { FC } from 'react';
import { FormLabel, Input, Switch } from '@grafana/ui';
import { e2e } from '@grafana/e2e';
export interface Props {
dataSourceName: string;
@@ -28,7 +29,7 @@ const BasicSettings: FC<Props> = ({ dataSourceName, isDefault, onDefaultChange,
placeholder="Name"
onChange={event => onNameChange(event.target.value)}
required
aria-label="Datasource settings page name input field"
aria-label={e2e.pages.DataSource.selectors.name}
/>
</div>
<Switch

View File

@@ -1,4 +1,6 @@
import React, { FC } from 'react';
import { e2e } from '@grafana/e2e';
import config from 'app/core/config';
export interface Props {
@@ -17,7 +19,7 @@ const ButtonRow: FC<Props> = ({ isReadOnly, onDelete, onSubmit, onTest }) => {
className="btn btn-primary"
disabled={isReadOnly}
onClick={event => onSubmit(event)}
aria-label="Save and Test button"
aria-label={e2e.pages.DataSource.selectors.saveAndTest}
>
Save &amp; Test
</button>
@@ -32,7 +34,7 @@ const ButtonRow: FC<Props> = ({ isReadOnly, onDelete, onSubmit, onTest }) => {
className="btn btn-danger"
disabled={isReadOnly}
onClick={onDelete}
aria-label="Delete button"
aria-label={e2e.pages.DataSource.selectors.delete}
>
Delete
</button>

View File

@@ -3,6 +3,7 @@ import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import isString from 'lodash/isString';
import { e2e } from '@grafana/e2e';
// Components
import Page from 'app/core/components/Page/Page';
import { GenericDataSourcePlugin, PluginSettings } from './PluginSettings';
@@ -271,7 +272,7 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> {
<div className="gf-form-group">
{testingMessage && (
<div className={`alert-${testingStatus} alert`} aria-label="Datasource settings page Alert">
<div className={`alert-${testingStatus} alert`} aria-label={e2e.pages.DataSource.selectors.alert}>
<div className="alert-icon">
{testingStatus === 'error' ? (
<i className="fa fa-exclamation-triangle" />
@@ -280,7 +281,7 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> {
)}
</div>
<div className="alert-body">
<div className="alert-title" aria-label="Datasource settings page Alert message">
<div className="alert-title" aria-label={e2e.pages.DataSource.selectors.alertMessage}>
{testingMessage}
</div>
</div>

View File

@@ -22,7 +22,7 @@ exports[`Render should render component 1`] = `
Name
</Component>
<Input
aria-label="Datasource settings page name input field"
aria-label="Data source settings page name input field"
className="gf-form-input max-width-23"
onChange={[Function]}
placeholder="Name"

View File

@@ -12,7 +12,7 @@ exports[`Render should render component 1`] = `
Test
</button>
<button
aria-label="Delete button"
aria-label="Data source settings page Delete button"
className="btn btn-danger"
disabled={true}
onClick={[MockFunction]}
@@ -34,7 +34,7 @@ exports[`Render should render with buttons enabled 1`] = `
className="gf-form-button-row"
>
<button
aria-label="Save and Test button"
aria-label="Data source settings page Save and Test button"
className="btn btn-primary"
disabled={false}
onClick={[Function]}
@@ -43,7 +43,7 @@ exports[`Render should render with buttons enabled 1`] = `
Save & Test
</button>
<button
aria-label="Delete button"
aria-label="Data source settings page Delete button"
className="btn btn-danger"
disabled={false}
onClick={[MockFunction]}

View File

@@ -6,6 +6,7 @@ import Drop from 'tether-drop';
import baron from 'baron';
import { PanelEvents } from '@grafana/data';
import { getLocationSrv } from '@grafana/runtime';
import { e2e } from '@grafana/e2e';
const module = angular.module('grafana.directives');
@@ -21,7 +22,7 @@ const panelTemplate = `
<i class="fa fa-spinner fa-spin"></i>
</span>
<panel-header class="panel-title-container" panel-ctrl="ctrl" aria-label="Panel Title"></panel-header>
<panel-header class="panel-title-container" panel-ctrl="ctrl" aria-label={{ctrl.selectors.title(ctrl.panel.title)}}></panel-header>
</div>
<div class="panel-content">
@@ -42,6 +43,7 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => {
const panelContent = elem.find('.panel-content');
const cornerInfoElem = elem.find('.panel-info-corner');
const ctrl = scope.ctrl;
ctrl.selectors = e2e.pages.Panels.Panel.selectors;
let infoDrop: any;
let panelScrollbar: any;

View File

@@ -1,5 +1,6 @@
import { coreModule } from 'app/core/core';
import { AngularPanelMenuItem } from '@grafana/data';
import { e2e } from '@grafana/e2e';
const template = `
<span class="panel-title">
@@ -35,7 +36,9 @@ function renderMenuItem(item: AngularPanelMenuItem, ctrl: any) {
}
html += `><i class="${item.icon}"></i>`;
html += `<span class="dropdown-item-text" aria-label="${item.text} panel menu item">${item.text}</span>`;
html += `<span class="dropdown-item-text" aria-label="${e2e.pages.Panels.Panel.selectors.headerItems(item.text)}">${
item.text
}</span>`;
if (item.shortcut) {
html += `<span class="dropdown-menu-item-shortcut">${item.shortcut}</span>`;

View File

@@ -26,7 +26,7 @@
<div class="confirm-modal-buttons">
<button ng-show="onAltAction" type="button" class="btn btn-primary" ng-click="dismiss();onAltAction();">{{altActionText}}</button>
<button ng-show="onConfirm" type="button" class="btn btn-danger" ng-click="onConfirm();dismiss();" ng-disabled="!confirmTextValid" give-focus="true" aria-label="Confirm Modal Danger Button">{{yesText}}</button>
<button ng-show="onConfirm" type="button" class="btn btn-danger" ng-click="onConfirm();dismiss();" ng-disabled="!confirmTextValid" give-focus="true" aria-label={{selectors.delete}}>{{yesText}}</button>
<button type="button" class="btn btn-inverse" ng-click="dismiss()">{{noText}}</button>
</div>
</div>

View File

@@ -3,7 +3,7 @@
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Scenario</label>
<div class="gf-form-select-wrapper width-15">
<select class="gf-form-input" ng-model="ctrl.target.scenarioId" ng-options="v.id as v.name for v in ctrl.scenarioList" ng-change="ctrl.scenarioChanged()" aria-label="Scenario Select"></select>
<select class="gf-form-input" ng-model="ctrl.target.scenarioId" ng-options="v.id as v.name for v in ctrl.scenarioList" ng-change="ctrl.scenarioChanged()" aria-label={{ctrl.selectors.scenarioSelect}}></select>
</div>
</div>
<div class="gf-form gf-form gf-form--grow" ng-if="ctrl.scenario.stringInput">

View File

@@ -1,9 +1,10 @@
import _ from 'lodash';
import { dateMath, dateTime } from '@grafana/data';
import { e2e } from '@grafana/e2e';
import { QueryCtrl } from 'app/plugins/sdk';
import { defaultQuery } from './runStreams';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { dateTime, dateMath } from '@grafana/data';
export const defaultPulse: any = {
timeStep: 60,
@@ -30,6 +31,7 @@ export class TestDataQueryCtrl extends QueryCtrl {
selectedPoint: any;
showLabels = false;
selectors: typeof e2e.pages.Panels.DataSource.TestData.QueryTab.selectors;
/** @ngInject */
constructor($scope: any, $injector: any) {
@@ -40,6 +42,7 @@ export class TestDataQueryCtrl extends QueryCtrl {
this.newPointTime = dateTime();
this.selectedPoint = { text: 'Select point', value: null };
this.showLabels = showLabelsFor.includes(this.target.scenarioId);
this.selectors = e2e.pages.Panels.DataSource.TestData.QueryTab.selectors;
}
getPoints() {

View File

@@ -44,7 +44,7 @@
</div>
</div>
<div class="section gf-form-group" aria-label="X-Axis section">
<div class="section gf-form-group" aria-label={{ctrl.selectors.xAxisSection}}>
<h5 class="section-heading">X-Axis</h5>
<gf-form-switch class="gf-form" label="Show" label-class="width-6" checked="ctrl.panel.xaxis.show" on-change="ctrl.render()"></gf-form-switch>

View File

@@ -1,3 +1,4 @@
import { e2e } from '@grafana/e2e';
import { GraphCtrl } from './module';
export class AxesEditorCtrl {
@@ -7,6 +8,7 @@ export class AxesEditorCtrl {
xAxisModes: any;
xAxisStatOptions: any;
xNameSegment: any;
selectors: typeof e2e.pages.Panels.Visualization.Graph.VisualizationTab.selectors;
/** @ngInject */
constructor(private $scope: any) {
@@ -43,6 +45,7 @@ export class AxesEditorCtrl {
this.panel.xaxis.name = 'specify field';
}
}
this.selectors = e2e.pages.Panels.Visualization.Graph.VisualizationTab.selectors;
}
setUnitFormat(axis: { format: any }) {

View File

@@ -0,0 +1,59 @@
import { e2e } from '@grafana/e2e';
import { ScenarioContext } from '@grafana/e2e/src/support';
e2e.scenario({
describeName: 'Smoke tests',
itName: 'Login scenario, create test data source, dashboard, panel, and export scenario',
addScenarioDataSource: true,
addScenarioDashBoard: true,
skipScenario: false,
scenario: ({ dataSourceName, dashboardTitle, dashboardUid }: ScenarioContext) => {
e2e.flows.openDashboard(dashboardTitle);
e2e.pages.Dashboard.toolbarItems('Add panel').click();
e2e.pages.AddDashboard.ctaButtons('Add Query').click();
e2e.pages.Panels.EditPanel.tabItems('Queries').click();
e2e.pages.Panels.DataSource.TestData.QueryTab.scenarioSelect().select('CSV Metric Values');
e2e.pages.Panels.EditPanel.tabItems('Visualization').click();
e2e.pages.Panels.Visualization.Graph.VisualizationTab.xAxisSection()
.contains('Show')
.click();
e2e.flows.saveDashboard();
e2e.pages.Dashboard.backArrow().click();
e2e.pages.Panels.Panel.title('Panel Title').click();
e2e.pages.Panels.Panel.headerItems('Share').click();
e2e.pages.SharePanelModal.linkToRenderedImage().then(($a: any) => {
// extract the fully qualified href property
const url = $a.prop('href');
// Test that the image renderer returns 200 OK
e2e().request({ method: 'GET', url, timeout: 120000 });
// Download image
if (!e2e.env('CIRCLE_SHA1')) {
return;
}
const theOutputImage = `${e2e.config().screenshotsFolder}/theOutput/smoke-test-scenario.png`;
const theTruthImage = `${e2e.config().screenshotsFolder}/theTruth/smoke-test-scenario.png`;
e2e().wrap(
e2e.imgSrcToBlob(url).then((blob: any) => {
e2e.blobToBase64String(blob).then((base64String: string) => {
const data = base64String.replace(/^data:image\/\w+;base64,/, '');
e2e().writeFile(theOutputImage, data, 'base64');
});
})
);
e2e().wait(1000); // give the io a chance to flush image to disk
e2e().compareSnapshot({ pathToFileA: theOutputImage, pathToFileB: theTruthImage });
});
},
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["**/*.ts"]
}