mirror of
https://github.com/apache/superset.git
synced 2026-04-23 18:14:56 +00:00
394 lines
12 KiB
TypeScript
394 lines
12 KiB
TypeScript
/**
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
import { ControlPanelState } from '../../src/types';
|
|
|
|
// Mock the utilities to avoid complex dependencies
|
|
jest.mock('../../src/utils', () => ({
|
|
formatSelectOptions: jest.fn((options: any[]) =>
|
|
options.map((opt: any) => [opt, opt]),
|
|
),
|
|
displayTimeRelatedControls: jest.fn(() => true),
|
|
getColorControlsProps: jest.fn(() => ({})),
|
|
D3_FORMAT_OPTIONS: [],
|
|
D3_FORMAT_DOCS: '',
|
|
D3_TIME_FORMAT_OPTIONS: [],
|
|
D3_TIME_FORMAT_DOCS: '',
|
|
DEFAULT_TIME_FORMAT: '%Y-%m-%d',
|
|
DEFAULT_NUMBER_FORMAT: '',
|
|
}));
|
|
|
|
// Mock shared controls
|
|
const mockSharedControls = {
|
|
matrixify_dimension_x: {
|
|
shouldMapStateToProps: (
|
|
prevState: ControlPanelState,
|
|
state: ControlPanelState,
|
|
) => {
|
|
const fieldsToCheck = [
|
|
'matrixify_topn_value_x',
|
|
'matrixify_topn_metric_x',
|
|
'matrixify_topn_order_x',
|
|
'matrixify_dimension_selection_mode_x',
|
|
];
|
|
return fieldsToCheck.some(
|
|
field => prevState?.form_data?.[field] !== state?.form_data?.[field],
|
|
);
|
|
},
|
|
mapStateToProps: ({ datasource, controls, form_data }: any) => {
|
|
const getValue = (key: string, defaultValue?: any) =>
|
|
form_data?.[key] ?? controls?.[key]?.value ?? defaultValue;
|
|
|
|
return {
|
|
datasource,
|
|
selectionMode: getValue(
|
|
'matrixify_dimension_selection_mode_x',
|
|
'members',
|
|
),
|
|
topNMetric: getValue('matrixify_topn_metric_x'),
|
|
topNValue: getValue('matrixify_topn_value_x'),
|
|
topNOrder: getValue('matrixify_topn_order_x'),
|
|
formData: form_data,
|
|
};
|
|
},
|
|
},
|
|
matrixify_dimension_y: {
|
|
shouldMapStateToProps: (
|
|
prevState: ControlPanelState,
|
|
state: ControlPanelState,
|
|
) => {
|
|
const fieldsToCheck = [
|
|
'matrixify_topn_value_y',
|
|
'matrixify_topn_metric_y',
|
|
'matrixify_topn_order_y',
|
|
'matrixify_dimension_selection_mode_y',
|
|
];
|
|
return fieldsToCheck.some(
|
|
field => prevState?.form_data?.[field] !== state?.form_data?.[field],
|
|
);
|
|
},
|
|
mapStateToProps: ({ datasource, controls, form_data }: any) => {
|
|
const getValue = (key: string, defaultValue?: any) =>
|
|
form_data?.[key] ?? controls?.[key]?.value ?? defaultValue;
|
|
|
|
return {
|
|
datasource,
|
|
selectionMode: getValue(
|
|
'matrixify_dimension_selection_mode_y',
|
|
'members',
|
|
),
|
|
topNMetric: getValue('matrixify_topn_metric_y'),
|
|
topNValue: getValue('matrixify_topn_value_y'),
|
|
topNOrder: getValue('matrixify_topn_order_y'),
|
|
formData: form_data,
|
|
};
|
|
},
|
|
},
|
|
};
|
|
|
|
const createMockState = (
|
|
formData: any = {},
|
|
controls: any = {},
|
|
): ControlPanelState => ({
|
|
slice: { slice_id: 123 },
|
|
form_data: formData,
|
|
datasource: null,
|
|
controls,
|
|
common: {},
|
|
metadata: {},
|
|
});
|
|
|
|
const createMockControlState = (value: any = null) => ({ value });
|
|
|
|
test('matrixify_dimension_x should return true when topN value changes', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 10, // Changed
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(true);
|
|
});
|
|
|
|
test('matrixify_dimension_x should return true when topN metric changes', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric2', // Changed
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(true);
|
|
});
|
|
|
|
test('matrixify_dimension_x should return true when topN order changes', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'asc', // Changed
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(true);
|
|
});
|
|
|
|
test('matrixify_dimension_x should return true when selection mode changes', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'members', // Changed
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(true);
|
|
});
|
|
|
|
test('matrixify_dimension_x should return false when no relevant fields change', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
unrelated_field: 'value1',
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
unrelated_field: 'value2', // Changed, but not relevant
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(false);
|
|
});
|
|
|
|
test('matrixify_dimension_x should return false when states are identical', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const state = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_order_x: 'desc',
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(state, state)).toBe(false);
|
|
});
|
|
|
|
test('matrixify_dimension_x should handle missing form_data gracefully', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState(); // No form_data
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(true);
|
|
});
|
|
|
|
test('matrixify_dimension_x should handle undefined values gracefully', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_x: undefined,
|
|
matrixify_topn_metric_x: null,
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 5,
|
|
matrixify_topn_metric_x: 'metric1',
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(true);
|
|
});
|
|
|
|
test('matrixify_dimension_y should check y-axis specific fields', () => {
|
|
const control = mockSharedControls.matrixify_dimension_y;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_y: 5,
|
|
matrixify_topn_metric_y: 'metric1',
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_y: 10, // Changed
|
|
matrixify_topn_metric_y: 'metric1',
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(true);
|
|
});
|
|
|
|
test('matrixify_dimension_y should not trigger on x-axis changes', () => {
|
|
const control = mockSharedControls.matrixify_dimension_y;
|
|
|
|
const prevState = createMockState({
|
|
matrixify_topn_value_x: 5, // x-axis field
|
|
matrixify_topn_value_y: 5, // y-axis field (unchanged)
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
matrixify_topn_value_x: 10, // x-axis field changed
|
|
matrixify_topn_value_y: 5, // y-axis field (unchanged)
|
|
});
|
|
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(false);
|
|
});
|
|
|
|
test('mapStateToProps should map form_data values correctly', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const state = createMockState({
|
|
matrixify_dimension_selection_mode_x: 'topn',
|
|
matrixify_topn_metric_x: 'metric1',
|
|
matrixify_topn_value_x: 10,
|
|
matrixify_topn_order_x: 'desc',
|
|
});
|
|
|
|
const mockDatasource: any = { id: 1, columns: [] };
|
|
state.datasource = mockDatasource;
|
|
|
|
const result = control.mapStateToProps!(state);
|
|
|
|
expect(result).toEqual({
|
|
datasource: mockDatasource,
|
|
selectionMode: 'topn',
|
|
topNMetric: 'metric1',
|
|
topNValue: 10,
|
|
topNOrder: 'desc',
|
|
formData: state.form_data,
|
|
});
|
|
});
|
|
|
|
test('mapStateToProps should fall back to control values when form_data is missing', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const state = createMockState(
|
|
{}, // Empty form_data
|
|
{
|
|
matrixify_dimension_selection_mode_x: createMockControlState('members'),
|
|
matrixify_topn_metric_x: createMockControlState('metric2'),
|
|
matrixify_topn_value_x: createMockControlState(15),
|
|
},
|
|
);
|
|
|
|
const result = control.mapStateToProps!(state);
|
|
|
|
expect(result.selectionMode).toBe('members');
|
|
expect(result.topNMetric).toBe('metric2');
|
|
expect(result.topNValue).toBe(15);
|
|
});
|
|
|
|
test('mapStateToProps should use default values when both form_data and controls are missing', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const state = createMockState({}, {});
|
|
|
|
const result = control.mapStateToProps!(state);
|
|
|
|
expect(result.selectionMode).toBe('members'); // Default value
|
|
expect(result.topNMetric).toBeUndefined();
|
|
expect(result.topNValue).toBeUndefined();
|
|
expect(result.topNOrder).toBeUndefined();
|
|
});
|
|
|
|
test('mapStateToProps should prioritize form_data over control values', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const state = createMockState(
|
|
{
|
|
matrixify_dimension_selection_mode_x: 'topn', // form_data value
|
|
},
|
|
{
|
|
matrixify_dimension_selection_mode_x: createMockControlState('members'), // control value
|
|
},
|
|
);
|
|
|
|
const result = control.mapStateToProps!(state);
|
|
|
|
expect(result.selectionMode).toBe('topn'); // Should use form_data value
|
|
});
|
|
|
|
test('should efficiently check only relevant fields', () => {
|
|
const control = mockSharedControls.matrixify_dimension_x;
|
|
|
|
const prevState = createMockState({
|
|
// Many fields, only some relevant
|
|
field1: 'value1',
|
|
field2: 'value2',
|
|
matrixify_topn_value_x: 5, // Relevant
|
|
field3: 'value3',
|
|
matrixify_topn_metric_x: 'metric1', // Relevant
|
|
field4: 'value4',
|
|
matrixify_other_control: 'value5',
|
|
});
|
|
|
|
const nextState = createMockState({
|
|
field1: 'value1_changed', // Not relevant
|
|
field2: 'value2_changed', // Not relevant
|
|
matrixify_topn_value_x: 5, // Relevant, unchanged
|
|
field3: 'value3_changed', // Not relevant
|
|
matrixify_topn_metric_x: 'metric1', // Relevant, unchanged
|
|
field4: 'value4_changed', // Not relevant
|
|
matrixify_other_control: 'value5_changed', // Not relevant
|
|
});
|
|
|
|
// Should return false because no relevant fields changed
|
|
expect(control.shouldMapStateToProps!(prevState, nextState)).toBe(false);
|
|
});
|