From afa41f0ff345b0b5014730d24960df1ce86e950a Mon Sep 17 00:00:00 2001 From: zjyhhhher <1059752643@qq.com> Date: Mon, 26 May 2025 21:21:52 +0800 Subject: [PATCH 1/3] test: add test case of getChartSpecWithContext when chartType is circlePacking --- ...ChartSpecWithContext_circlePacking.test.ts | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts diff --git a/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts b/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts new file mode 100644 index 00000000..e0e737f7 --- /dev/null +++ b/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts @@ -0,0 +1,121 @@ +import { Dict } from '@visactor/vutils'; +import { getChartSpecWithContext } from '../../src/atom/chartGenerator/spec'; +import type { GenerateChartCellContext } from '../../src/atom/chartGenerator/type'; +import { ChartType } from '../../src/types'; + +const CHART_TYPE_LIST = [ + 'Bar Chart', + 'Line Chart', + 'Area Chart', + 'Pie Chart', + 'Scatter Plot', + 'Word Cloud', + 'Rose Chart', + 'Radar Chart', + 'Sankey Chart', + 'Funnel Chart', + 'Dual Axis Chart', + 'Waterfall Chart', + 'Box Plot', + 'Linear Progress chart', + 'Circular Progress chart', + 'Liquid Chart', + 'Bubble Circle Packing', + 'Map Chart', + 'Range Column Chart', + 'Sunburst Chart', + 'Treemap Chart', + 'Gauge Chart', + 'Basic Heat Map', + 'Venn Chart', + 'Dynamic Bar Chart' +]; + +const generateRandomValue = (min = 100, max = 2000) => Math.floor(Math.random() * (max - min + 1)) + min; + +function generateDataStructure() { + const createProducts = () => [ + { name: 'Office Supplies', value: generateRandomValue() }, + { name: 'Furniture', value: generateRandomValue() }, + { name: 'Electronic equipment', value: generateRandomValue() } + ]; + + const createRegions = (count: number) => + Array.from({ length: count }, (_, i) => ({ + name: `Region${i + 1}`, + children: createProducts() + })); + + const createCountries = (names: string[]) => + names.map(country => ({ + name: country, + children: createRegions(6) // 每个国家6个地区 + })); + + return [ + { + name: 'root', + children: createCountries(['Country A', 'Country B', 'Country C']) + } + ]; +} +const dataItem = generateDataStructure(); +//console.log("dataItem", JSON.stringify(dataItem, null, 2)); + +describe('getChartSpecWithContext', () => { + it('should generate correct basic circle packing spec', () => { + //console.log("data",data); + const context = { + chartTypeList: CHART_TYPE_LIST, + transpose: false, + command: 'Generate a BasicCircle Packing chart', + cell: { x: 'name', size: 'value' }, + dataTable: dataItem, + chartType: ChartType.BubbleCirclePacking.toUpperCase() + }; + const { chartType, spec } = getChartSpecWithContext(context); + //console.log("basic spec", JSON.stringify(spec, null, 2)); + expect(chartType).toBe(ChartType.BubbleCirclePacking); + expect(spec.type).toBe('circlePacking'); + expect(spec.data.values).toEqual(dataItem); + }); + + it('should generate correct bubble circle packing spec', () => { + const buble_data = new Array(19).fill(0).map((_, i) => { + return { + name: `bubble-${i + 1}`, + value: i + 1 + }; + }); + const context = { + chartTypeList: CHART_TYPE_LIST, + transpose: false, + command: 'Generate a Bubble Circle Packing chart', + cell: { x: 'name', size: 'value' }, + dataTable: buble_data, + chartType: ChartType.BubbleCirclePacking.toUpperCase() + }; + const { chartType, spec } = getChartSpecWithContext(context); + //console.log("bubble spec", JSON.stringify(spec, null, 2)); + expect(chartType).toBe(ChartType.BubbleCirclePacking); + expect(spec.type).toBe('circlePacking'); + expect(spec.data.values).toEqual(buble_data); + }); + + it('should generate correct nulti-root circle packing spec', () => { + const multi_root_data = dataItem[0].children; + const context = { + chartTypeList: CHART_TYPE_LIST, + transpose: false, + command: 'Generate a Multi-root Circle Packing chart', + cell: { x: 'name', size: 'value' }, + dataTable: multi_root_data, + chartType: ChartType.BubbleCirclePacking.toUpperCase() + }; + const { chartType, spec } = getChartSpecWithContext(context); + //console.log("multi-root spec", JSON.stringify(spec, null, 2)); + expect(chartType).toBe(ChartType.BubbleCirclePacking); + expect(spec.type).toBe('circlePacking'); + expect(spec.data.values).toEqual(multi_root_data); + }); +}); From adf83f4c7f4e24e252249721d999e33c7d801c51 Mon Sep 17 00:00:00 2001 From: zjyhhhher <1059752643@qq.com> Date: Tue, 3 Jun 2025 02:17:42 +0800 Subject: [PATCH 2/3] test: add test case of getChartSpecWithContext when chartType is circlePacking (comprehensive version) --- ...ChartSpecWithContext_circlePacking.test.ts | 155 +++++++++++++++++- 1 file changed, 147 insertions(+), 8 deletions(-) diff --git a/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts b/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts index e0e737f7..02677771 100644 --- a/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts +++ b/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts @@ -2,6 +2,9 @@ import { Dict } from '@visactor/vutils'; import { getChartSpecWithContext } from '../../src/atom/chartGenerator/spec'; import type { GenerateChartCellContext } from '../../src/atom/chartGenerator/type'; import { ChartType } from '../../src/types'; +import { builtinThemeMap } from '../../src/atom/chartGenerator/const'; +import { BuiltinThemeType } from '../../src/atom/chartGenerator/const'; +import { COLOR_THEMES } from '../../src/atom/chartGenerator/spec/constants'; const CHART_TYPE_LIST = [ 'Bar Chart', @@ -63,21 +66,131 @@ const dataItem = generateDataStructure(); //console.log("dataItem", JSON.stringify(dataItem, null, 2)); describe('getChartSpecWithContext', () => { - it('should generate correct basic circle packing spec', () => { + it('should generate full spec for circlepacking chart with diverse inputs', () => { + //console.log("data",data); + const context = { + chartTypeList: CHART_TYPE_LIST, + cell: { x: 'name', size: 'value' }, + dataTable: dataItem, + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + chartTheme: 'semiThemeLight', + colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'], + totalTime: 5, + simpleVChartSpec: { + type: 'circlePacking', + title: [ + { text: 'CirclePacking Chart-1', orient: 'top' }, + { text: 'CirclePacking Chart-2', orient: 'left' } + ], + dataZoom: [{ orient: 'left' }, { orient: 'bottom' }], + label: [{ position: 'top', style: { fill: 'black' } }], + indicator: [ + { + title: 'Cluster A', + content: ['Overlap A'] + } + ], + palette: ['#123456', '#abcdef'], + background: '#ffffff' + } + }; + const result = getChartSpecWithContext(context); + //console.log("basic spec", JSON.stringify(result.spec, null, 2)); + + expect(result.spec).toBeDefined(); + expect(result.spec.type).toBe('circlePacking'); + expect(result.spec.data.values).toEqual(dataItem); + expect(result.spec.color).toEqual(['#123456', '#abcdef']); + expect(result.spec.background).toEqual('#ffffff'); + expect(result.spec.title).toEqual([ + { text: 'CirclePacking Chart-1', orient: 'top' }, + { text: 'CirclePacking Chart-2', orient: 'left' } + ]); + expect(result.spec.label).toEqual([{ position: 'top', style: { fill: 'black' }, visible: true }]); + expect(result.spec.indicator).toEqual([ + { + title: { style: { text: 'Cluster A' } }, + content: [{ style: { text: 'Overlap A' } }] + } + ]); + expect(result.spec.dataZoom).toEqual([{ orient: 'left' }, { orient: 'bottom' }]); + expect(result.spec.valueField).toEqual('value'); + expect(result.spec.categoryField).toEqual('name'); + expect(result.spec.drill).toEqual(true); + expect(result.spec.layoutPadding).toEqual(5); + expect(result.spec.animationEnter).toEqual({ easing: 'cubicInOut' }); + expect(result.spec.animationExit).toEqual({ easing: 'cubicInOut' }); + expect(result.spec.animationUpdate).toEqual({ easing: 'cubicInOut' }); + expect(result.spec.theme).toEqual(builtinThemeMap[BuiltinThemeType.SemiThemeLight]); + }); + + it('should apply default colors if colors and palette are not given', () => { //console.log("data",data); const context = { chartTypeList: CHART_TYPE_LIST, - transpose: false, - command: 'Generate a BasicCircle Packing chart', cell: { x: 'name', size: 'value' }, dataTable: dataItem, chartType: ChartType.BubbleCirclePacking.toUpperCase() }; - const { chartType, spec } = getChartSpecWithContext(context); - //console.log("basic spec", JSON.stringify(spec, null, 2)); - expect(chartType).toBe(ChartType.BubbleCirclePacking); - expect(spec.type).toBe('circlePacking'); - expect(spec.data.values).toEqual(dataItem); + const result = getChartSpecWithContext(context); + + expect(result.spec.color).toBeDefined(); + expect(result.spec.color).toEqual(COLOR_THEMES.default); + }); + + it('should apply custom colors if colors are given', () => { + //console.log("data",data); + const context = { + chartTypeList: CHART_TYPE_LIST, + cell: { x: 'name', size: 'value' }, + dataTable: dataItem, + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'] + }; + const result = getChartSpecWithContext(context); + + expect(result.spec.color).toBeDefined(); + expect(result.spec.color).toEqual([ + '#f57c6e', + '#f2b56f', + '#f2a7da', + '#84c3b7', + '#88d8db', + '#71b7ed', + '#b8aeeb', + '#f2a7da', + '#fae69e' + ]); + }); + + it('should apply string theme and colors are undefined', () => { + const context = { + chartTypeList: CHART_TYPE_LIST, + cell: { x: 'name', size: 'value' }, + dataTable: dataItem, + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + chartTheme: 'semiThemeLight', + colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'] + }; + + const result = getChartSpecWithContext(context); + expect(result.spec.theme).toEqual(builtinThemeMap[BuiltinThemeType.SemiThemeLight]); + expect(result.spec.color).toBeUndefined(); + }); + + it('should apply custom object theme and colors are undefined', () => { + const context = { + chartTypeList: CHART_TYPE_LIST, + cell: { x: 'name', size: 'value' }, + dataTable: dataItem, + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + chartTheme: { colorScheme: ['#1a2b3c'] }, + colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'] + }; + + const result = getChartSpecWithContext(context); + expect(result.spec.theme).toEqual({ colorScheme: ['#1a2b3c'] }); + expect(result.spec.color).toBeUndefined(); }); it('should generate correct bubble circle packing spec', () => { @@ -118,4 +231,30 @@ describe('getChartSpecWithContext', () => { expect(spec.type).toBe('circlePacking'); expect(spec.data.values).toEqual(multi_root_data); }); + + it('should handle missing dataTable fields gracefully', () => { + const context = { + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + cell: { x: 'name', size: 'value' }, + dataTable: [{}] + }; + + const result = getChartSpecWithContext(context); + //console.log("basic spec", JSON.stringify(result.spec, null, 2)); + expect(result.spec).toBeDefined(); + expect(result.spec.type).toBe('circlePacking'); + }); + + it('should handle missing cell fields gracefully', () => { + const context = { + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + cell: { x: 'name', size: 'value' }, + dataTable: dataItem + }; + + const result = getChartSpecWithContext(context); + //console.log("basic spec", JSON.stringify(result.spec, null, 2)); + expect(result.spec).toBeDefined(); + expect(result.spec.type).toBe('circlePacking'); + }); }); From 6619f45137128564bfc7fdf70f197dff10189383 Mon Sep 17 00:00:00 2001 From: zjyhhhher <1059752643@qq.com> Date: Thu, 5 Jun 2025 22:37:33 +0800 Subject: [PATCH 3/3] test: add test case of getChartSpecWithContext when chartType is CirclePacking (add specific test for functions in circlePacking.ts) --- ...ChartSpecWithContext_circlePacking.test.ts | 104 +++++++----------- 1 file changed, 42 insertions(+), 62 deletions(-) diff --git a/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts b/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts index 02677771..deafe3fe 100644 --- a/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts +++ b/packages/vmind/__tests__/unit/getChartSpecWithContext_circlePacking.test.ts @@ -52,7 +52,7 @@ function generateDataStructure() { const createCountries = (names: string[]) => names.map(country => ({ name: country, - children: createRegions(6) // 每个国家6个地区 + children: createRegions(6) })); return [ @@ -63,19 +63,20 @@ function generateDataStructure() { ]; } const dataItem = generateDataStructure(); -//console.log("dataItem", JSON.stringify(dataItem, null, 2)); describe('getChartSpecWithContext', () => { + const baseContext = { + chartTypeList: CHART_TYPE_LIST, + cell: { x: 'category', size: 'value' }, + dataTable: dataItem, + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + totalTime: 5 + }; + it('should generate full spec for circlepacking chart with diverse inputs', () => { - //console.log("data",data); const context = { - chartTypeList: CHART_TYPE_LIST, - cell: { x: 'name', size: 'value' }, - dataTable: dataItem, - chartType: ChartType.BubbleCirclePacking.toUpperCase(), + ...baseContext, chartTheme: 'semiThemeLight', - colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'], - totalTime: 5, simpleVChartSpec: { type: 'circlePacking', title: [ @@ -95,7 +96,6 @@ describe('getChartSpecWithContext', () => { } }; const result = getChartSpecWithContext(context); - //console.log("basic spec", JSON.stringify(result.spec, null, 2)); expect(result.spec).toBeDefined(); expect(result.spec.type).toBe('circlePacking'); @@ -115,7 +115,7 @@ describe('getChartSpecWithContext', () => { ]); expect(result.spec.dataZoom).toEqual([{ orient: 'left' }, { orient: 'bottom' }]); expect(result.spec.valueField).toEqual('value'); - expect(result.spec.categoryField).toEqual('name'); + expect(result.spec.categoryField).toEqual('category'); expect(result.spec.drill).toEqual(true); expect(result.spec.layoutPadding).toEqual(5); expect(result.spec.animationEnter).toEqual({ easing: 'cubicInOut' }); @@ -125,13 +125,7 @@ describe('getChartSpecWithContext', () => { }); it('should apply default colors if colors and palette are not given', () => { - //console.log("data",data); - const context = { - chartTypeList: CHART_TYPE_LIST, - cell: { x: 'name', size: 'value' }, - dataTable: dataItem, - chartType: ChartType.BubbleCirclePacking.toUpperCase() - }; + const context = { ...baseContext }; const result = getChartSpecWithContext(context); expect(result.spec.color).toBeDefined(); @@ -139,12 +133,8 @@ describe('getChartSpecWithContext', () => { }); it('should apply custom colors if colors are given', () => { - //console.log("data",data); const context = { - chartTypeList: CHART_TYPE_LIST, - cell: { x: 'name', size: 'value' }, - dataTable: dataItem, - chartType: ChartType.BubbleCirclePacking.toUpperCase(), + ...baseContext, colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'] }; const result = getChartSpecWithContext(context); @@ -165,10 +155,7 @@ describe('getChartSpecWithContext', () => { it('should apply string theme and colors are undefined', () => { const context = { - chartTypeList: CHART_TYPE_LIST, - cell: { x: 'name', size: 'value' }, - dataTable: dataItem, - chartType: ChartType.BubbleCirclePacking.toUpperCase(), + ...baseContext, chartTheme: 'semiThemeLight', colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'] }; @@ -180,10 +167,7 @@ describe('getChartSpecWithContext', () => { it('should apply custom object theme and colors are undefined', () => { const context = { - chartTypeList: CHART_TYPE_LIST, - cell: { x: 'name', size: 'value' }, - dataTable: dataItem, - chartType: ChartType.BubbleCirclePacking.toUpperCase(), + ...baseContext, chartTheme: { colorScheme: ['#1a2b3c'] }, colors: ['#f57c6e', '#f2b56f', '#f2a7da', '#84c3b7', '#88d8db', '#71b7ed', '#b8aeeb', '#f2a7da', '#fae69e'] }; @@ -193,43 +177,41 @@ describe('getChartSpecWithContext', () => { expect(result.spec.color).toBeUndefined(); }); - it('should generate correct bubble circle packing spec', () => { - const buble_data = new Array(19).fill(0).map((_, i) => { - return { - name: `bubble-${i + 1}`, - value: i + 1 - }; - }); + // test for `bubbleCirclePackingData` + it('should preserve "value" field when size is "value"', () => { const context = { chartTypeList: CHART_TYPE_LIST, - transpose: false, - command: 'Generate a Bubble Circle Packing chart', - cell: { x: 'name', size: 'value' }, - dataTable: buble_data, - chartType: ChartType.BubbleCirclePacking.toUpperCase() + cell: { x: 'category', size: 'other' }, + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + totalTime: 5, + dataTable: [ + { category: 'A', value: 20, other: 5 }, + { category: 'B', value: 15, other: 3 } + ] }; - const { chartType, spec } = getChartSpecWithContext(context); - //console.log("bubble spec", JSON.stringify(spec, null, 2)); - expect(chartType).toBe(ChartType.BubbleCirclePacking); - expect(spec.type).toBe('circlePacking'); - expect(spec.data.values).toEqual(buble_data); + + const result = getChartSpecWithContext(context); + expect(result.spec.data.values).toMatchObject([ + { category: 'A', value: 5 }, + { category: 'B', value: 3 } + ]); }); - it('should generate correct nulti-root circle packing spec', () => { - const multi_root_data = dataItem[0].children; + // test for `bubbleCirclePackingField` + it('should generate correct categoryField', () => { const context = { chartTypeList: CHART_TYPE_LIST, - transpose: false, - command: 'Generate a Multi-root Circle Packing chart', - cell: { x: 'name', size: 'value' }, - dataTable: multi_root_data, - chartType: ChartType.BubbleCirclePacking.toUpperCase() + cell: { color: 'category', size: 'value', x: 'hahaha' }, + chartType: ChartType.BubbleCirclePacking.toUpperCase(), + totalTime: 5, + dataTable: [ + { category: 'A', value: 20 }, + { category: 'B', value: 15 } + ] }; - const { chartType, spec } = getChartSpecWithContext(context); - //console.log("multi-root spec", JSON.stringify(spec, null, 2)); - expect(chartType).toBe(ChartType.BubbleCirclePacking); - expect(spec.type).toBe('circlePacking'); - expect(spec.data.values).toEqual(multi_root_data); + + const result = getChartSpecWithContext(context); + expect(result.spec.categoryField).toEqual(context.cell.color || context.cell.x); }); it('should handle missing dataTable fields gracefully', () => { @@ -240,7 +222,6 @@ describe('getChartSpecWithContext', () => { }; const result = getChartSpecWithContext(context); - //console.log("basic spec", JSON.stringify(result.spec, null, 2)); expect(result.spec).toBeDefined(); expect(result.spec.type).toBe('circlePacking'); }); @@ -253,7 +234,6 @@ describe('getChartSpecWithContext', () => { }; const result = getChartSpecWithContext(context); - //console.log("basic spec", JSON.stringify(result.spec, null, 2)); expect(result.spec).toBeDefined(); expect(result.spec.type).toBe('circlePacking'); });