Skip to content

Add react component for Timeline visualization - #524

Draft
MuhammadTalha57 wants to merge 28 commits into
cucumber:mainfrom
MuhammadTalha57:enhancement/timeline-report
Draft

Add react component for Timeline visualization#524
MuhammadTalha57 wants to merge 28 commits into
cucumber:mainfrom
MuhammadTalha57:enhancement/timeline-report

Conversation

@MuhammadTalha57

@MuhammadTalha57 MuhammadTalha57 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Status

This PR is currently a draft.

While testing the implementation, I realized the tests I initially wrote don't accurately reflect real parallel execution. I'm updating the test suite to better cover actual parallel behavior before requesting a review.

🤔 What's changed?

This PR adds a Timeline react component for better visualization of parallel runs.

image image

⚡️ What's your motivation?

Fixes #126 .

🏷️ What kind of change is this?

  • 🏦 Refactoring/debt/DX (improvement to code design, tooling, etc. without changing behaviour)
  • ⚡ New feature (non-breaking change which adds new behaviour)

♻️ Anything particular you want feedback on?

I’d appreciate feedback on adding Timeline component in Report component as opposed to Cucumber-jvm which implements Timeline separately. However Timeline can still be used as separate component.

📋 Checklist:

  • I agree to respect and uphold the Cucumber Community Code of Conduct
  • I've changed the behaviour of the code
    • I have added/updated tests to cover my changes.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly.
  • Users should know about my change
    • I have added an entry to the "Unreleased" section of the CHANGELOG, linking to this pull request.

This text was originally generated from a template, then edited by hand. You can modify the template here.

@MuhammadTalha57 MuhammadTalha57 changed the title Enhancement/timeline report Add react component for Timeline visualization Jul 5, 2026
@MuhammadTalha57
MuhammadTalha57 marked this pull request as draft July 5, 2026 10:17
@mpkorstanje
mpkorstanje requested review from davidjgoss and mpkorstanje and removed request for davidjgoss July 5, 2026 16:31
@mpkorstanje

Copy link
Copy Markdown
Member

I’d appreciate feedback on adding Timeline component in Report component as opposed to Cucumber-jvm which implements Timeline separately. However Timeline can still be used as separate component.

I don't think it should go into the main report. It will not be informative most of the time and it doesn't interact with the search component. I think some sort of tab-structure or drop-down menu might be useful here to switch between the different reports.

@davidjgoss thoughts?

@mpkorstanje
mpkorstanje removed their request for review July 5, 2026 16:44

@davidjgoss davidjgoss left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is great, thanks for picking this up.

I appreciate this is still in draft but I'll give some high-level (functionality, not implementation) feedback now in case it's helpful:

  • I agree with the tabs approach - I see the timeline as an alternate way to view and navigate the test run, with the list of feature files being the other one
  • On that basis, I think selecting a test case should show the full <TestCaseOutcome/> below it, not just a summary - that component should be re-usable, but you might need to add the title yourself
  • Where there is only one process, I think the "Main process" label can be dropped entirely
  • I'm happy to see the filters and search honoured, though I'd like the timeline axis to remain the full test run time and not contract, so you can see where your matching scenarios are on the whole timeline, if that makes sense

Also, I just landed #528 which if you merge it in here will give you a more realistic sample ("parallel") to work with for testing without hand-crafting anything. This might expose some edge cases for you.

Image

@davidjgoss

Copy link
Copy Markdown
Member

Also, if you have any friction with Query like having to jump through hoops calling multiple methods to get to some bit of data, do flag that up here and we can probably smooth it out. For example some methods accept TestCaseStarted but could also work with TestCaseFinished which is what you're using here.

@MuhammadTalha57

MuhammadTalha57 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@davidjgoss Thanks for the feedback.
I have made the following changes:

  1. Used vis-timeline js to render timeline (as it is also being used in cucumber-jvm)
  2. Added tab based switching.
  3. <TestCaseOutcome/> is shown along with the summary.
  4. Removed "Main process" label when no worker id is present.
  5. Now axis doesn't change on filtering items.
image image

I haven't updated tests as they are not compatible with updated implementation. I think it makes sense to write tests after getting feedback on current implementation.

I want to ask that timeline component should reflect same behavior on searching and filtering as Filtered docs ?
And currently I am just searching based on string matching.
I would appreciate feedback on searching and filtering and hints to implement the same behavior as of Filtered docs.

@mpkorstanje

mpkorstanje commented Jul 9, 2026

Copy link
Copy Markdown
Member

Used vis-timeline js to render timeline (as it is also being used in cucumber-jvm)

Oh. You don't have to copy anything from Cucumber JVM. The choice at the time was rather arbitrary. If there are better libraries for rendering graphs and timelines free to use those.

@MuhammadTalha57

Copy link
Copy Markdown
Contributor Author

Used vis-timeline js to render timeline (as it is also being used in cucumber-jvm)

Oh. You don't have to copy anything from Cucumber JVM. The choice at the time was rather arbitrary. If there are better libraries for rendering graphs and timelines free to use those.

Actually, I ended up using vis-timeline after exploring a few alternatives.

My first implementation was a custom React component because I thought it would integrate better than a vanilla JavaScript library. However, implementing features like smooth zooming and panning proved more involved than I expected.

I then evaluated React timeline libraries, including react-calendar-timeline, but the ones I tried didn't provide the level of time resolution I needed (for example, zooming down to milliseconds).

After that, I revisited vis-timeline and found it met the requirements while also providing the interaction I was looking for, so I switched to that instead.

If there's another library you'd recommend that would be a better fit for this project, I'd be happy to explore it.

@mpkorstanje

mpkorstanje commented Jul 10, 2026

Copy link
Copy Markdown
Member

Mmh. Then we've inherited some problems from the vis-timeline too.

The current design is not very readable. The timeline compresses the pickle names too much.
I don't know what would make it more readable. Perhaps flipping the axis of the timeline could help here? And/or perhaps the text can scale with it.

I don't have quite enough time to look into this much deeper today. But let me know what you think.

@davidjgoss

davidjgoss commented Jul 10, 2026

Copy link
Copy Markdown
Member

Some thoughts on the timeline rendering:

  • I would drop the font size to say 0.75em
  • The solid background colour based on the status is a bit garish and probably not great for accessibility. Consider matching what we do on the document accordion - pale grey background, normal text, and prepend the coloured status icon - maybe this would make it too hard to distinguish e.g. failures amongst 99% success, but feels worth a shot

The vis-timeline library concerns me due to how much it would inflate the bundle size, along with legacy dependencies like moment. However, I think it's the right thing to be quickly iterating with for now - once we settle on a subset of functionality we need, we can shop around again and/or look at rolling our own if it makes sense.

I want to ask that timeline component should reflect same behavior on searching and filtering as Filtered docs? And currently I am just searching based on string matching.

Good question. We do have all the logic we need for properly searching and filtering already, but it's kind of wrapped up in the "documents" view. I think I can uncouple it from that and make it easier for you though - leave that with me for a day or two.

I haven't updated tests as they are not compatible with updated implementation. I think it makes sense to write tests after getting feedback on current implementation.

Definitely! We're still experimenting at this point.

@MuhammadTalha57

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed feedback, I'll get working on these changes and update the PR soon.

@MuhammadTalha57

MuhammadTalha57 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

I've experimented with a few different UI approaches based on the feedback. Here are the results:

1. Neutral background (no status background color)
image
2. No labels + tooltip on hover
image
3. Swapped axes

vis-timeline doesn't support swapping the axes so I used a temporary prototype (generated with AI) to visualize how a vertical timeline might look.


image
4. Fullscreen mode

I also tried a fullscreen view to provide more horizontal space. In practice, I don't think it significantly improves readability.


image

My impression is that retaining status colors makes it much easier to scan the timeline although using lighter shades would be better.

I also think supporting both horizontal and vertical orientations could be valuable as each has its own strengths.

@mpkorstanje
mpkorstanje self-requested a review July 14, 2026 23:20

@mpkorstanje mpkorstanje left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The tooltip on hover looks like it could be a nice approach. Is it possible to use a non-native tool tip that looks like the card with the status icon and pickle name from the first experiment?

@MuhammadTalha57

Copy link
Copy Markdown
Contributor Author

Other than what has been said above, not really. If it helps, I can lock down:

  • A timeline with time on the horizontal axis
  • Pastel colored blocks on the timeline with no info.
  • Tool tip card with details on hover.
  • Theme of the timeline should fit the existing theme
  • Filtering not required, but might be nice in a later iteration.

And I haven't done an design exercise like this asynchronously before. But I'm happy to give early feedback quickly. As you'll doing most of the work I reckon it is important to avoid wasting your time.

Is there anything specific you need to know?

Thanks for locking that down, it helps a lot!

I'll get started on this. Since we've discussed vis-timeline concerns previously, I'm going to try a custom React implementation again. My plan is to build the basic static layout first (horizontal axis, pastel blocks per worker row, and the custom hover tooltip).

I'll share a quick screenshot/WIP of that layout for your early feedback before I dive into the panning/zoom logic. Does that approach work for you?

@davidjgoss davidjgoss mentioned this pull request Jul 22, 2026
7 tasks
@mpkorstanje

Copy link
Copy Markdown
Member

Sounds good!

@davidjgoss

Copy link
Copy Markdown
Member

Just to add to a couple of points.

Filtering not required, but might be nice in a later iteration.

Fair, but I did just add a new hook useFilteredTestCases in #545 that should make that zero effort if you merge it in - it was part of a refactor I wanted to do anyway, and now gives you TestCaseFinisheds that have been pre-filtered for the status, tag and search criteria.

I'm going to try a custom React implementation again...build the basic static layout first

Yep we were going to have to tackle it sooner or later. I agree with the iterative approach. In particular I'd defer worrying about timestamp/duration markers.

the custom hover tooltip

There ought to be something you can build on in react-aria for this.

@davidjgoss

Copy link
Copy Markdown
Member

I'll also call out that this won't yet play nicely with retries, because the Query methods for test cases (started and finished) only give you back the final attempts where tests have been retried. This is something we can tackle later as part of #75.

@MuhammadTalha57

Copy link
Copy Markdown
Contributor Author

I've completed the first iteration - apologies for the delay, I got caught up with other things.
Here are the screenshots:
image
image
image

A few things to note:

  1. Border Radius: Looks fine in most of the cases but feels a bit off when there are many short duration runs (as in the last screenshot). Without it though, Adjacent bars tend to blend together making it harder to distinguish boundaries without hovering. Happy to hear thoughts on this.
  2. Block Colors: For consistency, I reused the same statuses colors in SearchBar component.
  3. Tooltip: Implemented using react-aria.
  4. Axis: Not functional yet - Items are placed arbitrarily and the axis unit is fixed for now just to calculate width of each bar.

For the axis, I'm thinking of using discrete units rather than continuous scale (e.g. stepping through 5ms -> 10ms -> 50ms -> ...). It would also help to avoid decimal precision issue when calculating bar widths. Would appreciate feedback whether this approach makes sense before I proceed.

@mpkorstanje

Copy link
Copy Markdown
Member

Border Radius: Looks fine in most of the cases but feels a bit off when there are many short duration runs (as in the last screenshot). Without it though, Adjacent bars tend to blend together making it harder to distinguish boundaries without hovering. Happy to hear thoughts on this.

Looks good to me.

Block Colors: For consistency, I reused the same statuses colors in SearchBar component.

Looks good.

Tooltip: Implemented using react-aria.

I'll defer that to David. But looks good.

Axis: Not functional yet - Items are placed arbitrarily and the axis unit is fixed for now just to calculate width of each bar.
For the axis, I'm thinking of using discrete units rather than continuous scale (e.g. stepping through 5ms -> 10ms -> 50ms -> ...). It would also help to avoid decimal precision issue when calculating bar widths. Would appreciate feedback whether this approach makes sense before I proceed.

Sounds like a good idea.

How do you plan to handle items with a duration < 1ms? There should be some sort of minimum resolution.

@MuhammadTalha57

Copy link
Copy Markdown
Contributor Author

How do you plan to handle items with a duration < 1ms? There should be some sort of minimum resolution.

I was thinking to set the minimum resolution to 1ms as it's the safest smallest unit I guess I can work with.

For rendering,
I'm gonna fix the min-width to maybe 2px.

Though, It will make the bars out of sync with real time. But I believe the primary use case of this component is to let the user inspect the duration of each run rather than actual start/end time. Correct me If I'm wrong here. If that's the use case then I guess min-width solution is pretty simple and effective here.

@mpkorstanje

mpkorstanje commented Jul 27, 2026

Copy link
Copy Markdown
Member

Though, It will make the bars out of sync with real time. But I believe the primary use case of this component is to let the user inspect the duration of each run rather than actual start/end time. Correct me If I'm wrong here. If that's the use case then I guess min-width solution is pretty simple and effective here.

Doesn't the minimum resolution depends on the zoom level? If you're zoomed all the way out, the the minimum 2px might represent 1ms, 5ms, 10ms or even 1000ms.

I suppose you could bucket the scenarios when the go below the minimum resolution. So when you hover over the bucket it shows multiple scenarios. It still creates the correct impression that the time is efficiently filled without changing the start and end times.

It is important that the start/end times don't move. Users will be looking for gaps to try and see why execution isn't efficient. If a bunch of small test cases push the line to the right, then one might wonder (incorrectly) why they're not executed on the thread that appears to be done (but actually isn't).

Btw, I do think an absolute minimum of 1ms is fine. But it doesn't quite catch everything.

@MuhammadTalha57

Copy link
Copy Markdown
Contributor Author

Thanks for pointing that out determining the resolution based on active zoom level makes total sense.

Regarding the bucketing approach: I’m curious how we should handle isolated micro-runs where grouping isn't possible (for example, a 1ms task sandwiched by two longer runs, like [30s -> 1ms -> 30s]). In that scenario, bucketing wouldn't merge the small task with anything else.

To tackle the sub-pixel issue for these isolated events, I looked into how existing trace tools (like Google’s Perfetto) handle this. They typically enforce a minimum visual width (e.g., 1px or 2px) strictly so the event remains visible.

However, I want to be completely transparent that defaulting to a min-width does introduce an inevitable visual error. For instance, if a 1ms task is forced to render at 2px on a heavily zoomed-out scale (where 2px might represent 500ms) it will visually over-represent the time taken. This means the visual start/end boundaries will have a slight offset and might push adjacent visual blocks even if the underlying timestamp data in the tooltip remains 100% accurate.

I would appreciate your thoughts on this trade-off. Should we accept this slight visual offset for the sake of keeping isolated micro-runs visible or is there another behavior you'd prefer?

@mpkorstanje

mpkorstanje commented Jul 28, 2026

Copy link
Copy Markdown
Member

Regarding the bucketing approach: I’m curious how we should handle isolated micro-runs where grouping isn't possible (for example, a 1ms task sandwiched by two longer runs, like [30s -> 1ms -> 30s]). In that scenario, bucketing wouldn't merge the small task with anything else.

Earlier you said you wanted to use discrete units. So the 1ms task would fall in a bucket that also includes a part of either one of the 30s tasks.

Visually the 1ms task can then "steal" a part of the 30s tasks bar.

I would appreciate your thoughts on this trade-off. Should we accept this slight visual offset for the sake of keeping isolated micro-runs visible or is there another behavior you'd prefer?

I thought I already answered that. For this view preserving the empty space where nothing happens is important.

@MuhammadTalha57

Copy link
Copy Markdown
Contributor Author

I've completed the first draft of the Timeline component. Here is a breakdown of the key features and implementation details:

1. Item Buckets Items with duration less than minDuration are grouped together and rendered as a single TimelineBar with status Unknown. For grouped items tooltip is displayed as a vertical stack and clicking on tooltip will display the details of selected item. image
2. Axis Axis Ruler is implemented to allow panning and zoom. Axis units are hardcoded [smallest = 1ms, largest = 24hr] resulting in discrete axis rather than continuous. Panning is allowed to a certain offset. 1 unit is displayed with 2px and one minorTick denotes 5 units. image image
3. Duration Calculation Since start and end is being measured in ms therefore multiple items can have same start or end time and duration is calculated considering both points inclusive i.e. duration = end - start + 1 resulting in overlapping timeline bars. image image

To optimize performance, I used useRef + inline styles + CSS variables to handle panning. This avoids costly React re-renders and performs significantly better though panning can still become slightly laggy at very low zoom levels due to DOM node density.

I would appreciate feedback on the current implementation especially regarding the Duration calculation (inclusive end) and the Item Bucket logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add timeline visualisation for parallel runners

3 participants