mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-06 00:09:22 +00:00
fix: Issue 1538 autorun loading (#11428)
This commit is contained in:
@@ -311,34 +311,47 @@ describe("FieldValuesPanel.vue", () => {
|
||||
expect((wrapper.vm as any).selectedValues).toEqual([]);
|
||||
});
|
||||
|
||||
it("emits 'add-multiple-search-terms' with include when Include mode button clicked", async () => {
|
||||
wrapper = createWrapper({ showMultiSelect: true, fieldValues: buildFieldValues(3) });
|
||||
// Set filterMode to 'exclude' while selectedValues is empty so no premature emit
|
||||
(wrapper.vm as any).filterMode = "exclude";
|
||||
await nextTick();
|
||||
(wrapper.vm as any).selectedValues = ["value-1", "value-2"];
|
||||
await nextTick();
|
||||
const includeBtn = wrapper.find(
|
||||
'[data-test="field-values-panel-include-mode-btn"]'
|
||||
);
|
||||
await includeBtn.trigger("click");
|
||||
const emitted = wrapper.emitted("add-multiple-search-terms");
|
||||
expect(emitted).toBeTruthy();
|
||||
expect(emitted![0]).toEqual(["status", ["value-1", "value-2"], "include"]);
|
||||
});
|
||||
// Mode-switch emissions are debounced (300 ms) so fake timers are required
|
||||
// to flush the debounce before asserting the emitted event.
|
||||
describe("mode switch emits (debounced)", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("emits 'add-multiple-search-terms' with exclude when Exclude mode button clicked", async () => {
|
||||
wrapper = createWrapper({ showMultiSelect: true, fieldValues: buildFieldValues(3) });
|
||||
// filterMode defaults to 'include'; set selectedValues then click exclude
|
||||
(wrapper.vm as any).selectedValues = ["value-1"];
|
||||
await nextTick();
|
||||
const excludeBtn = wrapper.find(
|
||||
'[data-test="field-values-panel-exclude-mode-btn"]'
|
||||
);
|
||||
await excludeBtn.trigger("click");
|
||||
const emitted = wrapper.emitted("add-multiple-search-terms");
|
||||
expect(emitted).toBeTruthy();
|
||||
expect(emitted![0]).toEqual(["status", ["value-1"], "exclude"]);
|
||||
it("emits 'add-multiple-search-terms' with include when Include mode button clicked", async () => {
|
||||
wrapper = createWrapper({ showMultiSelect: true, fieldValues: buildFieldValues(3) });
|
||||
// Set filterMode to 'exclude' while selectedValues is empty so no premature emit
|
||||
(wrapper.vm as any).filterMode = "exclude";
|
||||
await nextTick();
|
||||
(wrapper.vm as any).selectedValues = ["value-1", "value-2"];
|
||||
await nextTick();
|
||||
const includeBtn = wrapper.find(
|
||||
'[data-test="field-values-panel-include-mode-btn"]'
|
||||
);
|
||||
await includeBtn.trigger("click");
|
||||
vi.runAllTimers();
|
||||
const emitted = wrapper.emitted("add-multiple-search-terms");
|
||||
expect(emitted).toBeTruthy();
|
||||
expect(emitted![0]).toEqual(["status", ["value-1", "value-2"], "include"]);
|
||||
});
|
||||
|
||||
it("emits 'add-multiple-search-terms' with exclude when Exclude mode button clicked", async () => {
|
||||
wrapper = createWrapper({ showMultiSelect: true, fieldValues: buildFieldValues(3) });
|
||||
// filterMode defaults to 'include'; set selectedValues then click exclude
|
||||
(wrapper.vm as any).selectedValues = ["value-1"];
|
||||
await nextTick();
|
||||
const excludeBtn = wrapper.find(
|
||||
'[data-test="field-values-panel-exclude-mode-btn"]'
|
||||
);
|
||||
await excludeBtn.trigger("click");
|
||||
vi.runAllTimers();
|
||||
const emitted = wrapper.emitted("add-multiple-search-terms");
|
||||
expect(emitted).toBeTruthy();
|
||||
expect(emitted![0]).toEqual(["status", ["value-1"], "exclude"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("does NOT clear selectedValues after applying multi-select include", async () => {
|
||||
|
||||
@@ -207,7 +207,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, ref, watch } from "vue";
|
||||
import { watchDebounced } from "@vueuse/core";
|
||||
import { useDebounceFn, watchDebounced } from "@vueuse/core";
|
||||
import EqualIcon from "@/components/icons/EqualIcon.vue";
|
||||
import NotEqualIcon from "@/components/icons/NotEqualIcon.vue";
|
||||
import { formatLargeNumber } from "@/utils/zincutils";
|
||||
@@ -323,18 +323,29 @@ watchDebounced(
|
||||
{ debounce: 300 },
|
||||
);
|
||||
|
||||
/**
|
||||
* Debounced emission for mode changes. The UI toggle updates immediately
|
||||
* (instant feedback) but the API call is consolidated so that rapid
|
||||
* include→exclude→include clicks only fire one search at the end, preventing
|
||||
* a brief "no events found" flash (from an intermediate result set) that
|
||||
* would reset the field-list scroll position.
|
||||
*/
|
||||
const debouncedEmitModeChange = useDebounceFn((mode: "include" | "exclude") => {
|
||||
if (selectedValues.value.length > 0) {
|
||||
emit("add-multiple-search-terms", props.fieldName, [...selectedValues.value], mode);
|
||||
}
|
||||
}, 300);
|
||||
|
||||
/**
|
||||
* Called when the user explicitly clicks the include/exclude toggle.
|
||||
* Sets filterMode and immediately re-applies the current selection under
|
||||
* the new mode so the query updates in real time.
|
||||
* Updates filterMode immediately for instant visual feedback, then
|
||||
* debounces the actual query emission to avoid rapid successive API calls.
|
||||
* This is the ONLY path that should emit add-multiple-search-terms for
|
||||
* a mode change — the prop-sync watcher below only updates the ref silently.
|
||||
*/
|
||||
const setFilterMode = (mode: "include" | "exclude") => {
|
||||
filterMode.value = mode;
|
||||
if (selectedValues.value.length > 0) {
|
||||
emit("add-multiple-search-terms", props.fieldName, [...selectedValues.value], mode);
|
||||
}
|
||||
debouncedEmitModeChange(mode);
|
||||
};
|
||||
|
||||
// Keep filterMode in sync with the external filter state.
|
||||
|
||||
@@ -220,7 +220,8 @@ const useLogs = () => {
|
||||
if (
|
||||
searchObj.loading == false &&
|
||||
searchObj.loadingHistogram == false &&
|
||||
searchObj.meta.logsVisualizeToggle == "logs"
|
||||
searchObj.meta.logsVisualizeToggle == "logs" &&
|
||||
searchObj.data.stream.selectedStream.length > 0
|
||||
) {
|
||||
searchObj.loading = true;
|
||||
await getQueryData(false);
|
||||
|
||||
@@ -326,6 +326,7 @@ export const useSearchBar = () => {
|
||||
//if empty then we are displaying no events found... message on the UI instead of throwing in an error format
|
||||
if (!allStreamFields.length) {
|
||||
// searchObj.data.errorMsg = t("search.noFieldFound");
|
||||
searchObj.loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -639,6 +639,8 @@ describe("Logs Index", async () => {
|
||||
});
|
||||
|
||||
it("Should execute runQueryFn and show job scheduler", async () => {
|
||||
// runQueryFn guards against empty selectedStream, so a stream must be present.
|
||||
wrapper.vm.searchObj.data.stream.selectedStream = ["stream1"];
|
||||
// do not rely on spying internal closures; assert state change
|
||||
await wrapper.vm.runQueryFn();
|
||||
expect(wrapper.vm.showJobScheduler).toBe(true);
|
||||
|
||||
@@ -980,6 +980,7 @@ export default defineComponent({
|
||||
const runQueryFn = async () => {
|
||||
// searchObj.data.resultGrid.currentPage = 0;
|
||||
// searchObj.runQuery = false;
|
||||
if (!searchObj.data.stream.selectedStream.length) return;
|
||||
try {
|
||||
searchObj.loading = true;
|
||||
searchObj.meta.refreshHistogram = true;
|
||||
@@ -2252,6 +2253,7 @@ export default defineComponent({
|
||||
store.state.zoConfig?.auto_query_enabled &&
|
||||
store.state.zoConfig?.query_on_stream_selection !== false &&
|
||||
searchObj.meta.logsVisualizeToggle === "logs" &&
|
||||
searchObj.data.stream.selectedStream.length > 0 &&
|
||||
!searchObj.loading &&
|
||||
!searchObj.loadingHistogram
|
||||
) {
|
||||
|
||||
@@ -63,8 +63,9 @@ export const removeFieldCondition = (
|
||||
fieldName: string,
|
||||
): string => {
|
||||
const esc = fieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const fieldPattern = new RegExp(`^"?${esc}"?\\s*[=!<>]`, "i");
|
||||
const multiPattern = new RegExp(`^\\(\\s*"?${esc}"?\\s*[=!<>]`, "i");
|
||||
// Match both comparison operators (=, !=, <, >) and IS / IS NOT (for null checks)
|
||||
const fieldPattern = new RegExp(`^"?${esc}"?\\s*(?:[=!<>]|is\\s)`, "i");
|
||||
const multiPattern = new RegExp(`^\\(\\s*"?${esc}"?\\s*(?:[=!<>]|is\\s)`, "i");
|
||||
|
||||
const remaining = queryStr
|
||||
.trim()
|
||||
|
||||
Reference in New Issue
Block a user