mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[7.12] [ML] Data Frame Analytics: Stabilize canvas element functional tests. (#96311)
- Fix color assertion with risk of percentage being rounded to 0. - Better naming of attributes of expected values. - Adds assertions to use the sample size dropdown and randomize query switch.
This commit is contained in:
parent
f9feddb721
commit
bf055e5256
15 changed files with 532 additions and 198 deletions
|
@ -1,6 +1,6 @@
|
|||
.tab-datavisualizer_index_select,
|
||||
.tab-timeseriesexplorer,
|
||||
.tab-explorer, {
|
||||
.tab-explorer {
|
||||
// Make all page background white until More of the pages use EuiPage to wrap in panel-like components
|
||||
background-color: $euiColorEmptyShade;
|
||||
}
|
||||
|
@ -22,3 +22,12 @@
|
|||
.clear, .clearfix {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// Helper class for functional tests to disable anti-aliasing for canvas elements
|
||||
.mlDisableAntiAliasing {
|
||||
-webkit-font-smoothing : none;
|
||||
|
||||
* canvas {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ export const ScatterplotMatrix: FC<ScatterplotMatrixProps> = ({
|
|||
{splom === undefined || vegaSpec === undefined ? (
|
||||
<VegaChartLoading />
|
||||
) : (
|
||||
<div data-test-subj="mlScatterplotMatrix">
|
||||
<div data-test-subj={`mlScatterplotMatrix ${isLoading ? 'loading' : 'loaded'}`}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
|
@ -316,6 +316,7 @@ export const ScatterplotMatrix: FC<ScatterplotMatrixProps> = ({
|
|||
fullWidth
|
||||
>
|
||||
<EuiSelect
|
||||
data-test-subj="mlScatterplotMatrixSampleSizeSelect"
|
||||
compressed
|
||||
options={sampleSizeOptions}
|
||||
value={fetchSize}
|
||||
|
@ -340,6 +341,7 @@ export const ScatterplotMatrix: FC<ScatterplotMatrixProps> = ({
|
|||
fullWidth
|
||||
>
|
||||
<EuiSwitch
|
||||
data-test-subj="mlScatterplotMatrixRandomizeQuerySwitch"
|
||||
name="mlScatterplotMatrixRandomizeQuery"
|
||||
label={randomizeQuery ? TOGGLE_ON : TOGGLE_OFF}
|
||||
checked={randomizeQuery}
|
||||
|
|
|
@ -120,14 +120,6 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
language: SEARCH_QUERY_LANGUAGE.KUERY,
|
||||
});
|
||||
|
||||
const scatterplotFieldOptions = useMemo(
|
||||
() =>
|
||||
includesTableItems
|
||||
.filter((d) => d.feature_type === 'numerical' && d.is_included)
|
||||
.map((d) => d.name),
|
||||
[includesTableItems]
|
||||
);
|
||||
|
||||
const toastNotifications = getToastNotifications();
|
||||
|
||||
const setJobConfigQuery: ExplorationQueryBarProps['setSearchQuery'] = (update) => {
|
||||
|
@ -341,16 +333,37 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
[currentIndexPattern.fields]
|
||||
);
|
||||
|
||||
const scatterplotMatrixProps = useMemo(
|
||||
() => ({
|
||||
color: isJobTypeWithDepVar ? dependentVariable : undefined,
|
||||
fields: includesTableItems
|
||||
.filter((d) => d.feature_type === 'numerical' && d.is_included)
|
||||
.map((d) => d.name),
|
||||
index: currentIndexPattern.title,
|
||||
legendType: getScatterplotMatrixLegendType(jobType),
|
||||
searchQuery: jobConfigQuery,
|
||||
}),
|
||||
[
|
||||
currentIndexPattern.title,
|
||||
dependentVariable,
|
||||
includesTableItems,
|
||||
isJobTypeWithDepVar,
|
||||
jobConfigQuery,
|
||||
jobType,
|
||||
]
|
||||
);
|
||||
|
||||
// Show the Scatterplot Matrix only if
|
||||
// - There's more than one suitable field available
|
||||
// - The job type is outlier detection, or
|
||||
// - The job type is regression or classification and the dependent variable has been set
|
||||
const showScatterplotMatrix =
|
||||
(jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION ||
|
||||
((jobType === ANALYSIS_CONFIG_TYPE.REGRESSION ||
|
||||
jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION) &&
|
||||
!dependentVariableEmpty)) &&
|
||||
scatterplotFieldOptions.length > 1;
|
||||
const showScatterplotMatrix = useMemo(
|
||||
() =>
|
||||
(jobType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION ||
|
||||
(isJobTypeWithDepVar && !dependentVariableEmpty)) &&
|
||||
scatterplotMatrixProps.fields.length > 1,
|
||||
[dependentVariableEmpty, jobType, scatterplotMatrixProps.fields.length]
|
||||
);
|
||||
|
||||
// Don't render until `savedSearchQuery` has been initialized.
|
||||
// `undefined` means uninitialized, `null` means initialized but not used.
|
||||
|
@ -550,18 +563,7 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
paddingSize="m"
|
||||
data-test-subj="mlAnalyticsCreateJobWizardScatterplotMatrixPanel"
|
||||
>
|
||||
<ScatterplotMatrix
|
||||
fields={scatterplotFieldOptions}
|
||||
index={currentIndexPattern.title}
|
||||
color={
|
||||
jobType === ANALYSIS_CONFIG_TYPE.REGRESSION ||
|
||||
jobType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION
|
||||
? dependentVariable
|
||||
: undefined
|
||||
}
|
||||
legendType={getScatterplotMatrixLegendType(jobType)}
|
||||
searchQuery={jobConfigQuery}
|
||||
/>
|
||||
<ScatterplotMatrix {...scatterplotMatrixProps} />
|
||||
</EuiPanel>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
|
|
|
@ -53,6 +53,7 @@ export const getRocCurveChartVegaLiteSpec = (
|
|||
|
||||
return {
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v4.8.1.json',
|
||||
background: 'transparent',
|
||||
// Left padding of 45px to align the left axis of the chart with the confusion matrix above.
|
||||
padding: { left: 45, top: 0, right: 0, bottom: 0 },
|
||||
config: {
|
||||
|
|
|
@ -12,8 +12,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const ml = getService('ml');
|
||||
const editedDescription = 'Edited description';
|
||||
|
||||
// Failing: See https://github.com/elastic/kibana/issues/91450
|
||||
describe.skip('classification creation', function () {
|
||||
describe('classification creation', function () {
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('ml/bm_classification');
|
||||
await ml.testResources.createIndexPatternIfNeeded('ft_bank_marketing', '@timestamp');
|
||||
|
@ -43,24 +42,19 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
createIndexPattern: true,
|
||||
expected: {
|
||||
rocCurveColorState: [
|
||||
// background
|
||||
{ key: '#FFFFFF', value: 93 },
|
||||
// tick/grid/axis
|
||||
{ key: '#98A2B3', value: 1 },
|
||||
{ key: '#DDDDDD', value: 3 },
|
||||
{ color: '#DDDDDD', percentage: 50 },
|
||||
// line
|
||||
{ key: '#6092C0', value: 1 },
|
||||
{ color: '#98A2B3', percentage: 30 },
|
||||
],
|
||||
scatterplotMatrixColorStats: [
|
||||
// background
|
||||
{ key: '#000000', value: 94 },
|
||||
// marker colors
|
||||
{ color: '#7FC6B3', percentage: 1 },
|
||||
{ color: '#88ADD0', percentage: 0.03 },
|
||||
// tick/grid/axis
|
||||
{ key: '#DDDDDD', value: 1 },
|
||||
{ key: '#D3DAE6', value: 1 },
|
||||
{ key: '#F5F7FA', value: 1 },
|
||||
// scatterplot circles
|
||||
{ key: '#6A717D', value: 1 },
|
||||
{ key: '#54B39A', value: 1 },
|
||||
{ color: '#DDDDDD', percentage: 8 },
|
||||
{ color: '#D3DAE6', percentage: 8 },
|
||||
{ color: '#F5F7FA', percentage: 15 },
|
||||
],
|
||||
row: {
|
||||
type: 'classification',
|
||||
|
@ -83,6 +77,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.navigation.navigateToDataFrameAnalytics();
|
||||
|
||||
await ml.testExecution.logTestStep('loads the source selection modal');
|
||||
|
||||
// Disable anti-aliasing to stabilize canvas image rendering assertions
|
||||
await ml.commonUI.disableAntiAliasing();
|
||||
|
||||
await ml.dataFrameAnalytics.startAnalyticsCreation();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
|
@ -111,9 +109,18 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.testExecution.logTestStep('displays the include fields selection');
|
||||
await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the sample size to 10000 for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.setScatterplotMatrixSampleSizeSelectValue('10000');
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the randomize query switch to true for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.setScatterplotMatrixRandomizeQueryCheckState(true);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the scatterplot matrix');
|
||||
await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement(
|
||||
'mlAnalyticsCreateJobWizardScatterplotMatrixFormRow',
|
||||
await ml.dataFrameAnalyticsCreation.assertScatterplotMatrix(
|
||||
testData.expected.scatterplotMatrixColorStats
|
||||
);
|
||||
|
||||
|
@ -231,18 +238,39 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.testExecution.logTestStep('displays the results view for created job');
|
||||
await ml.dataFrameAnalyticsTable.openResultsView(testData.jobId);
|
||||
await ml.dataFrameAnalyticsResults.assertClassificationEvaluatePanelElementsExists();
|
||||
await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement(
|
||||
'mlDFAnalyticsClassificationExplorationRocCurveChart',
|
||||
testData.expected.rocCurveColorState
|
||||
);
|
||||
await ml.dataFrameAnalyticsResults.assertClassificationTablePanelExists();
|
||||
await ml.dataFrameAnalyticsResults.assertResultsTableExists();
|
||||
await ml.dataFrameAnalyticsResults.assertResultsTableTrainingFiltersExist();
|
||||
await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty();
|
||||
await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement(
|
||||
'mlDFExpandableSection-splom',
|
||||
|
||||
await ml.testExecution.logTestStep('displays the ROC curve chart');
|
||||
await ml.commonUI.assertColorsInCanvasElement(
|
||||
'mlDFAnalyticsClassificationExplorationRocCurveChart',
|
||||
testData.expected.rocCurveColorState,
|
||||
['#000000'],
|
||||
undefined,
|
||||
undefined,
|
||||
// increased tolerance for ROC curve chart up from 10 to 20
|
||||
// since the returned colors vary quite a bit on each run.
|
||||
20
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the sample size to 10000 for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsResults.setScatterplotMatrixSampleSizeSelectValue('10000');
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the randomize query switch to true for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsResults.setScatterplotMatrixRandomizeQueryCheckState(true);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the scatterplot matrix');
|
||||
await ml.dataFrameAnalyticsResults.assertScatterplotMatrix(
|
||||
testData.expected.scatterplotMatrixColorStats
|
||||
);
|
||||
|
||||
await ml.commonUI.resetAntiAliasing();
|
||||
});
|
||||
|
||||
it('displays the analytics job in the map view', async () => {
|
||||
|
|
|
@ -50,26 +50,21 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
{ chartAvailable: true, id: 'Exterior2nd', legend: '3 categories' },
|
||||
{ chartAvailable: true, id: 'Fireplaces', legend: '0 - 3' },
|
||||
],
|
||||
scatterplotMatrixColorStatsWizard: [
|
||||
// background
|
||||
{ key: '#000000', value: 91 },
|
||||
// tick/grid/axis
|
||||
{ key: '#6A717D', value: 2 },
|
||||
{ key: '#F5F7FA', value: 2 },
|
||||
{ key: '#D3DAE6', value: 1 },
|
||||
// scatterplot circles
|
||||
{ key: '#54B399', value: 1 },
|
||||
{ key: '#54B39A', value: 1 },
|
||||
scatterplotMatrixColorsWizard: [
|
||||
// markers
|
||||
{ color: '#52B398', percentage: 15 },
|
||||
// grey boilerplate
|
||||
{ color: '#6A717D', percentage: 33 },
|
||||
],
|
||||
scatterplotMatrixColorStatsResults: [
|
||||
// background
|
||||
{ key: '#000000', value: 91 },
|
||||
// red markers
|
||||
{ color: '#D98071', percentage: 1 },
|
||||
// tick/grid/axis, grey markers
|
||||
// the red outlier color is not above the 1% threshold.
|
||||
{ key: '#6A717D', value: 2 },
|
||||
{ key: '#98A2B3', value: 1 },
|
||||
{ key: '#F5F7FA', value: 2 },
|
||||
{ key: '#D3DAE6', value: 1 },
|
||||
{ color: '#6A717D', percentage: 33 },
|
||||
{ color: '#D3DAE6', percentage: 8 },
|
||||
{ color: '#98A1B3', percentage: 12 },
|
||||
// anti-aliasing
|
||||
{ color: '#F5F7FA', percentage: 30 },
|
||||
],
|
||||
row: {
|
||||
type: 'outlier_detection',
|
||||
|
@ -93,6 +88,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.navigation.navigateToDataFrameAnalytics();
|
||||
|
||||
await ml.testExecution.logTestStep('loads the source selection modal');
|
||||
|
||||
// Disable anti-aliasing to stabilize canvas image rendering assertions
|
||||
await ml.commonUI.disableAntiAliasing();
|
||||
|
||||
await ml.dataFrameAnalytics.startAnalyticsCreation();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
|
@ -127,10 +126,19 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.testExecution.logTestStep('displays the include fields selection');
|
||||
await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the sample size to 10000 for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.setScatterplotMatrixSampleSizeSelectValue('10000');
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the randomize query switch to true for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.setScatterplotMatrixRandomizeQueryCheckState(true);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the scatterplot matrix');
|
||||
await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement(
|
||||
'mlAnalyticsCreateJobWizardScatterplotMatrixFormRow',
|
||||
testData.expected.scatterplotMatrixColorStatsWizard
|
||||
await ml.dataFrameAnalyticsCreation.assertScatterplotMatrix(
|
||||
testData.expected.scatterplotMatrixColorsWizard
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('continues to the additional options step');
|
||||
|
@ -250,10 +258,23 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.dataFrameAnalyticsResults.assertResultsTableExists();
|
||||
await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty();
|
||||
await ml.dataFrameAnalyticsResults.assertFeatureInfluenceCellNotEmpty();
|
||||
await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement(
|
||||
'mlDFExpandableSection-splom',
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the sample size to 10000 for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsResults.setScatterplotMatrixSampleSizeSelectValue('10000');
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the randomize query switch to true for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsResults.setScatterplotMatrixRandomizeQueryCheckState(true);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the scatterplot matrix');
|
||||
await ml.dataFrameAnalyticsResults.assertScatterplotMatrix(
|
||||
testData.expected.scatterplotMatrixColorStatsResults
|
||||
);
|
||||
|
||||
await ml.commonUI.resetAntiAliasing();
|
||||
});
|
||||
|
||||
it('displays the analytics job in the map view', async () => {
|
||||
|
|
|
@ -41,14 +41,13 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
createIndexPattern: true,
|
||||
expected: {
|
||||
scatterplotMatrixColorStats: [
|
||||
// background
|
||||
{ key: '#000000', value: 80 },
|
||||
// some marker colors of the continuous color scale
|
||||
{ color: '#61AFA3', percentage: 2 },
|
||||
{ color: '#D1E5E0', percentage: 2 },
|
||||
// tick/grid/axis
|
||||
{ key: '#6A717D', value: 1 },
|
||||
{ key: '#F5F7FA', value: 2 },
|
||||
{ key: '#D3DAE6', value: 1 },
|
||||
// because a continuous color scale is used for the scatterplot circles,
|
||||
// none of the generated colors is above the 1% threshold.
|
||||
{ color: '#6A717D', percentage: 10 },
|
||||
{ color: '#F5F7FA', percentage: 10 },
|
||||
{ color: '#D3DAE6', percentage: 3 },
|
||||
],
|
||||
row: {
|
||||
type: 'regression',
|
||||
|
@ -72,6 +71,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.navigation.navigateToDataFrameAnalytics();
|
||||
|
||||
await ml.testExecution.logTestStep('loads the source selection modal');
|
||||
|
||||
// Disable anti-aliasing to stabilize canvas image rendering assertions
|
||||
await ml.commonUI.disableAntiAliasing();
|
||||
|
||||
await ml.dataFrameAnalytics.startAnalyticsCreation();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
|
@ -100,9 +103,18 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.testExecution.logTestStep('displays the include fields selection');
|
||||
await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists();
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the sample size to 10000 for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.setScatterplotMatrixSampleSizeSelectValue('10000');
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the randomize query switch to true for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsCreation.setScatterplotMatrixRandomizeQueryCheckState(true);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the scatterplot matrix');
|
||||
await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement(
|
||||
'mlAnalyticsCreateJobWizardScatterplotMatrixFormRow',
|
||||
await ml.dataFrameAnalyticsCreation.assertScatterplotMatrix(
|
||||
testData.expected.scatterplotMatrixColorStats
|
||||
);
|
||||
|
||||
|
@ -224,10 +236,23 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.dataFrameAnalyticsResults.assertResultsTableExists();
|
||||
await ml.dataFrameAnalyticsResults.assertResultsTableTrainingFiltersExist();
|
||||
await ml.dataFrameAnalyticsResults.assertResultsTableNotEmpty();
|
||||
await ml.dataFrameAnalyticsCanvasElement.assertCanvasElement(
|
||||
'mlDFExpandableSection-splom',
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the sample size to 10000 for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsResults.setScatterplotMatrixSampleSizeSelectValue('10000');
|
||||
|
||||
await ml.testExecution.logTestStep(
|
||||
'sets the randomize query switch to true for the scatterplot matrix'
|
||||
);
|
||||
await ml.dataFrameAnalyticsResults.setScatterplotMatrixRandomizeQueryCheckState(true);
|
||||
|
||||
await ml.testExecution.logTestStep('displays the scatterplot matrix');
|
||||
await ml.dataFrameAnalyticsResults.assertScatterplotMatrix(
|
||||
testData.expected.scatterplotMatrixColorStats
|
||||
);
|
||||
|
||||
await ml.commonUI.resetAntiAliasing();
|
||||
});
|
||||
|
||||
it('displays the analytics job in the map view', async () => {
|
||||
|
|
|
@ -161,8 +161,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: 'currency',
|
||||
legend: '1 category',
|
||||
colorStats: [
|
||||
{ key: '#000000', value: 10 },
|
||||
{ key: '#54B399', value: 90 },
|
||||
{ color: '#000000', percentage: 10 },
|
||||
{ color: '#54B399', percentage: 90 },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -177,8 +177,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: 'customer_gender',
|
||||
legend: '2 categories',
|
||||
colorStats: [
|
||||
{ key: '#000000', value: 15 },
|
||||
{ key: '#54B399', value: 85 },
|
||||
{ color: '#000000', percentage: 15 },
|
||||
{ color: '#54B399', percentage: 85 },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -186,8 +186,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: 'customer_id',
|
||||
legend: 'top 20 of 46 categories',
|
||||
colorStats: [
|
||||
{ key: '#54B399', value: 35 },
|
||||
{ key: '#000000', value: 60 },
|
||||
{ color: '#54B399', percentage: 35 },
|
||||
{ color: '#000000', percentage: 60 },
|
||||
],
|
||||
},
|
||||
{ chartAvailable: false, id: 'customer_last_name', legend: 'Chart not supported.' },
|
||||
|
@ -196,8 +196,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: 'customer_phone',
|
||||
legend: '1 category',
|
||||
colorStats: [
|
||||
{ key: '#000000', value: 10 },
|
||||
{ key: '#54B399', value: 90 },
|
||||
{ color: '#000000', percentage: 10 },
|
||||
{ color: '#54B399', percentage: 90 },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -205,8 +205,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: 'day_of_week',
|
||||
legend: '7 categories',
|
||||
colorStats: [
|
||||
{ key: '#000000', value: 20 },
|
||||
{ key: '#54B399', value: 75 },
|
||||
{ color: '#000000', percentage: 20 },
|
||||
{ color: '#54B399', percentage: 75 },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -8,26 +8,13 @@
|
|||
import { rgb, nest } from 'd3';
|
||||
|
||||
interface ColorStat {
|
||||
key: string;
|
||||
value: number;
|
||||
color: string;
|
||||
percentage: number;
|
||||
pixels?: number;
|
||||
withinTolerance?: boolean;
|
||||
}
|
||||
|
||||
type ColorStats = ColorStat[];
|
||||
|
||||
/**
|
||||
* Returns if a given value is within the tolerated range of an expected value
|
||||
*
|
||||
* @param actualValue
|
||||
* @param expectedValue
|
||||
* @param toleranceRange
|
||||
* @returns if actualValue is within the tolerance of expectedValue
|
||||
*/
|
||||
function isValueWithinTolerance(actualValue: number, expectedValue: number, toleranceRange = 10) {
|
||||
const lower = expectedValue - toleranceRange / 2;
|
||||
const upper = expectedValue + toleranceRange / 2;
|
||||
return lower <= actualValue && upper >= actualValue;
|
||||
}
|
||||
export type CanvasElementColorStats = ColorStat[];
|
||||
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
|
@ -35,6 +22,28 @@ export async function CanvasElementProvider({ getService }: FtrProviderContext)
|
|||
const { driver } = await getService('__webdriver__').init();
|
||||
|
||||
return new (class CanvasElementService {
|
||||
// disable font anti-aliasing to be more resilient
|
||||
// against OS rendering differences
|
||||
public async disableAntiAliasing() {
|
||||
await driver.executeScript(
|
||||
`
|
||||
document.body.style["font-smooth"] = "never";
|
||||
document.body.style["-webkit-font-smoothing"] = "none";
|
||||
document.body.classList.add("mlDisableAntiAliasing");
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
public async resetAntiAliasing() {
|
||||
await driver.executeScript(
|
||||
`
|
||||
document.body.style["font-smooth"] = "";
|
||||
document.body.style["-webkit-font-smoothing"] = "";
|
||||
document.body.classList.remove("mlDisableAntiAliasing");
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the image data of a canvas element
|
||||
* @param selector querySelector to access the canvas element.
|
||||
|
@ -60,36 +69,33 @@ export async function CanvasElementProvider({ getService }: FtrProviderContext)
|
|||
*
|
||||
* @param selector querySelector to access the canvas element.
|
||||
* @param expectedColorStats - optional stats to compare against and check if the percentage is within the tolerance.
|
||||
* @param threshold - colors below this percentage threshold will be filtered from the returned list of colors
|
||||
* @param percentageThreshold - colors below this percentage threshold will be filtered from the returned list of colors
|
||||
* @param channelTolerance - tolerance for each RGB channel value
|
||||
* @param exclude - colors to exclude, useful for e.g. known background color values
|
||||
* @returns an array of colors and their percentage of appearance in the given image data
|
||||
*/
|
||||
public async getColorStats(
|
||||
selector: string,
|
||||
expectedColorStats?: ColorStats,
|
||||
threshold = 5
|
||||
): Promise<ColorStats> {
|
||||
expectedColorStats?: CanvasElementColorStats,
|
||||
exclude?: string[],
|
||||
percentageThreshold = 5,
|
||||
channelTolerance = 10,
|
||||
valueTolerance = 10
|
||||
): Promise<CanvasElementColorStats> {
|
||||
const imageData = await this.getImageData(selector);
|
||||
// transform the array of RGBA numbers to an array of hex values
|
||||
const colors: string[] = [];
|
||||
for (let i = 0; i < imageData.length; i += 4) {
|
||||
// uses d3's `rgb` method create a color object, `toString()` returns the hex value
|
||||
colors.push(
|
||||
rgb(imageData[i], imageData[i + 1], imageData[i + 2])
|
||||
.toString()
|
||||
.toUpperCase()
|
||||
);
|
||||
const r = imageData[i];
|
||||
const g = imageData[i + 1];
|
||||
const b = imageData[i + 2];
|
||||
const color = rgb(r, g, b).toString().toUpperCase();
|
||||
if (exclude === undefined || !exclude.includes(color)) colors.push(color);
|
||||
}
|
||||
|
||||
const expectedColorStatsMap =
|
||||
expectedColorStats !== undefined
|
||||
? expectedColorStats.reduce((p, c) => {
|
||||
p[c.key] = c.value;
|
||||
return p;
|
||||
}, {} as Record<string, number>)
|
||||
: {};
|
||||
|
||||
function getPixelPercentage(pixelsNum: number): number {
|
||||
return Math.round((pixelsNum / colors.length) * 100);
|
||||
return (pixelsNum / colors.length) * 100;
|
||||
}
|
||||
|
||||
// - d3's nest/key/entries methods will group the array of hex values so we can count
|
||||
|
@ -101,17 +107,133 @@ export async function CanvasElementProvider({ getService }: FtrProviderContext)
|
|||
return nest<string>()
|
||||
.key((d) => d)
|
||||
.entries(colors)
|
||||
.filter((s) => getPixelPercentage(s.values.length) >= threshold)
|
||||
.map((s) => {
|
||||
const value = getPixelPercentage(s.values.length);
|
||||
.filter((s) => getPixelPercentage(s.values.length) >= percentageThreshold)
|
||||
.sort((a, b) => a.key.localeCompare(b.key))
|
||||
.map((s, i) => {
|
||||
const percentage = getPixelPercentage(s.values.length);
|
||||
const pixels = s.values.length;
|
||||
return {
|
||||
key: s.key,
|
||||
value,
|
||||
color: s.key,
|
||||
percentage,
|
||||
pixels,
|
||||
...(expectedColorStats !== undefined
|
||||
? { withinTolerance: isValueWithinTolerance(value, expectedColorStatsMap[s.key]) }
|
||||
? {
|
||||
withinTolerance:
|
||||
this.isValueWithinTolerance(
|
||||
percentage,
|
||||
pixels,
|
||||
expectedColorStats[i]?.percentage,
|
||||
valueTolerance
|
||||
) &&
|
||||
this.isColorWithinTolerance(
|
||||
s.key,
|
||||
expectedColorStats[i]?.color,
|
||||
channelTolerance
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getColorStats() but also checks if each supplied
|
||||
* expected color lies within channelTolerance.
|
||||
*/
|
||||
public async getColorStatsWithColorTolerance(
|
||||
selector: string,
|
||||
expectedColorStats: CanvasElementColorStats,
|
||||
exclude?: string[],
|
||||
percentageThreshold = 0,
|
||||
channelTolerance = 10,
|
||||
valueTolerance = 10
|
||||
): Promise<CanvasElementColorStats> {
|
||||
const actualColorStats = await this.getColorStats(
|
||||
selector,
|
||||
undefined,
|
||||
exclude,
|
||||
percentageThreshold,
|
||||
channelTolerance,
|
||||
valueTolerance
|
||||
);
|
||||
|
||||
return expectedColorStats.map((expectedColor) => {
|
||||
const colorsWithinTolerance = actualColorStats.filter((d) =>
|
||||
this.isColorWithinTolerance(d.color, expectedColor.color, channelTolerance)
|
||||
);
|
||||
const colorPercentageWithinTolerance = colorsWithinTolerance.reduce(
|
||||
(sum, x) => sum + x.percentage,
|
||||
0
|
||||
);
|
||||
const pixelsWithinTolerance = colorsWithinTolerance.reduce(
|
||||
(sum, x) => sum + (x.pixels || 0),
|
||||
0
|
||||
);
|
||||
|
||||
return {
|
||||
color: expectedColor.color,
|
||||
percentage: colorPercentageWithinTolerance,
|
||||
pixels: pixelsWithinTolerance,
|
||||
withinTolerance: this.isValueWithinTolerance(
|
||||
colorPercentageWithinTolerance,
|
||||
pixelsWithinTolerance,
|
||||
expectedColor.percentage,
|
||||
valueTolerance
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a given color is within the tolerated range of an expected color
|
||||
*
|
||||
* @param actualColor
|
||||
* @param expectedColor
|
||||
* @param toleranceRange
|
||||
* @returns if actualColor is within the tolerance of expectedColor
|
||||
*/
|
||||
public isColorWithinTolerance(actualColor: string, expectedColor: string, toleranceRange = 10) {
|
||||
const actualRGB = rgb(actualColor);
|
||||
const expectedRGB = rgb(expectedColor);
|
||||
|
||||
const lowerR = expectedRGB.r - toleranceRange / 2;
|
||||
const upperR = expectedRGB.r + toleranceRange / 2;
|
||||
const lowerG = expectedRGB.g - toleranceRange / 2;
|
||||
const upperG = expectedRGB.g + toleranceRange / 2;
|
||||
const lowerB = expectedRGB.b - toleranceRange / 2;
|
||||
const upperB = expectedRGB.b + toleranceRange / 2;
|
||||
|
||||
return (
|
||||
lowerR <= actualRGB.r &&
|
||||
upperR >= actualRGB.r &&
|
||||
lowerG <= actualRGB.g &&
|
||||
upperG >= actualRGB.g &&
|
||||
lowerB <= actualRGB.b &&
|
||||
upperB >= actualRGB.b
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a given value is within the tolerated range of an expected value
|
||||
*
|
||||
* @param actualPercentage
|
||||
* @param actualPixels
|
||||
* @param expectedPercentage
|
||||
* @param toleranceRange
|
||||
* @returns if actualValue is within the tolerance of expectedValue
|
||||
*/
|
||||
public isValueWithinTolerance(
|
||||
actualPercentage: number,
|
||||
actualPixels: number,
|
||||
expectedPercentage: number,
|
||||
toleranceRange = 10
|
||||
) {
|
||||
const lower = expectedPercentage - toleranceRange / 2;
|
||||
const upper = expectedPercentage + toleranceRange / 2;
|
||||
return (
|
||||
// actualPercentage could be rounded to 0 so we check against actualPixels if they are above 0.
|
||||
actualPixels > 0 && lower <= actualPercentage && upper >= actualPercentage
|
||||
);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import { ProvidedType } from '@kbn/test/types/ftr';
|
|||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
import type { CanvasElementColorStats } from '../canvas_element';
|
||||
|
||||
interface SetValueOptions {
|
||||
clearWithKeyboard?: boolean;
|
||||
typeCharByChar?: boolean;
|
||||
|
@ -18,6 +20,7 @@ interface SetValueOptions {
|
|||
export type MlCommonUI = ProvidedType<typeof MachineLearningCommonUIProvider>;
|
||||
|
||||
export function MachineLearningCommonUIProvider({ getService }: FtrProviderContext) {
|
||||
const canvasElement = getService('canvasElement');
|
||||
const log = getService('log');
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
@ -212,5 +215,42 @@ export function MachineLearningCommonUIProvider({ getService }: FtrProviderConte
|
|||
`${testDataSubj} slider value should be '${expectedValue}' (got '${actualValue}')`
|
||||
);
|
||||
},
|
||||
|
||||
async disableAntiAliasing() {
|
||||
await canvasElement.disableAntiAliasing();
|
||||
},
|
||||
|
||||
async resetAntiAliasing() {
|
||||
await canvasElement.resetAntiAliasing();
|
||||
},
|
||||
|
||||
async assertColorsInCanvasElement(
|
||||
dataTestSubj: string,
|
||||
expectedColorStats: CanvasElementColorStats,
|
||||
exclude?: string[],
|
||||
percentageThreshold = 0,
|
||||
channelTolerance = 10,
|
||||
valueTolerance = 10
|
||||
) {
|
||||
await retry.tryForTime(30 * 1000, async () => {
|
||||
await testSubjects.existOrFail(dataTestSubj);
|
||||
|
||||
const actualColorStatsWithTolerance = await canvasElement.getColorStatsWithColorTolerance(
|
||||
`[data-test-subj="${dataTestSubj}"] canvas`,
|
||||
expectedColorStats,
|
||||
exclude,
|
||||
percentageThreshold,
|
||||
channelTolerance,
|
||||
valueTolerance
|
||||
);
|
||||
|
||||
expect(actualColorStatsWithTolerance.every((d) => d.withinTolerance)).to.eql(
|
||||
true,
|
||||
`Color stats for '${dataTestSubj}' should be within tolerance. Expected: '${JSON.stringify(
|
||||
expectedColorStats
|
||||
)}' (got '${JSON.stringify(actualColorStatsWithTolerance)}')`
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningDataFrameAnalyticsCanvasElementProvider({
|
||||
getService,
|
||||
}: FtrProviderContext) {
|
||||
const canvasElement = getService('canvasElement');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return new (class AnalyticsCanvasElement {
|
||||
public async assertCanvasElement(
|
||||
dataTestSubj: string,
|
||||
expectedColorStats: Array<{
|
||||
key: string;
|
||||
value: number;
|
||||
}>
|
||||
) {
|
||||
await testSubjects.existOrFail(dataTestSubj);
|
||||
|
||||
const actualColorStats = await canvasElement.getColorStats(
|
||||
`[data-test-subj="${dataTestSubj}"] canvas`,
|
||||
expectedColorStats,
|
||||
1
|
||||
);
|
||||
expect(actualColorStats.every((d) => d.withinTolerance)).to.eql(
|
||||
true,
|
||||
`Color stats for canvas element should be within tolerance. Expected: '${JSON.stringify(
|
||||
expectedColorStats
|
||||
)}' (got '${JSON.stringify(actualColorStats)}')`
|
||||
);
|
||||
}
|
||||
})();
|
||||
}
|
|
@ -9,7 +9,8 @@ import expect from '@kbn/expect';
|
|||
import { DataFrameAnalyticsConfig } from '../../../../plugins/ml/public/application/data_frame_analytics/common';
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { MlCommonUI } from './common_ui';
|
||||
import type { CanvasElementColorStats } from '../canvas_element';
|
||||
import type { MlCommonUI } from './common_ui';
|
||||
import { MlApi } from './api';
|
||||
import {
|
||||
isRegressionAnalysis,
|
||||
|
@ -255,6 +256,62 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
|
|||
await this.assertDependentVariableSelection([dependentVariable]);
|
||||
},
|
||||
|
||||
async assertScatterplotMatrix(expectedValue: CanvasElementColorStats) {
|
||||
await testSubjects.existOrFail(
|
||||
'mlAnalyticsCreateJobWizardScatterplotMatrixPanel > mlScatterplotMatrix loaded',
|
||||
{
|
||||
timeout: 5000,
|
||||
}
|
||||
);
|
||||
await testSubjects.scrollIntoView(
|
||||
'mlAnalyticsCreateJobWizardScatterplotMatrixPanel > mlScatterplotMatrix loaded'
|
||||
);
|
||||
await mlCommonUI.assertColorsInCanvasElement(
|
||||
'mlAnalyticsCreateJobWizardScatterplotMatrixPanel',
|
||||
expectedValue,
|
||||
['#000000']
|
||||
);
|
||||
},
|
||||
|
||||
async setScatterplotMatrixSampleSizeSelectValue(selectValue: string) {
|
||||
await testSubjects.selectValue('mlScatterplotMatrixSampleSizeSelect', selectValue);
|
||||
|
||||
const actualSelectState = await testSubjects.getAttribute(
|
||||
'mlScatterplotMatrixSampleSizeSelect',
|
||||
'value'
|
||||
);
|
||||
|
||||
expect(actualSelectState).to.eql(
|
||||
selectValue,
|
||||
`Sample size should be '${selectValue}' (got '${actualSelectState}')`
|
||||
);
|
||||
},
|
||||
|
||||
async getScatterplotMatrixRandomizeQuerySwitchCheckState(): Promise<boolean> {
|
||||
const state = await testSubjects.getAttribute(
|
||||
'mlScatterplotMatrixRandomizeQuerySwitch',
|
||||
'aria-checked'
|
||||
);
|
||||
return state === 'true';
|
||||
},
|
||||
|
||||
async assertScatterplotMatrixRandomizeQueryCheckState(expectedCheckState: boolean) {
|
||||
const actualCheckState = await this.getScatterplotMatrixRandomizeQuerySwitchCheckState();
|
||||
expect(actualCheckState).to.eql(
|
||||
expectedCheckState,
|
||||
`Randomize query check state should be '${expectedCheckState}' (got '${actualCheckState}')`
|
||||
);
|
||||
},
|
||||
|
||||
async setScatterplotMatrixRandomizeQueryCheckState(checkState: boolean) {
|
||||
await retry.tryForTime(30000, async () => {
|
||||
if ((await this.getScatterplotMatrixRandomizeQuerySwitchCheckState()) !== checkState) {
|
||||
await testSubjects.click('mlScatterplotMatrixRandomizeQuerySwitch');
|
||||
}
|
||||
await this.assertScatterplotMatrixRandomizeQueryCheckState(checkState);
|
||||
});
|
||||
},
|
||||
|
||||
async assertTrainingPercentInputExists() {
|
||||
await testSubjects.existOrFail('mlAnalyticsCreateJobWizardTrainingPercentSlider');
|
||||
},
|
||||
|
|
|
@ -10,9 +10,13 @@ import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrap
|
|||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export function MachineLearningDataFrameAnalyticsResultsProvider({
|
||||
getService,
|
||||
}: FtrProviderContext) {
|
||||
import type { CanvasElementColorStats } from '../canvas_element';
|
||||
import type { MlCommonUI } from './common_ui';
|
||||
|
||||
export function MachineLearningDataFrameAnalyticsResultsProvider(
|
||||
{ getService }: FtrProviderContext,
|
||||
mlCommonUI: MlCommonUI
|
||||
) {
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
|
@ -81,6 +85,62 @@ export function MachineLearningDataFrameAnalyticsResultsProvider({
|
|||
});
|
||||
},
|
||||
|
||||
async setScatterplotMatrixSampleSizeSelectValue(selectValue: string) {
|
||||
await testSubjects.selectValue('mlScatterplotMatrixSampleSizeSelect', selectValue);
|
||||
|
||||
const actualSelectState = await testSubjects.getAttribute(
|
||||
'mlScatterplotMatrixSampleSizeSelect',
|
||||
'value'
|
||||
);
|
||||
|
||||
expect(actualSelectState).to.eql(
|
||||
selectValue,
|
||||
`Sample size should be '${selectValue}' (got '${actualSelectState}')`
|
||||
);
|
||||
},
|
||||
|
||||
async getScatterplotMatrixRandomizeQuerySwitchCheckState(): Promise<boolean> {
|
||||
const state = await testSubjects.getAttribute(
|
||||
'mlScatterplotMatrixRandomizeQuerySwitch',
|
||||
'aria-checked'
|
||||
);
|
||||
return state === 'true';
|
||||
},
|
||||
|
||||
async assertScatterplotMatrixRandomizeQueryCheckState(expectedCheckState: boolean) {
|
||||
const actualCheckState = await this.getScatterplotMatrixRandomizeQuerySwitchCheckState();
|
||||
expect(actualCheckState).to.eql(
|
||||
expectedCheckState,
|
||||
`Randomize query check state should be '${expectedCheckState}' (got '${actualCheckState}')`
|
||||
);
|
||||
},
|
||||
|
||||
async setScatterplotMatrixRandomizeQueryCheckState(checkState: boolean) {
|
||||
await retry.tryForTime(30000, async () => {
|
||||
if ((await this.getScatterplotMatrixRandomizeQuerySwitchCheckState()) !== checkState) {
|
||||
await testSubjects.click('mlScatterplotMatrixRandomizeQuerySwitch');
|
||||
}
|
||||
await this.assertScatterplotMatrixRandomizeQueryCheckState(checkState);
|
||||
});
|
||||
},
|
||||
|
||||
async assertScatterplotMatrix(expectedValue: CanvasElementColorStats) {
|
||||
await testSubjects.existOrFail('mlDFExpandableSection-splom > mlScatterplotMatrix loaded', {
|
||||
timeout: 5000,
|
||||
});
|
||||
await testSubjects.scrollIntoView('mlDFExpandableSection-splom > mlScatterplotMatrix loaded');
|
||||
await mlCommonUI.assertColorsInCanvasElement(
|
||||
'mlDFExpandableSection-splom',
|
||||
expectedValue,
|
||||
['#000000'],
|
||||
undefined,
|
||||
undefined,
|
||||
// increased tolerance up from 10 to 20
|
||||
// since the returned randomized colors vary quite a bit on each run.
|
||||
20
|
||||
);
|
||||
},
|
||||
|
||||
async assertFeatureImportanceDecisionPathChartElementsExists() {
|
||||
await testSubjects.existOrFail('mlDFADecisionPathChart', {
|
||||
timeout: 5000,
|
||||
|
|
|
@ -18,7 +18,6 @@ import { MachineLearningDataFrameAnalyticsProvider } from './data_frame_analytic
|
|||
import { MachineLearningDataFrameAnalyticsCreationProvider } from './data_frame_analytics_creation';
|
||||
import { MachineLearningDataFrameAnalyticsEditProvider } from './data_frame_analytics_edit';
|
||||
import { MachineLearningDataFrameAnalyticsResultsProvider } from './data_frame_analytics_results';
|
||||
import { MachineLearningDataFrameAnalyticsCanvasElementProvider } from './data_frame_analytics_canvas_element';
|
||||
import { MachineLearningDataFrameAnalyticsMapProvider } from './data_frame_analytics_map';
|
||||
import { MachineLearningDataFrameAnalyticsTableProvider } from './data_frame_analytics_table';
|
||||
import { MachineLearningDataVisualizerProvider } from './data_visualizer';
|
||||
|
@ -63,12 +62,12 @@ export function MachineLearningProvider(context: FtrProviderContext) {
|
|||
api
|
||||
);
|
||||
const dataFrameAnalyticsEdit = MachineLearningDataFrameAnalyticsEditProvider(context, commonUI);
|
||||
const dataFrameAnalyticsResults = MachineLearningDataFrameAnalyticsResultsProvider(context);
|
||||
const dataFrameAnalyticsResults = MachineLearningDataFrameAnalyticsResultsProvider(
|
||||
context,
|
||||
commonUI
|
||||
);
|
||||
const dataFrameAnalyticsMap = MachineLearningDataFrameAnalyticsMapProvider(context);
|
||||
const dataFrameAnalyticsTable = MachineLearningDataFrameAnalyticsTableProvider(context);
|
||||
const dataFrameAnalyticsCanvasElement = MachineLearningDataFrameAnalyticsCanvasElementProvider(
|
||||
context
|
||||
);
|
||||
|
||||
const dataVisualizer = MachineLearningDataVisualizerProvider(context);
|
||||
const dataVisualizerTable = MachineLearningDataVisualizerTableProvider(context, commonUI);
|
||||
|
@ -113,7 +112,6 @@ export function MachineLearningProvider(context: FtrProviderContext) {
|
|||
dataFrameAnalyticsResults,
|
||||
dataFrameAnalyticsMap,
|
||||
dataFrameAnalyticsTable,
|
||||
dataFrameAnalyticsCanvasElement,
|
||||
dataVisualizer,
|
||||
dataVisualizerFileBased,
|
||||
dataVisualizerIndexBased,
|
||||
|
|
|
@ -10,6 +10,15 @@ import expect from '@kbn/expect';
|
|||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
import type { CanvasElementColorStats } from '../canvas_element';
|
||||
|
||||
export type HistogramCharts = Array<{
|
||||
chartAvailable: boolean;
|
||||
id: string;
|
||||
legend?: string;
|
||||
colorStats?: CanvasElementColorStats;
|
||||
}>;
|
||||
|
||||
export function TransformWizardProvider({ getService }: FtrProviderContext) {
|
||||
const aceEditor = getService('aceEditor');
|
||||
const canvasElement = getService('canvasElement');
|
||||
|
@ -197,14 +206,7 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) {
|
|||
);
|
||||
},
|
||||
|
||||
async assertIndexPreviewHistogramCharts(
|
||||
expectedHistogramCharts: Array<{
|
||||
chartAvailable: boolean;
|
||||
id: string;
|
||||
legend: string;
|
||||
colorStats?: any[];
|
||||
}>
|
||||
) {
|
||||
async assertIndexPreviewHistogramCharts(expectedHistogramCharts: HistogramCharts) {
|
||||
// For each chart, get the content of each header cell and assert
|
||||
// the legend text and column id and if the chart should be present or not.
|
||||
await retry.tryForTime(5000, async () => {
|
||||
|
@ -215,17 +217,25 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) {
|
|||
await testSubjects.existOrFail(`mlDataGridChart-${index}-histogram`);
|
||||
|
||||
if (expected.colorStats !== undefined) {
|
||||
const actualColorStats = await canvasElement.getColorStats(
|
||||
`[data-test-subj="mlDataGridChart-${index}-histogram"] .echCanvasRenderer`,
|
||||
expected.colorStats
|
||||
const sortedExpectedColorStats = [...expected.colorStats].sort((a, b) =>
|
||||
a.color.localeCompare(b.color)
|
||||
);
|
||||
|
||||
const actualColorStats = await canvasElement.getColorStats(
|
||||
`[data-test-subj="mlDataGridChart-${index}-histogram"] .echCanvasRenderer`,
|
||||
sortedExpectedColorStats
|
||||
);
|
||||
|
||||
expect(actualColorStats.length).to.eql(
|
||||
sortedExpectedColorStats.length,
|
||||
`Expected and actual color stats for column '${expected.id}' should have the same amount of elements. Expected: ${sortedExpectedColorStats.length} (got ${actualColorStats.length})`
|
||||
);
|
||||
expect(actualColorStats.every((d) => d.withinTolerance)).to.eql(
|
||||
true,
|
||||
`Color stats for column '${
|
||||
expected.id
|
||||
}' should be within tolerance. Expected: '${JSON.stringify(
|
||||
expected.colorStats
|
||||
sortedExpectedColorStats
|
||||
)}' (got '${JSON.stringify(actualColorStats)}')`
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue