From 031ae443f1a42540194149e3da1c71c477aa0791 Mon Sep 17 00:00:00 2001 From: kimyenac Date: Sat, 26 Sep 2026 18:40:23 +0900 Subject: [PATCH] [ZEPPELIN-6581] Match interpreter search queries literally instead of as regex The New UI interpreter search compiled the raw query with `new RegExp(value, 'i')`. Incomplete metacharacters such as `(` or `[` threw an uncaught SyntaxError and left the list showing the previous results, and `.` matched every interpreter. Filter with a case-insensitive literal substring match instead, keeping the case-insensitive behavior from ZEPPELIN-6147. --- .../interpreter/interpreter.component.spec.ts | 75 +++++++++++++++++++ .../interpreter/interpreter.component.ts | 5 +- 2 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.spec.ts diff --git a/zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.spec.ts b/zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.spec.ts new file mode 100644 index 00000000000..d261b00d82e --- /dev/null +++ b/zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.spec.ts @@ -0,0 +1,75 @@ +/* + * Licensed 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 { ChangeDetectorRef } from '@angular/core'; +import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzModalService } from 'ng-zorro-antd/modal'; +import { describe, expect, it, vi } from 'vitest'; + +import { Interpreter } from '@zeppelin/interfaces'; +import { InterpreterService } from '@zeppelin/services'; + +import { InterpreterComponent } from './interpreter.component'; + +// InterpreterSettingManager rejects new setting names outside ^[-_a-zA-Z0-9]+$, and every bundled +// interpreter group fits it, so the fixtures stay within that set. +const NAMES = ['spark', 'spark-sql', 'python', 'jdbc', 'md']; + +const createComponent = () => { + const cdr = { markForCheck: vi.fn() } as unknown as ChangeDetectorRef; + const component = new InterpreterComponent( + {} as InterpreterService, + cdr, + {} as NzModalService, + {} as NzMessageService + ); + component.interpreterSettings = NAMES.map(name => ({ name }) as Interpreter); + component.filteredInterpreterSettings = component.interpreterSettings; + return component; +}; + +const filteredNames = (component: InterpreterComponent) => component.filteredInterpreterSettings.map(e => e.name); + +describe('InterpreterComponent.filterInterpreters', () => { + it.each(['(', '[', '*', '.', '\\'])('matches the metacharacter %s literally without throwing', query => { + const component = createComponent(); + + expect(() => component.filterInterpreters(query)).not.toThrow(); + expect(filteredNames(component)).toEqual([]); + }); + + it('does not treat the query as a pattern', () => { + const component = createComponent(); + + // As a pattern, `.` matches any character and `sp.rk` matches `spark`. + component.filterInterpreters('sp.rk'); + + expect(filteredNames(component)).toEqual([]); + }); + + it('matches names case-insensitively', () => { + const component = createComponent(); + + component.filterInterpreters('SPARK'); + + expect(filteredNames(component)).toEqual(['spark', 'spark-sql']); + }); + + it('restores the full list when the query is cleared', () => { + const component = createComponent(); + component.filterInterpreters('jdbc'); + + component.filterInterpreters(''); + + expect(filteredNames(component)).toEqual(NAMES); + }); +}); diff --git a/zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.ts b/zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.ts index 0c31e2a752c..d04ce62c974 100644 --- a/zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.ts +++ b/zeppelin-web-angular/src/app/pages/workspace/interpreter/interpreter.component.ts @@ -55,9 +55,8 @@ export class InterpreterComponent implements OnInit, OnDestroy { } filterInterpreters(value: string) { - this.filteredInterpreterSettings = this.interpreterSettings.filter( - e => e.name.search(new RegExp(value, 'i')) !== -1 - ); + const query = value.toLowerCase(); + this.filteredInterpreterSettings = this.interpreterSettings.filter(e => e.name.toLowerCase().includes(query)); this.cdr.markForCheck(); }