Add testcase filter using parameters - #1355
Conversation
Transcribing the two functions and running them: The Suggested fix: decide "is this filter active" from key presence, not from the value — Worth adding
This backslash escaping can't work — the search grammar has no escape support, so the text written back into the filter box becomes unparseable.
The Options: teach the grammar an escape rule, or when a name contains
The generated filter is broader than the permutation the user actually clicked, so the nav tree keeps siblings around. Two causes stack:
Navigation itself is accurate (it goes through
Running the sanitiser inside Putting the conversion in
Both are new exposure: with There's also no truncation. Suggestions: wrap the Minor, same function: a Also worth noting:
Once a testcase is selected this combobox can't be edited — the user has to clear the whole field to pick a different one.
Seeding
The spread is bounded by the call stack. Measured on node 24: So a multitest subtree with roughly 10^5+ testcases takes down the dropdown with Separately,
Styling convention: the rest of the web UI uses aphrodite ( (The hardcoded light-mode colours are fine — there's no theme/dark-mode support in this UI.) |
a92972b to
32353e0
Compare
yuxuan-ms
left a comment
There was a problem hiding this comment.
ExtendedSearchDropdown: three behaviour issues, plus some size
1. The generated filter still keeps sibling multitests whose names share a prefix
handleResultClick now emits {type: "test", search: [test]}, but name_filter (Report/reportFilter.js:11) matches by substring, not equality. Two multitests named MyTest and MyTest2 with the same suite and testcase names: clicking the permutation under MyTest leaves both in the nav tree. I transcribed the emitted filters through PropagateIndices + filterReport and got
report-1/mt1/ts1/test_order/test_order__0
report-1/mt2/ts1/test_order/test_order__0
where only the first was clicked. The testcase name is exact now (re:"^…$"), so this is only about the test/suite terms.
Emitting an anchored regexp for the test name as well should fix it — mt2's entries carry MyTest2|multitest in name_type_index, so ^MyTest$ won't match them, while mt1's subtree matches both anchored terms:
filters.push({ type: "regexp", search: `^${_.escapeRegExp(test)}$` });Worth a test with two multitests sharing a name prefix.
2. Typing the full testcase name clears the input box
handleTestcaseSearch sets setSearchText("") on an exact match (line 286) while the input renders value={searchText}. So when the user types the last character of test_order, the field goes blank and shows the placeholder, even though the selection took effect and the permutations render below. The next keystroke then starts from an empty string rather than extending what was typed.
Clicking an option goes through handleTestcaseSelect, which sets searchText to the name (line 311), so the two paths disagree. Setting searchText to value in the exact-match branch makes them consistent.
3. text: "" and interactive mode
Skipping the text terms when a name contains " is the right call, but it creates a state that didn't exist before: filters are applied while filter.text is empty. InteractiveReport.js:538 uses the truthiness of filteredReport.filter.text as its "is a filter active" signal, and when it's falsy shallowReportEntry doesn't attach the pruned subtree — so "run all" would run the unfiltered set while the tree on screen is filtered. This is from reading the code, I haven't exercised that path. Narrow trigger (a testcase name containing a double quote), but it's worth either widening that check to filter.filters?.length or keeping the text non-empty in some other form.
Size and structure
The component is 756 lines: 108 helpers, 60 useMemo, 147 handlers, 218 render, 113 styles. A few things account for most of it.
The selection state is stored twice. FilterBox holds extendedSearchState with exactly the four fields the dropdown also keeps in useState, so every handler writes them twice — once through the setter, once through persistState with a hand-written patch object (6 call sites: 260, 275, 289, 298, 314, 331). That duplication has already produced two artefacts:
searchTextisn't in the persisted set, so it drifts out of sync withselectedTestcase(issue 2 above is part of this)- there are two reset mechanisms for the same event:
key={this.props.report?.uid}on the dropdown already remounts it with fresh state, andcomponentDidUpdate→resetExtendedSearchState()clears FilterBox's shadow copy
Making the dropdown controlled — value and onChange both from FilterBox — removes the four useState, the persistState wrapper, the six patch objects and one of the two reset paths.
8 useMemo, where the rest of src/ has 0. Only allTestcases walks the whole report tree; uniqueTestNames, uniqueTestsuiteNames, uniqueTestcaseNames, filteredTestcaseNames, filterOptions and filteredPermutations are uniq/filter/sortBy over a few dozen entries. Dropping those six also removes the eslint-disable-next-line react-hooks/exhaustive-deps.
Single-use helpers exported for tests. enrichWithParams (3 lines), getPermutations and compareParamValues are each used once. Inlining them and testing the behaviour through the component ("selecting quantity=1000 leaves one permutation") would cut a few hundred lines across the component and its test file. collectTestcases, getUniqueField, buildFilterOptions and applyParamFilters have real logic and are worth keeping exported.
Test hooks in the markup. data-testid appears 8 times here and 0 times anywhere else in src/; role= 6 times vs 0; aria-* 6 times vs 3 (in two files). The existing component tests select with .find("select"), .find(Component) and snapshots — worth following that rather than introducing a second convention.
Dead branch. In handleResultClick, !onNavigate can't be true: it's PropTypes.func.isRequired and FilterBox:185 only renders the dropdown when onExtendedSearchNavigate is set. !reportUid is a real guard (report can be null), keep that half.
compareParamValues isn't a total order. Once a parameter mixes types, 1000 and "1000" compare equal, "1000" < 2.5, but 1000 > 2.5, so the result depends on input order. [1000, "1000", true, null, "", 2.5, 500] currently renders as "", 2.5, 1000, 1000, 500, Buy, false, null, true. If parameter values end up as scalars only, one line covers it and sorts numbers correctly:
const compareParamValues = (left, right) =>
String(left).localeCompare(String(right), undefined, { numeric: true });32353e0 to
be1cc8b
Compare
a24410e to
82b2e74
Compare
91c1999 to
fff6843
Compare
Bug / Requirement Description
Clearly and concisely describe the problem.
Solution description
Describe your code changes in detail for reviewers.
Checklist: