Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions examples/single/robot/tests/conditional.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
*** Settings ***
Documentation Tests exercising IF/ELSE IF/ELSE branches.
... Used to verify that conditional steps carry correct
... start_time / end_time / duration into Qase TestOps.
Library Collections
Library String

*** Test Cases ***

IF Branch Taken
[Documentation] The first IF branch executes; ELSE branch is skipped.
[Tags] Q-100
${value}= Set Variable yes
IF "${value}" == "yes"
Log Going through the IF branch
Sleep 50ms
Should Be Equal ${value} yes
ELSE
Log This ELSE must remain skipped
Sleep 200ms
END

ELSE Branch Taken
[Documentation] IF condition is false; ELSE branch executes.
[Tags] Q-101
${value}= Set Variable no
IF "${value}" == "yes"
Log This IF must remain skipped
Sleep 200ms
ELSE
Log Going through the ELSE branch
Sleep 100ms
Should Be Equal ${value} no
END

ELSE IF Chain Middle Branch
[Documentation] Three-way chain: middle ELSE IF branch executes.
[Tags] Q-102
${value}= Set Variable medium
IF "${value}" == "low"
Log Low branch (skipped)
Sleep 150ms
ELSE IF "${value}" == "medium"
Log Middle branch executing
Sleep 75ms
Should Be Equal ${value} medium
ELSE
Log High branch (skipped)
Sleep 150ms
END
2 changes: 1 addition & 1 deletion qase-robotframework/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "qase-robotframework"
version = "6.0.1"
version = "6.0.2"
description = "Qase Robot Framework Plugin"
readme = "README.md"
authors = [{name = "Qase Team", email = "support@qase.io"}]
Expand Down
10 changes: 8 additions & 2 deletions qase-robotframework/src/qase/robotframework/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,14 @@ def __parse_condition_steps(self, result_step, accumulated_vars: dict = None) ->
else:
step = Listener._create_gherkin_step_from_name(body_element)

step.execution.start_time = None
step.execution.end_time = None
start_time = getattr(body_element, "start_time", None)
end_time = getattr(body_element, "end_time", None)
elapsed = getattr(body_element, "elapsed_time", None)

step.execution.start_time = start_time.timestamp() if start_time is not None else None
step.execution.end_time = end_time.timestamp() if end_time is not None else None
if elapsed is not None:
step.execution.duration = int(elapsed.total_seconds() * 1000)

steps.append(step)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,68 @@ def test_start_and_end_time_round_trip(self):

assert steps[0].execution.start_time == keyword.start_time.timestamp()
assert steps[0].execution.end_time == keyword.end_time.timestamp()


class _FakeIfBranch:
"""Stand-in for a Robot Framework IF/ELSE branch body element."""

def __init__(self, branch_type: str, elapsed_seconds: float, body=None):
start = datetime(2026, 1, 1, tzinfo=timezone.utc)
self.type = branch_type # "IF" / "ELSE IF" / "ELSE"
self.status = "PASS"
self.start_time = start
self.elapsed_time = timedelta(seconds=elapsed_seconds)
self.end_time = start + self.elapsed_time
self.body = body or []
self.values = ()


class TestParseConditionStepsTiming:
"""IF/ELSE branches must carry their RF start_time/end_time/duration.

Regression for the bug where __parse_condition_steps unconditionally
overwrote start_time/end_time with None, so conditional branches
showed up in TestOps without any timing data.
"""

def _parse(self, listener, branches):
result_step = MagicMock(spec=["body"])
result_step.body = branches
return listener._Listener__parse_condition_steps(result_step)

def test_executed_if_branch_carries_real_timestamps(self):
listener = _bare_listener()
branch = _FakeIfBranch("IF", elapsed_seconds=0.053)

steps = self._parse(listener, [branch])

assert len(steps) == 1
assert steps[0].execution.start_time == branch.start_time.timestamp()
assert steps[0].execution.end_time == branch.end_time.timestamp()
assert steps[0].execution.duration == 53

def test_skipped_else_branch_still_gets_timestamps(self):
"""Skipped branches have near-zero elapsed time but must still have
non-None start_time/end_time so the timeline shows them."""
listener = _bare_listener()
branch = _FakeIfBranch("ELSE", elapsed_seconds=0.0001)

steps = self._parse(listener, [branch])

assert steps[0].execution.start_time is not None
assert steps[0].execution.end_time is not None
assert steps[0].execution.duration == 0 # int(0.0001 * 1000) == 0

def test_else_if_chain_each_branch_keeps_timing(self):
listener = _bare_listener()
branches = [
_FakeIfBranch("IF", elapsed_seconds=0.0001), # not taken
_FakeIfBranch("ELSE IF", elapsed_seconds=0.076), # taken
_FakeIfBranch("ELSE", elapsed_seconds=0.0001), # not taken
]

steps = self._parse(listener, branches)

assert [s.execution.duration for s in steps] == [0, 76, 0]
assert all(s.execution.start_time is not None for s in steps)
assert all(s.execution.end_time is not None for s in steps)
Loading