Merge pull request #5401 from n-lark/5348/preserve-search-dialog-casing

Editor: Flow & subflow names are changed to all lowercase in search dialog #5348
pull/5427/head
Nick O'Leary 2026-01-07 15:15:10 +00:00 committed by GitHub
commit 633f90e38b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 180 additions and 4 deletions

View File

@ -49,9 +49,13 @@ RED.search = (function() {
function indexNode(n) {
var l = RED.utils.getNodeLabel(n);
if (l) {
l = (""+l).toLowerCase();
index[l] = index[l] || {};
index[l][n.id] = {node:n,label:l}
const originalLabel = "" + l;
const indexLabel = originalLabel.toLowerCase();
index[indexLabel] = index[indexLabel] || {};
index[indexLabel][n.id] = {
node: n,
label: originalLabel
};
}
l = l||n.label||n.name||n.id||"";
@ -683,7 +687,17 @@ RED.search = (function() {
show: show,
hide: hide,
search: search,
getSearchOptions: getSearchOptions
getSearchOptions: getSearchOptions,
// Expose internals for testing
_indexNode: indexNode,
get _index() { return index; },
set _index(val) { index = val; }
};
})();
// Allow CommonJS import for testing
if (typeof module !== "undefined" && module.exports) {
module.exports = RED.search;
}

View File

@ -0,0 +1,162 @@
const should = require("should");
const sinon = require("sinon");
const NR_TEST_UTILS = require("nr-test-utils");
// Path to the search.js module
const searchModulePath = NR_TEST_UTILS.resolve("@node-red/editor-client/src/js/ui/search.js");
describe("editor-client/ui/search", function() {
let search;
let mockRED;
beforeEach(function() {
// Set up minimal RED global mock - only what's needed for tests
mockRED = {
utils: {
getNodeLabel: sinon.stub()
}
};
global.RED = mockRED;
// Clear require cache to get fresh module instance
delete require.cache[searchModulePath];
search = require(searchModulePath);
// Reset the index for each test
search._index = {};
});
afterEach(function() {
sinon.restore();
delete global.RED;
delete require.cache[searchModulePath];
});
describe("indexNode", function() {
it("preserves original label casing in search results", function() {
const node = {
id: "node1",
type: "tab",
_def: { category: "config" }
};
mockRED.utils.getNodeLabel.returns("MyFlow Name");
search._indexNode(node);
// Verify the index key is lowercase (for case-insensitive searching)
should.exist(search._index["myflow name"]);
// Verify the stored label preserves original casing
search._index["myflow name"]["node1"].label.should.equal("MyFlow Name");
});
it("indexes node with mixed case label correctly", function() {
const node = {
id: "node2",
type: "subflow",
_def: { category: "subflows" }
};
mockRED.utils.getNodeLabel.returns("MySubFlow_Test");
search._indexNode(node);
// Index key should be lowercase
should.exist(search._index["mysubflow_test"]);
// Label should preserve original casing
search._index["mysubflow_test"]["node2"].label.should.equal("MySubFlow_Test");
});
it("handles uppercase labels", function() {
const node = {
id: "node3",
type: "tab",
_def: { category: "config" }
};
mockRED.utils.getNodeLabel.returns("UPPERCASE FLOW");
search._indexNode(node);
should.exist(search._index["uppercase flow"]);
search._index["uppercase flow"]["node3"].label.should.equal("UPPERCASE FLOW");
});
it("handles lowercase labels", function() {
const node = {
id: "node4",
type: "tab",
_def: { category: "config" }
};
mockRED.utils.getNodeLabel.returns("lowercase flow");
search._indexNode(node);
should.exist(search._index["lowercase flow"]);
search._index["lowercase flow"]["node4"].label.should.equal("lowercase flow");
});
it("stores node reference correctly", function() {
const node = {
id: "node5",
type: "tab",
_def: { category: "config" }
};
mockRED.utils.getNodeLabel.returns("Test Flow");
search._indexNode(node);
search._index["test flow"]["node5"].node.should.equal(node);
});
it("handles nodes without labels by falling back to id", function() {
const node = {
id: "node6",
type: "tab",
_def: { category: "config" }
};
mockRED.utils.getNodeLabel.returns(null);
search._indexNode(node);
// When there's no label from getNodeLabel,
// the node is still indexed by its id
should.exist(search._index["node6"]);
search._index["node6"]["node6"].label.should.equal("node6");
});
});
describe("search", function() {
it("finds nodes with case-insensitive search", function() {
// Manually set up index with mixed case labels
search._index = {
"myflow": {
"node1": {
node: { id: "node1", type: "tab", _def: { category: "config" } },
label: "MyFlow"
}
}
};
// Search with lowercase should find the node
const results = search.search("myflow");
results.length.should.equal(1);
results[0].label.should.equal("MyFlow");
});
it("returns preserved casing in search results", function() {
search._index = {
"test subflow": {
"node1": {
node: { id: "node1", type: "subflow", _def: { category: "subflows" } },
label: "Test SubFlow"
}
}
};
const results = search.search("test");
results.length.should.equal(1);
results[0].label.should.equal("Test SubFlow");
});
});
});