From e32cec43821deed3b82bccd7db2a0a38573f08f5 Mon Sep 17 00:00:00 2001 From: Hanabi <317387557+Hanabi9248@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:56:30 +0800 Subject: [PATCH 1/2] fix(format): preserve literal replacement characters in values Signed-off-by: Hanabi <317387557+Hanabi9248@users.noreply.github.com> --- src/util/format.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/format.ts b/src/util/format.ts index 6378072327..ba4bc6d594 100644 --- a/src/util/format.ts +++ b/src/util/format.ts @@ -144,7 +144,7 @@ export function formatTpl( const val = paramsList[seriesIdx][$vars[k]]; tpl = tpl.replace( wrapVar(TPL_VAR_ALIAS[k], seriesIdx), - encode ? encodeHTML(val) : val + () => (encode ? encodeHTML(val) : val) ); } } @@ -159,7 +159,7 @@ export function formatTplSimple(tpl: string, param: Dictionary, encode?: bo zrUtil.each(param, function (value, key) { tpl = tpl.replace( '{' + key + '}', - encode ? encodeHTML(value) : value + () => (encode ? encodeHTML(value) : value) ); }); return tpl; From 5e78e202fe75a7f6cfefe01f308a410da6de38b8 Mon Sep 17 00:00:00 2001 From: Hanabi <317387557+Hanabi9248@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:56:56 +0800 Subject: [PATCH 2/2] test(format): cover literal template replacement values Signed-off-by: Hanabi <317387557+Hanabi9248@users.noreply.github.com> --- test/ut/spec/util/format.test.ts | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 test/ut/spec/util/format.test.ts diff --git a/test/ut/spec/util/format.test.ts b/test/ut/spec/util/format.test.ts new file mode 100644 index 0000000000..4fc1374305 --- /dev/null +++ b/test/ut/spec/util/format.test.ts @@ -0,0 +1,53 @@ +/* +* 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 { formatTpl, formatTplSimple, encodeHTML } from '@/src/util/format'; + +describe('template values', function () { + const values = ['Price $&', 'Price $$', 'Price $\'', 'Price $`', '$&']; + + it('preserves replacement characters in series values', function () { + for (const value of values) { + for (const encode of [false, true]) { + const text = encode ? encodeHTML(value) : value; + expect(formatTpl('before {a} after', { + $vars: ['seriesName'], seriesName: value + }, encode)).toBe('before ' + text + ' after'); + } + } + }); + + it('preserves replacement characters in simple values', function () { + for (const value of values) { + for (const encode of [false, true]) { + const text = encode ? encodeHTML(value) : value; + expect(formatTplSimple('before {name} after', {name: value}, encode)) + .toBe('before ' + text + ' after'); + } + } + }); + + it('keeps indexed series and numeric values', function () { + expect(formatTpl('{a0}: {c0}; {a1}: {c1}', [ + {$vars: ['seriesName', 'name', 'value'], seriesName: '$$', value: 12}, + {$vars: ['seriesName', 'name', 'value'], seriesName: '$&', value: 34} + ])).toBe('$$: 12; $&: 34'); + expect(formatTplSimple('{value}', {value: 12})).toBe('12'); + }); +});