Skip to content
9 changes: 9 additions & 0 deletions bin/jmeter.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,15 @@ view.results.tree.renderers_order=.RenderAsText,.RenderAsRegexp,.RenderAsBoundar
# Set to 0 to store all results (might consume a lot of memory)
#view.results.tree.max_results=500

# Set to true to enable by default the checkbox for auto scrolling.
#view.results.tree.autoscroll=true

# Set to true to enable by default the checkbox for stop auto scrolling on 1st error.
#view.results.tree.autostop=true

# Default width of the scroll view
#view.results.tree.width=250

# Maximum size of Document that can be parsed by Tika engine; default=10 * 1024 * 1024 (10 MB)
# Set to 0 to disable the size check
#document.max_size=0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public abstract class SamplerResultTab implements ResultRenderer {
private static final Logger LOGGER = LoggerFactory.getLogger(SamplerResultTab.class);
// N.B. these are not multi-threaded, so don't make it static
private final DateTimeFormatter dateFormat = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss z") // ISO format $NON-NLS-1$
.ofPattern("yyyy-MM-dd HH:mm:ss.SSS O") // ISO format $NON-NLS-1$
.withZone(ZoneId.systemDefault());

private static final String NL = "\n"; // $NON-NLS-1$
Expand Down Expand Up @@ -265,6 +265,10 @@ public void setupTabPane() {
.append(JMeterUtils
.getResString("view_results_thread_name")).append(SPACE) //$NON-NLS-1$
.append(sampleResult.getThreadName()).append(NL);
statsBuff

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be mentioned in the title of this PR. Was this intended to be included?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, this was an later add-on. Purpose is: dealing with a long samplers listing in a View Results Tree one can easily lose relation between scroll on left side and selected sampler on right side.
Having the sampler name included can help in tracking between the 2 sources of information.
I will update PR title.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More changes included

.append(JMeterUtils
.getResString("view_results_sample_name")) //$NON-NLS-1$
.append(sampleResult.getSampleLabel()).append(NL);
String startTime = dateFormat
.format(Instant.ofEpochMilli(sampleResult.getStartTime()));
statsBuff
Expand Down Expand Up @@ -363,6 +367,10 @@ public void setupTabPane() {
resultModel.addRow(new RowResult(
JMeterUtils.getParsedLabel("view_results_thread_name"), //$NON-NLS-1$
sampleResult.getThreadName()));
resultModel.addRow(new RowResult(
JMeterUtils.getParsedLabel(
"view_results_sample_name"), //$NON-NLS-1$
sampleResult.getSampleLabel()));
resultModel.addRow(new RowResult(
JMeterUtils.getParsedLabel("view_results_sample_start"), //$NON-NLS-1$
startTime));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
Expand All @@ -43,8 +45,10 @@
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
Expand Down Expand Up @@ -116,6 +120,15 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
private static final String VIEWERS_ORDER =
JMeterUtils.getPropDefault("view.results.tree.renderers_order", ""); // $NON-NLS-1$ //$NON-NLS-2$

//default scroll checkbox status
private static final boolean SCROLL_CHECKBOX = JMeterUtils.getPropDefault("view.results.tree.autoscroll", true);

//default uncheck if failed scroll checkbox status
private static final boolean SCROLL_STOP_CHECKBOX = JMeterUtils.getPropDefault("view.results.tree.autostop", true);

// default tree scroll width
private static final int SCROLL_WIDTH = JMeterUtils.getPropDefault("view.results.tree.width", 300); // $NON-NLS-1$

private static final int REFRESH_PERIOD = JMeterUtils.getPropDefault("jmeter.gui.refresh_period", 500);

private static final ImageIcon imageSuccess = JMeterUtils.getImage(
Expand All @@ -138,6 +151,7 @@ public class ViewResultsFullVisualizer extends AbstractVisualizer
private Object resultsObject = null;
private TreeSelectionEvent lastSelectionEvent;
private JCheckBox autoScrollCB;
private JCheckBox scrollStopCB;
private final Queue<SampleResult> buffer = new ArrayDeque<>();
private final int maxResults;
private boolean dataChanged;
Expand All @@ -161,6 +175,11 @@ public void add(final SampleResult sample) {
}
buffer.add(sample);
dataChanged = true;
if (!"true".equals(System.getProperty("java.awt.headless"))) {
if (!sample.isSuccessful() && autoScrollCB.isSelected() && scrollStopCB.isSelected()) {
autoScrollCB.setSelected(false);
}
}
}
}

Expand Down Expand Up @@ -219,6 +238,7 @@ private void updateGui() {
if (selectedPath != null) {
jTree.setSelectionPath(selectedPath);
}

if (autoScrollCB.isSelected() && root.getChildCount() > 1) {
jTree.scrollPathToVisible(new TreePath(new Object[] { root,
treeModel.getChild(root, root.getChildCount() - 1) }));
Expand Down Expand Up @@ -328,6 +348,8 @@ public void clearData() {
}
resultsRender.clearData();
resultsObject = null;
autoScrollCB.setSelected(SCROLL_CHECKBOX);
scrollStopCB.setSelected(SCROLL_STOP_CHECKBOX);
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -390,6 +412,44 @@ private void valueChanged(TreeSelectionEvent e, boolean forceRendering) {
}
Object userObject = node.getUserObject();
resultsRender.setSamplerResult(userObject);

// Add Show Current Node button to the tab pane
if (rightSide.getTabCount() > 0) {
Component firstTab = rightSide.getComponentAt(0);
if (firstTab instanceof JPanel samplerResultTab) {
samplerResultTab = (JPanel) firstTab;
// Add button at the top of the sampler result panel
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JButton nodeInfoButton = new JButton(JMeterUtils.getResString("show_current_node")); // $NON-NLS-1$
nodeInfoButton.addActionListener(e2 -> {
// Get the currently selected node when button is clicked
DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();
if (currentNode != null) {
TreePath path = new TreePath(currentNode.getPath());
// First expand the path to make sure all parent nodes are visible
jTree.expandPath(path);
// Get the bounds before scrolling
Rectangle bounds = jTree.getPathBounds(path);
if (bounds != null) {
// Get the visible rectangle
Rectangle visible = jTree.getVisibleRect();
// Calculate the new viewport position to center the node
int centerY = Math.max(0, bounds.y - (visible.height - bounds.height) / 2);
// Create rectangle that will center the node
Rectangle scrollTo = new Rectangle(0, centerY, 1, visible.height);
jTree.scrollRectToVisible(scrollTo);
// Ensure the node is selected and visible
jTree.setSelectionPath(path);
jTree.repaint();
}
}
});
buttonPanel.add(nodeInfoButton);
samplerResultTab.add(buttonPanel, BorderLayout.NORTH);
samplerResultTab.revalidate();
}
}

resultsRender.setupTabPane(); // Processes Assertions
// display a SampleResult
if (userObject instanceof SampleResult sampleResult) {
Expand Down Expand Up @@ -429,15 +489,40 @@ private synchronized Component createLeftPanel() {
jTree.setRootVisible(false);
jTree.setShowsRootHandles(true);
JScrollPane treePane = new JScrollPane(jTree);
treePane.setPreferredSize(new Dimension(200, 300));

VerticalPanel leftPane = new VerticalPanel();
leftPane.add(treePane, BorderLayout.CENTER);
leftPane.add(createComboRender(), BorderLayout.NORTH);

// Add controls panel at the top
JPanel controlsPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
controlsPane.add(createComboRender());
leftPane.add(controlsPane, BorderLayout.NORTH);

// Add tree panel in the center with proper sizing
JPanel treePanel = new JPanel(new BorderLayout());
treePanel.add(treePane, BorderLayout.CENTER);
leftPane.add(treePanel, BorderLayout.CENTER);

// Create bottom panel with checkbox
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));

// Add autoscroll checkbox
autoScrollCB = new JCheckBox(JMeterUtils.getResString("view_results_autoscroll")); // $NON-NLS-1$
autoScrollCB.setSelected(false);
autoScrollCB.setSelected(SCROLL_CHECKBOX);
autoScrollCB.addItemListener(this);
leftPane.add(autoScrollCB, BorderLayout.SOUTH);
bottomPanel.add(autoScrollCB);

// Add autoscroll checkbox
scrollStopCB = new JCheckBox(JMeterUtils.getResString("view_results_scrollstop")); // $NON-NLS-1$
scrollStopCB.setSelected(SCROLL_STOP_CHECKBOX);
scrollStopCB.addItemListener(this);
bottomPanel.add(scrollStopCB);

leftPane.add(bottomPanel, BorderLayout.SOUTH);

// Ensure the left pane maintains its size during window resizing
leftPane.setMinimumSize(new Dimension(SCROLL_WIDTH, 0));
leftPane.setPreferredSize(new Dimension(SCROLL_WIDTH, 0));

return leftPane;
}

Expand Down Expand Up @@ -615,4 +700,9 @@ public Component getTreeCellRendererComponent(JTree tree, Object value,
public void itemStateChanged(ItemEvent e) {
// NOOP state is held by component
}

@Override
public String[] getFileExts() {
return new String[] { ".jtl" };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public abstract class AbstractVisualizer
/** Logging. */
private static final Logger log = LoggerFactory.getLogger(AbstractVisualizer.class);

/** File Extensions */
/** Default File Extensions */
private static final String[] EXTS = { ".xml", ".jtl", ".csv" }; // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$

/** A panel allowing results to be saved. */
Expand Down Expand Up @@ -150,7 +150,7 @@ public void actionPerformed(ActionEvent e) {
d.setVisible(true);
});

filePanel = new FilePanel(JMeterUtils.getResString("file_visualizer_output_file"), EXTS); // $NON-NLS-1$
filePanel = new FilePanel(JMeterUtils.getResString("file_visualizer_output_file"), getFileExts()); // $NON-NLS-1$
filePanel.addChangeListener(this);
filePanel.add(new JLabel(JMeterUtils.getResString("log_only"))); // $NON-NLS-1$
filePanel.add(errorLogging);
Expand Down Expand Up @@ -345,4 +345,8 @@ public void clearGui(){
super.clearGui();
filePanel.clearGui();
}

public String[] getFileExts() {
return EXTS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1401,8 +1401,9 @@ view_graph_tree_title=View Graph Tree
view_results_assertion_error=Assertion error:
view_results_assertion_failure=Assertion failure:
view_results_assertion_failure_message=Assertion failure message:
view_results_autoscroll=Scroll automatically
view_results_childsamples=Child samples
view_results_autoscroll=Auto scroll
view_results_scrollstop=Stop scroll on error
view_results_childsamples=Child samples?
view_results_datatype=Data type ("text"|"bin"|""):
view_results_desc=Shows the text results of sampling in tree form
view_results_error_count=Error Count:
Expand Down Expand Up @@ -1461,6 +1462,7 @@ view_results_table_request_tab_raw=Raw
view_results_table_result_tab_parsed=Parsed
view_results_table_result_tab_raw=Raw
view_results_thread_name=Thread Name:
view_results_sample_name=Sample label:
view_results_title=View Results
view_results_tree_title=View Results Tree
warning=Warning!
Expand Down Expand Up @@ -1545,3 +1547,4 @@ xpath2_extractor_match_number_failure=MatchNumber out of bonds \:
you_must_enter_a_valid_number=You must enter a valid number
zh_cn=Chinese (Simplified)
zh_tw=Chinese (Traditional)
show_current_node=Focus on selected node
12 changes: 12 additions & 0 deletions xdocs/usermanual/properties_reference.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,18 @@ JMETER-SERVER</source>
Can be switched off by setting the value to <code>-1</code>.<br/>
Defaults to: <code>10000</code>
</property>
<property name="view.results.tree.autoscroll">
Set to <code>true</code> to enable by default the checkbox for auto scrolling.<br/>
Defaults to: <code>true</code>
</property>
<property name="view.results.tree.autostop">
Set to <code>true</code> to enable by default the checkbox for stop auto scrolling on 1st error.<br/>
Defaults to: <code>true</code>
</property>
<property name="view.results.tree.width">
Default width of the scroll view.<br/>
Defaults to: <code>250</code>
</property>
<property name="document.max_size">
Maximum size (in bytes) of Document that can be parsed by Tika engine<br/>
Set to zero to disable the size check.<br/>
Expand Down