From fa34ed527fa81b565a0ce47527acff881d141460 Mon Sep 17 00:00:00 2001 From: tiwarir Date: Thu, 2 Jul 2026 11:27:35 -0400 Subject: [PATCH] fix(core): reset process flag on option error. close #15037 --- src/core/echarts.ts | 10 +- test/ut/spec/api/setOption.test.ts | 143 +++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 test/ut/spec/api/setOption.test.ts diff --git a/src/core/echarts.ts b/src/core/echarts.ts index 8214c11e0e..e1b370118b 100644 --- a/src/core/echarts.ts +++ b/src/core/echarts.ts @@ -774,7 +774,15 @@ class ECharts extends Eventful { ecModel.init(null, null, null, theme, this._locale, optionManager); } - this._model.setOption(option as ECBasicOption, { replaceMerge }, optionPreprocessorFuncs); + try { + this._model.setOption(option as ECBasicOption, { replaceMerge }, optionPreprocessorFuncs); + } + catch (e) { + this[PENDING_UPDATE] = null; + this[IN_EC_CYCLE_KEY] = false; + + throw e; + } const updateParams = { seriesTransition: transitionOpt, diff --git a/test/ut/spec/api/setOption.test.ts b/test/ut/spec/api/setOption.test.ts new file mode 100644 index 0000000000..0c5743469a --- /dev/null +++ b/test/ut/spec/api/setOption.test.ts @@ -0,0 +1,143 @@ +/* +* 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 { EChartsType } from '../../../../src/echarts'; +import { createChart, removeChart } from '../../core/utHelper'; + +const QUOTE = String.fromCharCode(96); +const MAIN_PROCESS_ERROR = '[ECharts] ' + QUOTE + 'setOption' + QUOTE + + ' should not be called during main process.'; + +describe('api/setOption', function () { + let chart: EChartsType; + + beforeEach(function () { + chart = createChart(); + }); + + afterEach(function () { + removeChart(chart); + }); + + it('recovers from an invalid calendar option error', function () { + const oldConsoleErr = console.error; + console.error = jest.fn(); + + try { + expect(function () { + chart.setOption({ + calendar: { + range: '999' + }, + visualMap: { + show: false, + min: 0, + max: 10 + }, + series: { + type: 'heatmap', + coordinateSystem: 'calendar', + data: [] + } + }); + }).toThrow(); + } + finally { + console.error = oldConsoleErr; + } + + const consoleErr = jest.fn(); + console.error = consoleErr; + + try { + expect(function () { + chart.setOption({ + calendar: { + range: '2020' + }, + visualMap: { + show: false, + min: 0, + max: 10 + }, + series: { + type: 'heatmap', + coordinateSystem: 'calendar', + data: [] + } + }); + }).not.toThrow(); + } + finally { + console.error = oldConsoleErr; + } + + expect(consoleErr).not.toHaveBeenCalledWith(MAIN_PROCESS_ERROR); + expect((chart.getOption().calendar as any[])[0].range).toEqual('2020'); + }); + + it('resets the main process state when option merge throws', function () { + expect(function () { + chart.setOption({ + xAxis: { + data: ['a'] + }, + yAxis: {}, + series: [ + { + id: 'duplicate', + type: 'line', + data: [1] + }, + { + id: 'duplicate', + type: 'line', + data: [2] + } + ] + }); + }).toThrowError(/duplicate/); + + const oldConsoleErr = console.error; + const consoleErr = jest.fn(); + console.error = consoleErr; + + try { + chart.setOption({ + xAxis: { + data: ['a'] + }, + yAxis: {}, + series: [ + { + id: 'valid', + type: 'line', + data: [1] + } + ] + }); + } + finally { + console.error = oldConsoleErr; + } + + expect(consoleErr).not.toHaveBeenCalledWith(MAIN_PROCESS_ERROR); + expect((chart.getOption().series as any[])[0].id).toEqual('valid'); + }); +});