feat: vrl support for visualization (#10237)

### **User description**
Design at :
https://github.com/openobserve/designs/tree/main/visualization-vrl-support
### **PR Type**
Enhancement


___

### **Description**
- Always default to table chart with VRL functions

- Preserve and propagate VRL function query across UI

- Enable dynamic columns for VRL-based table charts

- Remove visualization-mode restrictions for function editor


___

### Diagram Walkthrough


```mermaid
flowchart LR
  VRL["VRL Function Present?"]
  VRL -- "yes" --> Table["Force Table Chart"]
  Table -- "on table" --> DynCols["Enable Dynamic Columns"]
  VRL -- "no" --> OtherCharts["Allow Other Chart Types"]
```



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>useDashboardPanel.ts</strong><dd><code>Force table
chart for VRL functions</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

web/src/composables/useDashboardPanel.ts

<ul><li>Added VRL check in determineChartType<br> <li> Default to table
when VRL functions exist</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-fbba1700750053b9eaf38ddd30dab84531bc44131c795ebb713811e4930cd7cb">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>Index.vue</strong><dd><code>Propagate and handle VRL
function queries</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

web/src/plugins/logs/Index.vue

<ul><li>Copy and clear <code>vrlFunctionQuery</code> on query update<br>
<li> Force table type and set <code>table_dynamic_columns</code><br>
<li> Preserve <code>vrlFunctionQuery</code> in visualization flow</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0323c3da69b369843c5d72bf4df5b00ca6b5147192a213baf331659ecf92af62">+70/-2</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>SearchBar.vue</strong><dd><code>Enable VRL editor in
visualize mode</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

web/src/plugins/logs/SearchBar.vue

<ul><li>Always allow editing VRL function editor<br> <li> Removed
visualize-mode warning overlay</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-81893de4edf186b0556f610acb280c87baa2ffe67d3b48a60a866094c7635baf">+1/-26</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>TransformSelector.vue</strong><dd><code>Lift transform
restrictions in visualize mode</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

web/src/plugins/logs/TransformSelector.vue

<ul><li>Removed disable state for transform toggle/save in visualize<br>
<li> Removed visualize-mode tooltip restrictions</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0037d8dd9cfbacc26309b26e98ae953e7ccbd0891ac2fd01c6314ebd7eed9d2c">+3/-13</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>VisualizeLogsQuery.vue</strong><dd><code>Restrict chart
types when VRL functions present</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

web/src/plugins/logs/VisualizeLogsQuery.vue

<ul><li>Block non-table chart selection if VRL functions exist<br> <li>
Show error and reset to table on invalid type switch</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-979b01d943d58c5d70faeeac5948b7f02f0b2392a12b84ef07ba7f7393ab0261">+14/-0</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

</details>

___


___

### **PR Type**
Enhancement, Tests


___

### **Description**
- Default to table chart when VRL functions exist

- Propagate and encode VRL function query in UI

- Enable dynamic columns for VRL-based table charts

- Remove visualize-mode restrictions on VRL editor


___

### Diagram Walkthrough


```mermaid
flowchart LR
  VRL["VRL functions present?"]
  VRL -- "yes" --> Table["Force table chart"]
  Table --> DynCols["Enable dynamic columns"]
  DynCols --> Prop["Propagate VRL query"]
  VRL -- "no" --> Other["Allow other chart types"]
```



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><details><summary>5
files</summary><table>
<tr>
<td><strong>useDashboardPanel.spec.ts</strong><dd><code>Add VRL chart
type determination tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-5dbd79fdde0cbbb7db569095fa062931286463da0b5e72b1fbe99f56116fa5e2">+110/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>Index.spec.ts</strong><dd><code>Add VRL support tests in
Index.vue</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-a6ac1dd4a120625f1c0ed23803aa0cb63e8513b38abc975af72eefe3923b2c73">+189/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>SearchBar.spec.ts</strong><dd><code>Add VRL editor readOnly
and banner tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-63ac75d3fe278dad233e7dd7c51b3b33076d54a776b89ae05190d8522130c78b">+149/-1</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>VisualizeLogsQuery.spec.ts</strong><dd><code>Test VRL chart
type change restrictions</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-fdecf623f9a4bdfe679c6c2e4424f31cbce4a1234cb45e1de472189a46c9d4e2">+130/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>TransformSelector.spec.ts</strong><dd><code>Enable transform
toggle in visualize mode</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-c850f07fc5e32a152df85ca63e6ed45c9aeac2965a5bf1238daf7353a8699195">+247/-1</a>&nbsp;
</td>

</tr>

</table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>5
files</summary><table>
<tr>
<td><strong>useDashboardPanel.ts</strong><dd><code>Force table chart for
VRL functions</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-fbba1700750053b9eaf38ddd30dab84531bc44131c795ebb713811e4930cd7cb">+5/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>Index.vue</strong><dd><code>Propagate and apply VRL function
query</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0323c3da69b369843c5d72bf4df5b00ca6b5147192a213baf331659ecf92af62">+70/-2</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>SearchBar.vue</strong><dd><code>Remove VRL editor read-only
restriction</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-81893de4edf186b0556f610acb280c87baa2ffe67d3b48a60a866094c7635baf">+1/-26</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>TransformSelector.vue</strong><dd><code>Allow transform
selector in visualize mode</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-0037d8dd9cfbacc26309b26e98ae953e7ccbd0891ac2fd01c6314ebd7eed9d2c">+3/-13</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>VisualizeLogsQuery.vue</strong><dd><code>Restrict non-table
charts when VRL present</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/10237/files#diff-979b01d943d58c5d70faeeac5948b7f02f0b2392a12b84ef07ba7f7393ab0261">+14/-0</a>&nbsp;
&nbsp; </td>

</tr>
</table></details></td></tr></tr></tbody></table>

</details>

___
This commit is contained in:
Abhay Padamani
2026-01-30 11:09:37 +05:30
committed by GitHub
parent be7caf3dbd
commit 25a99bf39d
10 changed files with 918 additions and 43 deletions
@@ -5740,5 +5740,115 @@ describe("useDashboardPanel", () => {
const newField = panel.dashboardPanelData.data.queries[0].fields.y[panel.dashboardPanelData.data.queries[0].fields.y.length - 1];
expect(newField.color).toBeDefined();
});
/**
* VRL Visualization Support Tests
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
*
* Tests for the VRL function check added to determineChartType function.
* When VRL functions produce derived fields, chart type should always be "table".
*/
describe("VRL Visualization Support - determineChartType", () => {
it("should return 'table' when vrlFunctionFieldList has items (VRL functions present)", () => {
// Set VRL function fields to simulate VRL function output
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "parsed_json", type: "Utf8" },
{ name: "extracted_value", type: "Int64" },
];
const extractedFields = {
group_by: ["service"],
projections: ["timestamp", "count", "parsed_json"],
timeseries_field: "timestamp", // Has timeseries - would normally be "line"
};
// With VRL functions present, should always return "table"
const chartType = panel.determineChartType(extractedFields);
expect(chartType).toBe("table");
});
it("should return 'line' when vrlFunctionFieldList is empty and timeseries conditions met", () => {
// Clear VRL function fields
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
const extractedFields = {
group_by: ["service"],
projections: ["timestamp", "count"],
timeseries_field: "timestamp",
};
// Without VRL functions, normal logic applies
const chartType = panel.determineChartType(extractedFields);
expect(chartType).toBe("line");
});
it("should return 'table' with VRL functions even when perfect line chart conditions exist", () => {
// Single VRL derived field
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "computed_field", type: "Float64" },
];
// Perfect conditions for line chart: timeseries + single group_by
const extractedFields = {
group_by: ["service"],
projections: ["timestamp", "count"],
timeseries_field: "timestamp",
};
// VRL check takes precedence
const chartType = panel.determineChartType(extractedFields);
expect(chartType).toBe("table");
});
it("should return 'table' with VRL functions when no group_by fields", () => {
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "vrl_output", type: "Utf8" },
];
const extractedFields = {
group_by: [],
projections: ["timestamp", "message", "vrl_output"],
timeseries_field: "timestamp",
};
const chartType = panel.determineChartType(extractedFields);
expect(chartType).toBe("table");
});
it("should return 'table' with multiple VRL derived fields", () => {
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "field1", type: "Utf8" },
{ name: "field2", type: "Int64" },
{ name: "field3", type: "Float64" },
{ name: "field4", type: "Boolean" },
];
const extractedFields = {
group_by: [],
projections: ["timestamp"],
timeseries_field: "timestamp",
};
const chartType = panel.determineChartType(extractedFields);
expect(chartType).toBe("table");
});
it("should handle empty vrlFunctionFieldList array correctly (length check)", () => {
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
// Verify the condition check
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length).toBe(0);
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(false);
});
it("should handle single item vrlFunctionFieldList array correctly (truthy check)", () => {
panel.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "single_field", type: "Utf8" },
];
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length).toBe(1);
expect(panel.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(true);
});
});
});
});
+5
View File
@@ -2859,6 +2859,11 @@ const useDashboardPanelData = (pageKey: string = "dashboard") => {
projections: string[];
timeseries_field: string | null;
}): string => {
// If VRL functions are present, always default to table chart
if (dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0) {
return "table";
}
if (
extractedFields.timeseries_field &&
extractedFields.group_by.length <= 2
+189
View File
@@ -760,4 +760,193 @@ describe("Logs Index", async () => {
const result2 = wrapper.vm.removeFieldByName(result, 'f2');
expect(result2.find((c: any) => c.expr?.args?.expr?.column?.value === 'f2')).toBeUndefined();
});
/**
* VRL Visualization Support Tests - Index.vue
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
*
* Tests for VRL function query propagation and visualization handling:
* 1. VRL function query encoding with b64EncodeUnicode
* 2. Force table chart type when VRL functions present
* 3. Enable table_dynamic_columns for VRL table charts
* 4. shouldUseHistogramQuery excludes VRL function queries
*/
describe("VRL Visualization Support", () => {
describe("VRL function query propagation", () => {
it("should encode VRL function content when transformType is function", () => {
// Set up VRL function content
wrapper.vm.searchObj.data.tempFunctionContent = ".parsed = parse_json!(.message)";
wrapper.vm.searchObj.data.transformType = "function";
// Verify the VRL content is set
expect(wrapper.vm.searchObj.data.tempFunctionContent).toBe(".parsed = parse_json!(.message)");
expect(wrapper.vm.searchObj.data.transformType).toBe("function");
// The actual encoding happens in the component during visualization toggle
// Here we test the condition that triggers encoding
const shouldEncodeVrl =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function";
expect(shouldEncodeVrl).toBe(true);
});
it("should not encode VRL when tempFunctionContent is empty", () => {
wrapper.vm.searchObj.data.tempFunctionContent = "";
wrapper.vm.searchObj.data.transformType = "function";
const shouldEncodeVrl =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function";
expect(shouldEncodeVrl).toBeFalsy();
});
it("should not encode VRL when transformType is not function", () => {
wrapper.vm.searchObj.data.tempFunctionContent = "some code";
wrapper.vm.searchObj.data.transformType = "action";
const shouldEncodeVrl =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function";
expect(shouldEncodeVrl).toBe(false);
});
});
describe("Force table chart type for VRL", () => {
it("should identify when table chart should be forced for VRL", () => {
wrapper.vm.searchObj.data.tempFunctionContent = ".field = 1";
wrapper.vm.searchObj.data.transformType = "function";
const shouldAutoSelectChartType = true;
// Logic that forces table chart
const shouldForceTable =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function" &&
shouldAutoSelectChartType;
expect(shouldForceTable).toBe(true);
});
it("should not force table when auto-select is disabled", () => {
wrapper.vm.searchObj.data.tempFunctionContent = ".field = 1";
wrapper.vm.searchObj.data.transformType = "function";
const shouldAutoSelectChartType = false;
const shouldForceTable =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function" &&
shouldAutoSelectChartType;
expect(shouldForceTable).toBe(false);
});
});
describe("Dynamic columns for VRL table charts", () => {
it("should identify when dynamic columns should be enabled", () => {
wrapper.vm.searchObj.data.tempFunctionContent = ".new_field = 1";
wrapper.vm.searchObj.data.transformType = "function";
const chartType = "table";
// Logic that enables dynamic columns
const shouldEnableDynamicColumns =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function" &&
chartType === "table";
expect(shouldEnableDynamicColumns).toBe(true);
});
it("should not enable dynamic columns for non-table charts", () => {
wrapper.vm.searchObj.data.tempFunctionContent = ".new_field = 1";
wrapper.vm.searchObj.data.transformType = "function";
const chartType = "line";
const shouldEnableDynamicColumns =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function" &&
chartType === "table";
expect(shouldEnableDynamicColumns).toBe(false);
});
});
describe("shouldUseHistogramQuery with VRL functions", () => {
it("should return false when VRL function is present", () => {
wrapper.vm.searchObj.data.tempFunctionContent = ".field = 1";
wrapper.vm.searchObj.data.transformType = "function";
const chartType = "line";
const extractedFields = { group_by: [] };
// New logic: hasVrlFunction check
const hasVrlFunction =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function";
const shouldUseHistogramQuery =
chartType !== "table" &&
!(extractedFields?.group_by && extractedFields.group_by.length) &&
!hasVrlFunction;
expect(hasVrlFunction).toBe(true);
expect(shouldUseHistogramQuery).toBe(false);
});
it("should return true when no VRL function and conditions met", () => {
wrapper.vm.searchObj.data.tempFunctionContent = "";
wrapper.vm.searchObj.data.transformType = "";
const chartType = "line";
const extractedFields = { group_by: [] };
const hasVrlFunction =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function";
const shouldUseHistogramQuery =
chartType !== "table" &&
!(extractedFields?.group_by && extractedFields.group_by.length) &&
!hasVrlFunction;
expect(hasVrlFunction).toBeFalsy();
expect(shouldUseHistogramQuery).toBe(true);
});
it("should return false for table chart type regardless of VRL", () => {
wrapper.vm.searchObj.data.tempFunctionContent = "";
wrapper.vm.searchObj.data.transformType = "";
const chartType = "table";
const extractedFields = { group_by: [] };
const hasVrlFunction =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function";
const shouldUseHistogramQuery =
chartType !== "table" &&
!(extractedFields?.group_by && extractedFields.group_by.length) &&
!hasVrlFunction;
expect(shouldUseHistogramQuery).toBe(false);
});
it("should return false when group_by fields exist", () => {
wrapper.vm.searchObj.data.tempFunctionContent = "";
wrapper.vm.searchObj.data.transformType = "";
const chartType = "line";
const extractedFields = { group_by: ["service", "level"] };
const hasVrlFunction =
wrapper.vm.searchObj.data.tempFunctionContent &&
wrapper.vm.searchObj.data.transformType === "function";
const shouldUseHistogramQuery =
chartType !== "table" &&
!(extractedFields?.group_by && extractedFields.group_by.length) &&
!hasVrlFunction;
expect(shouldUseHistogramQuery).toBe(false);
});
});
});
});
+70 -2
View File
@@ -1604,6 +1604,22 @@ export default defineComponent({
dashboardPanelData.layout.currentQueryIndex
].customQuery = true;
// Copy VRL function query if present
if (
searchObj.data.tempFunctionContent &&
searchObj.data.transformType === "function"
) {
dashboardPanelData.data.queries[
dashboardPanelData.layout.currentQueryIndex
].vrlFunctionQuery = b64EncodeUnicode(
searchObj.data.tempFunctionContent,
);
} else {
dashboardPanelData.data.queries[
dashboardPanelData.layout.currentQueryIndex
].vrlFunctionQuery = "";
}
// Store current config and chart type to preserve them during rebuild
const queryParams = router.currentRoute.value.query;
let preservedConfig = null;
@@ -1694,6 +1710,17 @@ export default defineComponent({
return;
}
// Force table chart if VRL functions are present
if (
searchObj.data.tempFunctionContent &&
searchObj.data.transformType === "function" &&
shouldAutoSelectChartType
) {
dashboardPanelData.data.type = "table";
// Enable dynamic columns for VRL table charts
dashboardPanelData.data.config.table_dynamic_columns = true;
}
// set logs page data to searchResponseForVisualization
if (shouldUseHistogramQuery.value === true) {
// only do it if is_histogram_eligible is true on logs page
@@ -1721,6 +1748,8 @@ export default defineComponent({
// assign to visualizeChartData as well
visualizeChartData.value.queries[0].query =
dashboardPanelData.data.queries[0].query;
visualizeChartData.value.queries[0].vrlFunctionQuery =
dashboardPanelData.data.queries[0].vrlFunctionQuery;
}
}
} else {
@@ -1783,6 +1812,15 @@ export default defineComponent({
};
}
// Enable dynamic columns for VRL table charts (after preservedConfig to ensure it's set)
if (
searchObj.data.tempFunctionContent &&
searchObj.data.transformType === "function" &&
dashboardPanelData.data.type === "table"
) {
dashboardPanelData.data.config.table_dynamic_columns = true;
}
// run query
await copyDashboardDataToVisualize();
@@ -1831,6 +1869,22 @@ export default defineComponent({
dashboardPanelData.layout.currentQueryIndex
].customQuery = true;
// Update VRL function query if present
if (
searchObj.data.tempFunctionContent &&
searchObj.data.transformType === "function"
) {
dashboardPanelData.data.queries[
dashboardPanelData.layout.currentQueryIndex
].vrlFunctionQuery = b64EncodeUnicode(
searchObj.data.tempFunctionContent,
);
} else {
dashboardPanelData.data.queries[
dashboardPanelData.layout.currentQueryIndex
].vrlFunctionQuery = "";
}
// reset old rendered chart
visualizeChartData.value = {};
@@ -1842,6 +1896,15 @@ export default defineComponent({
return false;
}
// Enable dynamic columns for VRL table charts
if (
searchObj.data.tempFunctionContent &&
searchObj.data.transformType === "function" &&
dashboardPanelData.data.type === "table"
) {
dashboardPanelData.data.config.table_dynamic_columns = true;
}
// emit resize event
// this will rerender/call resize method of already rendered chart to resize
window.dispatchEvent(new Event("resize"));
@@ -2220,10 +2283,15 @@ export default defineComponent({
checkAbort();
/* Decide whether to use histogram query - don't use for table charts or when there are group_by fields */
/* Decide whether to use histogram query - don't use for table charts, when there are group_by fields, or when VRL functions are present */
const hasVrlFunction =
searchObj.data.tempFunctionContent &&
searchObj.data.transformType === "function";
shouldUseHistogramQuery.value =
dashboardPanelData.data.type !== "table" &&
!(extractedFields?.group_by && extractedFields.group_by.length);
!(extractedFields?.group_by && extractedFields.group_by.length) &&
!hasVrlFunction;
const finalQuery = logsPageQuery;
+149 -1
View File
@@ -3402,7 +3402,7 @@ describe("SearchBar.vue Actual Component Methods", () => {
componentInstance.updateQueryValue("SELECT * FROM logs");
componentInstance.resetRegionFilter();
componentInstance.createScheduleJob();
// Validate final state
expect(componentInstance.searchObj.data.query).toBe("test query");
expect(componentInstance.searchObj.data.editorValue).toBe("SELECT * FROM logs");
@@ -3410,4 +3410,152 @@ describe("SearchBar.vue Actual Component Methods", () => {
expect(componentInstance.regionFilter).toBe("");
expect(componentInstance.searchObj.meta.jobRecords).toBe(100);
});
});
/**
* VRL Visualization Support Tests - SearchBar VRL Editor
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
*
* Tests for the VRL editor changes:
* 1. VRL editor is no longer read-only in visualize mode
* 2. Warning banner removed for visualize mode
*/
describe("SearchBar.vue VRL Visualization Support", () => {
let vrlInstance: any;
beforeEach(() => {
vrlInstance = {
searchObj: {
data: {
tempFunctionContent: "",
transformType: "function",
query: "SELECT * FROM logs",
},
meta: {
logsVisualizeToggle: "search",
functionEditorPlaceholderFlag: true,
showTransformEditor: true,
},
},
};
});
describe("VRL Editor ReadOnly attribute changes", () => {
// Test: BEFORE - readOnly was based on logsVisualizeToggle
it("should understand the BEFORE behavior: readOnly was conditional on visualize mode", () => {
// BEFORE: :readOnly="searchObj.meta.logsVisualizeToggle === 'visualize'"
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
const beforeReadOnly = vrlInstance.searchObj.meta.logsVisualizeToggle === "visualize";
expect(beforeReadOnly).toBe(true); // Would have been read-only
});
// Test: AFTER - readOnly is always false
it("should have VRL editor always editable (readOnly=false)", () => {
// AFTER: :readOnly="false"
const afterReadOnly = false; // New fixed value
// In search mode
vrlInstance.searchObj.meta.logsVisualizeToggle = "search";
expect(afterReadOnly).toBe(false);
// In visualize mode - NOW ALSO EDITABLE
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
expect(afterReadOnly).toBe(false);
});
it("should allow editing VRL function content in visualize mode", () => {
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
vrlInstance.searchObj.data.tempFunctionContent = "";
// User can now edit VRL function in visualize mode
vrlInstance.searchObj.data.tempFunctionContent = ".parsed = parse_json!(.message)";
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(".parsed = parse_json!(.message)");
});
it("should allow editing VRL function content in search mode (unchanged)", () => {
vrlInstance.searchObj.meta.logsVisualizeToggle = "search";
vrlInstance.searchObj.data.tempFunctionContent = "";
// This worked before and should still work
vrlInstance.searchObj.data.tempFunctionContent = ".level = upcase!(.severity)";
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(".level = upcase!(.severity)");
});
});
describe("VRL Warning Banner removal", () => {
// Test: BEFORE - Warning banner was shown in visualize mode
it("should understand the BEFORE behavior: warning banner was shown in visualize mode", () => {
// BEFORE: v-if="searchObj.meta.logsVisualizeToggle === 'visualize'"
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
const beforeShowBanner = vrlInstance.searchObj.meta.logsVisualizeToggle === "visualize";
expect(beforeShowBanner).toBe(true); // Banner would have been shown
});
// Test: AFTER - Warning banner is completely removed
it("should NOT show VRL warning banner in visualize mode (banner removed)", () => {
// AFTER: The entire warning banner div is removed from template
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
// The banner element no longer exists in the template
const afterShowBanner = false; // Banner div removed
expect(afterShowBanner).toBe(false);
});
it("should NOT display 'VRL Function Editor is not supported in visualize mode' message", () => {
// This message was inside the removed banner
const warningMessage = "VRL Function Editor is not supported in visualize mode.";
// The message is no longer rendered since the banner was removed
// Simulating that the banner render condition no longer exists
const bannerRendered = false;
expect(bannerRendered).toBe(false);
});
});
describe("VRL Editor behavior in different modes", () => {
it("should allow VRL editing when transformType is function in visualize mode", () => {
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
vrlInstance.searchObj.data.transformType = "function";
// VRL editor should be enabled
const canEdit = vrlInstance.searchObj.data.transformType === "function";
expect(canEdit).toBe(true);
});
it("should handle VRL function content changes in visualize mode", () => {
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
vrlInstance.searchObj.data.transformType = "function";
// Simulate user typing VRL code
const vrlCode = `
.timestamp = to_timestamp!(.time)
.level = upcase!(.severity)
.parsed = parse_json!(.body)
`;
vrlInstance.searchObj.data.tempFunctionContent = vrlCode;
expect(vrlInstance.searchObj.data.tempFunctionContent).toContain("parse_json");
expect(vrlInstance.searchObj.data.tempFunctionContent).toContain("to_timestamp");
});
it("should maintain VRL function content when toggling between modes", () => {
const vrlCode = ".field = 1";
vrlInstance.searchObj.data.tempFunctionContent = vrlCode;
// Toggle to visualize
vrlInstance.searchObj.meta.logsVisualizeToggle = "visualize";
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(vrlCode);
// Toggle back to search
vrlInstance.searchObj.meta.logsVisualizeToggle = "search";
expect(vrlInstance.searchObj.data.tempFunctionContent).toBe(vrlCode);
});
});
});
+1 -26
View File
@@ -1184,9 +1184,7 @@ class="q-pr-sm q-pt-xs" />
? 'empty-function'
: ''
"
:readOnly="
searchObj.meta.logsVisualizeToggle === 'visualize'
"
:readOnly="false"
@keydown="handleKeyDown"
language="vrl"
@focus="
@@ -1198,29 +1196,6 @@ class="q-pr-sm q-pt-xs" />
/>
</div>
</div>
<div
v-if="searchObj.meta.logsVisualizeToggle === 'visualize'"
:class="
store.state.theme == 'dark'
? 'tw:bg-white tw:bg-opacity-10'
: 'tw:bg-black tw:bg-opacity-10'
"
class="tw:absolute tw:bottom-0 tw:w-full"
style="margin-top: 12px; display: flex; align-items: center; flex"
>
<q-icon
name="warning"
color="warning"
size="20px"
class="q-mx-sm"
/>
<span
class="text-negative q-pa-sm"
style="font-weight: semibold; font-size: 14px"
>VRL Function Editor is not supported in visualize
mode.</span
>
</div>
</template>
<template v-else-if="searchObj.data.transformType === 'action'">
<code-query-editor
+3 -13
View File
@@ -22,10 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class="o2-toggle-button-xs element-box-shadow"
size="xs"
flat
:disable="
!searchObj.data.transformType ||
searchObj.meta.logsVisualizeToggle === 'visualize'
"
:disable="!searchObj.data.transformType"
>
<q-icon :name="transformIcon" class="toolbar-icon-in-toggle" :class="transformsLabel" />
<q-tooltip class="tw:text-[12px]" :offset="[0, 2]">
@@ -52,7 +49,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class="btn-function no-case q-pl-sm q-pr-none no-border no-outline tw:border-none"
:class="`${searchObj.data.transformType || 'transform'}-icon`"
label-class="no-case"
:disable="searchObj.meta.logsVisualizeToggle === 'visualize'"
>
<q-list data-test="logs-search-saved-function-list">
<!-- Search Input -->
@@ -135,7 +131,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
data-test="logs-search-bar-save-transform-btn"
class=" save-transform-btn q-px-sm"
icon="save"
:disable="searchObj.data.transformType !== 'function' || searchObj.meta.logsVisualizeToggle === 'visualize'"
:disable="searchObj.data.transformType !== 'function'"
@click="fnSavedFunctionDialog"
>
@@ -143,7 +139,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
{{
searchObj.data.transformType === "action"
? t("search.saveActionDisabled")
: searchObj.meta.logsVisualizeToggle === 'visualize' ? 'Not supported for visualization' : t("common.save")
: t("common.save")
}}
</q-tooltip>
</q-btn>
@@ -244,8 +240,6 @@ const transformsLabel = computed(() => {
return searchObj.data.selectedTransform.name;
}
if (searchObj.meta.logsVisualizeToggle === 'visualize') return t("search.functionSelectionNotSupportedVisualization");
if (!isActionsEnabled.value) return t("search.functionLabel");
return searchObj.data.transformType === "action"
@@ -314,10 +308,6 @@ const fnSavedFunctionDialog = () => {
};
const getTransformLabelTooltip = computed(() => {
// function selection is not supported for visualization
if (searchObj.meta.logsVisualizeToggle === 'visualize') return t("search.functionSelectionNotSupportedVisualization");
if (!isActionsEnabled.value) return t("search.toggleFunctionEditor");
const editorType = searchObj.data.transformType === "action"
@@ -1519,4 +1519,134 @@ describe("VisualizeLogsQuery Component", () => {
});
});
});
/**
* VRL Visualization Support Tests - handleChartTypeChange
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
*
* Tests for the VRL function validation added to handleChartTypeChange.
* When VRL functions are present, only table chart type is allowed.
*/
describe("VRL Visualization Support - handleChartTypeChange", () => {
it("should allow switching to table when VRL functions are present", () => {
// Set VRL function fields
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "parsed_field", type: "Utf8" },
];
wrapper.vm.dashboardPanelData.data.type = "line";
// Simulate handleChartTypeChange to table
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("table");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
}
});
it("should prevent switching to line chart when VRL functions are present", () => {
// Set VRL function fields
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "vrl_output", type: "Utf8" },
];
wrapper.vm.dashboardPanelData.data.type = "table";
// Simulate handleChartTypeChange to line (should be prevented)
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("line");
// Should force back to table
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
}
});
it("should prevent switching to bar chart when VRL functions are present", () => {
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "derived", type: "Int64" },
];
wrapper.vm.dashboardPanelData.data.type = "table";
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("bar");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
}
});
it("should prevent switching to area chart when VRL functions are present", () => {
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "computed", type: "Float64" },
];
wrapper.vm.dashboardPanelData.data.type = "table";
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("area");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
}
});
it("should prevent switching to scatter chart when VRL functions are present", () => {
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "extracted", type: "Utf8" },
];
wrapper.vm.dashboardPanelData.data.type = "table";
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("scatter");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
}
});
it("should prevent switching to h-bar chart when VRL functions are present", () => {
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "parsed_json", type: "Utf8" },
];
wrapper.vm.dashboardPanelData.data.type = "table";
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("h-bar");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
}
});
it("should allow switching to any chart type when no VRL functions", () => {
// Clear VRL function fields
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
wrapper.vm.dashboardPanelData.data.type = "table";
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("line");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("line");
wrapper.vm.handleChartTypeChange("bar");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("bar");
wrapper.vm.handleChartTypeChange("area");
expect(wrapper.vm.dashboardPanelData.data.type).toBe("area");
}
});
it("should handle vrlFunctionFieldList length check correctly", () => {
// Test empty array
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [];
expect(wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(false);
// Test with items
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "field1", type: "Utf8" },
];
expect(wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0).toBe(true);
});
it("should handle multiple VRL derived fields", () => {
wrapper.vm.dashboardPanelData.meta.stream.vrlFunctionFieldList = [
{ name: "field1", type: "Utf8" },
{ name: "field2", type: "Int64" },
{ name: "field3", type: "Float64" },
];
wrapper.vm.dashboardPanelData.data.type = "table";
if (typeof wrapper.vm.handleChartTypeChange === "function") {
wrapper.vm.handleChartTypeChange("line");
// Should still be table due to VRL functions
expect(wrapper.vm.dashboardPanelData.data.type).toBe("table");
}
});
});
});
@@ -654,6 +654,20 @@ export default defineComponent({
return;
}
// Check if VRL functions are present and trying to switch to non-table chart
if (
dashboardPanelData.meta.stream.vrlFunctionFieldList.length > 0 &&
newType !== "table"
) {
showErrorNotification(
"VRL functions are present. Only table chart is supported when using VRL functions.",
);
// Force table chart type
dashboardPanelData.data.type = "table";
resetAggregationFunction();
return;
}
// If validation passes, proceed with the change
dashboardPanelData.data.type = newType;
resetAggregationFunction();
@@ -410,8 +410,16 @@ describe("TransformSelector.vue", () => {
expect(wrapper.exists()).toBe(true);
});
it("should disable everything when in visualize mode", () => {
/**
* VRL Visualization Support Tests - Updated behavior
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
*
* BEFORE: Transform selector was disabled in visualize mode
* AFTER: Transform selector is now enabled in visualize mode to support VRL functions
*/
it("should NOT disable toggle in visualize mode when transformType is set (VRL support)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "function";
const wrapper = mount(TransformSelector, {
props: defaultProps,
@@ -435,6 +443,93 @@ describe("TransformSelector.vue", () => {
},
});
// With VRL support, toggle should be enabled when transformType is set
// The disable condition is now only: !searchObj.data.transformType
expect(wrapper.exists()).toBe(true);
});
it("should still disable toggle when no transformType regardless of visualize mode", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = ""; // Empty - no transform type
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": {
template: '<div class="toggle" :disable="$attrs.disable" />',
},
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": { template: "<div><slot /></div>" },
"q-btn": true,
"q-icon": true,
"q-tooltip": true,
},
},
});
// Toggle should be disabled when no transformType
expect(wrapper.exists()).toBe(true);
});
it("should NOT disable save button in visualize mode when transformType is function (VRL support)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "function";
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": true,
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": { template: "<div><slot /></div>" },
"q-btn": {
template: '<button :disable="$attrs.disable" />',
},
"q-icon": true,
"q-tooltip": true,
},
},
});
// Save button should be enabled for function type even in visualize mode
// The disable condition is now only: searchObj.data.transformType !== 'function'
expect(wrapper.exists()).toBe(true);
});
it("should NOT disable dropdown in visualize mode (VRL support)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "function";
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": true,
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": {
template: '<div class="dropdown" :disable="$attrs.disable"><slot /></div>',
},
"q-btn": true,
"q-icon": true,
"q-tooltip": true,
},
},
});
// Dropdown disable condition was completely removed
expect(wrapper.exists()).toBe(true);
});
});
@@ -637,4 +732,155 @@ describe("TransformSelector.vue", () => {
expect(btnGroup.classes()).toContain("dark-theme");
});
});
/**
* VRL Visualization Support Tests - Computed Properties
* PR Reference: https://github.com/openobserve/openobserve/pull/9295
*
* Tests for the changes to transformsLabel and getTransformLabelTooltip computed properties
* that removed the visualization mode restrictions.
*/
describe("VRL Visualization Support - Computed Properties", () => {
it("should NOT show 'not supported for visualization' in transformsLabel (removed)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "function";
mockSearchObj.data.selectedTransform = null;
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": true,
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": {
template: '<div :label="$attrs.label"><slot /></div>',
},
"q-btn": true,
"q-icon": true,
"q-tooltip": true,
},
},
});
// transformsLabel should show "Function" not the visualization restriction message
// The line "if (searchObj.meta.logsVisualizeToggle === 'visualize') return t(...)" was removed
expect(wrapper.exists()).toBe(true);
});
it("should show standard function label in visualize mode (VRL support)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "function";
mockSearchObj.data.selectedTransform = null;
mockIsActionsEnabled.value = true;
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": true,
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": { template: "<div><slot /></div>" },
"q-btn": true,
"q-icon": true,
"q-tooltip": true,
},
},
});
// Label should be "Function" in visualize mode now
expect(wrapper.exists()).toBe(true);
});
it("should show standard action label in visualize mode (VRL support)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "action";
mockSearchObj.data.selectedTransform = null;
mockIsActionsEnabled.value = true;
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": true,
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": { template: "<div><slot /></div>" },
"q-btn": true,
"q-icon": true,
"q-tooltip": true,
},
},
});
// Label should be "Action" in visualize mode now
expect(wrapper.exists()).toBe(true);
});
it("should show standard tooltip in visualize mode (VRL support)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "function";
mockSearchObj.meta.showTransformEditor = false;
mockIsActionsEnabled.value = true;
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": true,
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": { template: "<div><slot /></div>" },
"q-btn": true,
"q-icon": true,
"q-tooltip": { template: "<div class='tooltip'><slot /></div>" },
},
},
});
// Tooltip should show "Show Function Editor" not visualization restriction message
// The line "if (searchObj.meta.logsVisualizeToggle === 'visualize') return t(...)" was removed
expect(wrapper.exists()).toBe(true);
});
it("should show 'Save' tooltip for function type in visualize mode (VRL support)", () => {
mockSearchObj.meta.logsVisualizeToggle = "visualize";
mockSearchObj.data.transformType = "function";
const wrapper = mount(TransformSelector, {
props: defaultProps,
global: {
plugins: [store, i18n, Quasar],
mocks: {
$q,
},
stubs: {
"q-toggle": true,
"q-btn-group": { template: "<div><slot /></div>" },
"q-btn-dropdown": { template: "<div><slot /></div>" },
"q-btn": true,
"q-icon": true,
"q-tooltip": { template: "<div class='tooltip'><slot /></div>" },
},
},
});
// Save button tooltip should show "Save" not "Not supported for visualization"
// The ternary check for logsVisualizeToggle === 'visualize' was removed
expect(wrapper.exists()).toBe(true);
});
});
});