This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new ca6316c9d4 fix(frontend): use locale-independent lowercasing in isSink
(#8648)
ca6316c9d4 is described below
commit ca6316c9d4bd281389e3e7b0e85ad70da12245f1
Author: Luis Fernando Caro Reyna
<[email protected]>
AuthorDate: Thu Sep 24 18:32:02 2026 +0000
fix(frontend): use locale-independent lowercasing in isSink (#8648)
### What changes were proposed in this PR?
`isSink` folded operator types with `toLocaleLowerCase()`, which is
locale-dependent. In a Turkish-locale browser an uppercase `I` folds to
`ı`,
so a type spelled `SINK` becomes `sınk` and stops matching: the operator
drops out of the result panel and becomes eligible for the result-view
and
cache toggles it is meant to be excluded from. An operator type is a
machine
identifier, so case folding must not depend on the browser's locale.
Before: `isSink("SINK")` can return false under a Turkish runtime locale
After: `isSink("SINK")` is always true
Adds direct unit coverage for `isSink`: the casing contract, negative
cases, and a regression test that stubs `toLocaleLowerCase` with Turkish
folding — the one case `#8603` cannot pin, since it fails on the old
implementation.
### Any related issues, documentation, discussions?
Closes #8616
Sequenced on #8603 (test-only coverage for the same helpers, same spec
file).
Whichever of the two merges first should rebase the other.
### How was this PR tested?
- Targeted spec: `yarn ng test --watch=false --include
"src/app/workspace/service/workflow-graph/model/workflow-graph.spec.ts"`
— 72 passed (regression test proved red before the fix)
- Full frontend suite: `yarn test:ci` — 229 files, 6244 passed, 1
skipped (pre-existing)
- `eslint` and `prettier --check` clean on changed files
### Was this PR authored or co-authored using generative AI tooling?
Yes. Co-authored with Claude Code; reviewed before submission.
---
.../workflow-graph/model/workflow-graph.spec.ts | 48 +++++++++++++++++++++-
.../service/workflow-graph/model/workflow-graph.ts | 2 +-
2 files changed, 47 insertions(+), 3 deletions(-)
diff --git
a/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.spec.ts
b/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.spec.ts
index 0b1a8a830e..48007ae949 100644
---
a/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.spec.ts
+++
b/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.spec.ts
@@ -27,9 +27,15 @@ import {
mockSentimentPredicate,
mockSentimentResultLink,
} from "./mock-workflow-data";
-import { WorkflowGraph } from "./workflow-graph";
+import { WorkflowGraph, isSink } from "./workflow-graph";
import { Observable } from "rxjs";
-import { Comment, OperatorLink, PortDescription, PortProperty } from
"../../../types/workflow-common.interface";
+import {
+ Comment,
+ OperatorLink,
+ OperatorPredicate,
+ PortDescription,
+ PortProperty,
+} from "../../../types/workflow-common.interface";
describe("WorkflowGraph", () => {
let workflowGraph: WorkflowGraph;
@@ -824,4 +830,42 @@ describe("WorkflowGraph", () => {
sub.unsubscribe();
});
});
+
+ describe("isSink", () => {
+ const buildPredicate = (operatorType: string): OperatorPredicate => ({
+ operatorID: "testOperator",
+ operatorType,
+ operatorVersion: "v1",
+ operatorProperties: {},
+ inputPorts: [{ portID: "input-0" }],
+ outputPorts: [],
+ showAdvanced: true,
+ isDisabled: false,
+ });
+
+ it("should match an operator type that contains sink in any casing", () =>
{
+ expect(isSink(buildPredicate("sink"))).toBe(true);
+ expect(isSink(buildPredicate("SINK"))).toBe(true);
+ expect(isSink(buildPredicate("SimpleSink"))).toBe(true);
+ expect(isSink(buildPredicate("dvdpowSinkExec"))).toBe(true);
+ });
+
+ it("should not match an operator type without sink", () => {
+ expect(isSink(buildPredicate("NlpSentiment"))).toBe(false);
+ expect(isSink(buildPredicate("scanSource"))).toBe(false);
+ });
+
+ it("should match a sink type even when the runtime locale folds uppercase
I to dotless ı", () => {
+ const turkishFolding = vi.spyOn(String.prototype,
"toLocaleLowerCase").mockImplementation(function (
+ this: string
+ ) {
+ return this.replace(/I/g, "ı").toLowerCase();
+ });
+ try {
+ expect(isSink(buildPredicate("SINK"))).toBe(true);
+ } finally {
+ turkishFolding.mockRestore();
+ }
+ });
+ });
});
diff --git
a/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.ts
b/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.ts
index 16366c8969..a0e5deeb87 100644
--- a/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.ts
+++ b/frontend/src/app/workspace/service/workflow-graph/model/workflow-graph.ts
@@ -76,7 +76,7 @@ export const VIEW_RESULT_OP_TYPE = "SimpleSink";
export const VIEW_RESULT_OP_NAME = "View Results";
export function isSink(operator: OperatorPredicate): boolean {
- return operator.operatorType.toLocaleLowerCase().includes("sink");
+ return operator.operatorType.toLowerCase().includes("sink");
}
export function isPythonUdf(operator: OperatorPredicate): boolean {