blob: 931f026cb5fe19813a6510338f1a8f45abc218dc [file] [log] [blame]
/**
* 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 { mergeReplaceArrays } from '@superset-ui/core';
describe('Theme Override Deep Merge Behavior', () => {
test('should merge nested objects correctly', () => {
const baseOptions = {
grid: {
left: '5%',
right: '5%',
top: '10%',
},
xAxis: {
type: 'category',
axisLabel: {
fontSize: 12,
},
},
};
const globalOverrides = {
grid: {
left: '10%',
bottom: '15%',
},
xAxis: {
axisLabel: {
color: '#333',
rotate: 45,
},
},
};
const result = mergeReplaceArrays(baseOptions, globalOverrides);
expect(result).toEqual({
grid: {
left: '10%', // overridden
right: '5%', // preserved
top: '10%', // preserved
bottom: '15%', // added
},
xAxis: {
type: 'category', // preserved
axisLabel: {
fontSize: 12, // preserved
color: '#333', // added
rotate: 45, // added
},
},
});
});
test('should replace arrays instead of merging them', () => {
const baseOptions = {
series: [
{ name: 'Series 1', type: 'line' },
{ name: 'Series 2', type: 'bar' },
],
};
const overrides = {
series: [{ name: 'New Series', type: 'pie' }],
};
const result = mergeReplaceArrays(baseOptions, overrides);
// Arrays are replaced entirely, not merged by index
expect(result.series).toEqual([{ name: 'New Series', type: 'pie' }]);
expect(result.series).toHaveLength(1);
});
test('should handle null overrides correctly', () => {
const baseOptions = {
grid: {
left: '5%',
right: '5%',
top: '10%',
},
tooltip: {
show: true,
backgroundColor: '#fff',
},
};
const overrides = {
grid: {
left: null,
bottom: '20%',
},
tooltip: {
backgroundColor: null,
borderColor: '#ccc',
},
};
const result = mergeReplaceArrays(baseOptions, overrides);
expect(result).toEqual({
grid: {
left: null, // overridden with null
right: '5%', // preserved (undefined values are ignored by lodash merge)
top: '10%', // preserved
bottom: '20%', // added
},
tooltip: {
show: true, // preserved
backgroundColor: null, // overridden with null
borderColor: '#ccc', // added
},
});
});
test('should handle override precedence correctly', () => {
const baseTheme = {
textStyle: { color: '#000', fontSize: 12 },
};
const pluginOptions = {
textStyle: { fontSize: 14 },
title: { text: 'Chart Title' },
};
const globalOverrides = {
textStyle: { color: '#333' },
grid: { left: '10%' },
};
const chartOverrides = {
textStyle: { color: '#666', fontWeight: 'bold' },
legend: { orient: 'vertical' },
};
// Simulate the merge order in Echart.tsx
const result = mergeReplaceArrays(
baseTheme,
pluginOptions,
globalOverrides,
chartOverrides,
);
expect(result).toEqual({
textStyle: {
color: '#666', // chart override wins
fontSize: 14, // from plugin options
fontWeight: 'bold', // from chart override
},
title: { text: 'Chart Title' }, // from plugin options
grid: { left: '10%' }, // from global override
legend: { orient: 'vertical' }, // from chart override
});
});
test('should preserve deep nested structures', () => {
const baseOptions = {
xAxis: {
axisLabel: {
textStyle: {
color: '#000',
fontSize: 12,
fontFamily: 'Arial',
},
},
},
};
const overrides = {
xAxis: {
axisLabel: {
textStyle: {
color: '#333',
fontWeight: 'bold',
},
rotate: 45,
},
splitLine: {
show: true,
},
},
};
const result = mergeReplaceArrays(baseOptions, overrides);
expect(result).toEqual({
xAxis: {
axisLabel: {
textStyle: {
color: '#333', // overridden
fontSize: 12, // preserved
fontFamily: 'Arial', // preserved
fontWeight: 'bold', // added
},
rotate: 45, // added
},
splitLine: {
show: true, // added
},
},
});
});
test('should handle function values correctly', () => {
const formatFunction = (value: any) => `${value}%`;
const overrideFunction = (value: any) => `$${value}`;
const baseOptions = {
yAxis: {
axisLabel: {
formatter: formatFunction,
},
},
};
const overrides = {
yAxis: {
axisLabel: {
formatter: overrideFunction,
},
},
};
const result = mergeReplaceArrays(baseOptions, overrides);
expect(result.yAxis.axisLabel.formatter).toBe(overrideFunction);
expect(result.yAxis.axisLabel.formatter('100')).toBe('$100');
});
test('should handle empty objects and arrays', () => {
const baseOptions = {
series: [{ name: 'Test', data: [1, 2, 3] }],
grid: { left: '5%' },
};
const emptyOverrides = {};
const arrayOverride = { series: [] };
const objectOverride = { grid: {} };
const resultEmpty = mergeReplaceArrays(baseOptions, emptyOverrides);
const resultArray = mergeReplaceArrays(baseOptions, arrayOverride);
const resultObject = mergeReplaceArrays(baseOptions, objectOverride);
expect(resultEmpty).toEqual(baseOptions);
// Empty array completely replaces existing array
expect(resultArray.series).toEqual([]);
expect(resultObject.grid).toEqual({ left: '5%' });
});
});