Skip to content

Add test for Pie and Legend animation#7488

Merged
PavelVanecek merged 1 commit into
mainfrom
pie-legend-animation
Jun 23, 2026
Merged

Add test for Pie and Legend animation#7488
PavelVanecek merged 1 commit into
mainfrom
pie-legend-animation

Conversation

@PavelVanecek

@PavelVanecek PavelVanecek commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Description

So I tried to reproduce the Pie + Legend animation problem and turns out that I accidentally fixed it in #7457. So here is at least a test to prevent regressions. I confirmed I can reproduce in browser and in the unit test before that commit, and cannot reproduce after the commit.

Related Issue

Closes #7254

Summary by CodeRabbit

  • Tests
    • Enhanced test infrastructure for pie chart animations with new helper functions for validating sector paths and angles during animation.
    • Added comprehensive test coverage for pie chart animations when a legend is present, including animation lifecycle callbacks.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Two animation assertion helpers (expectAnimatedPiePaths, expectAnimatedPieAngles) are extracted from Pie.animation.spec.tsx into standalone files under test/helper/. The spec is updated to import them. A new PieWithLegend.animation.spec.tsx suite is added, covering animation callbacks and rendered geometry when a Legend is present.

Changes

Pie Animation Test Helpers and Legend Coverage

Layer / File(s) Summary
Shared expectAnimatedPiePaths and expectAnimatedPieAngles helpers
test/helper/expectAnimatedPiePaths.ts, test/helper/expectAnimatedPieAngles.tsx
Adds two exported async helpers that drive a MockAnimationManager through configurable steps, collecting sector SVG d-path strings or start/end angle objects at each step while asserting geometry changes and attribute stability; final assertions include last-sector endAngle === 0 for the angles helper.
Refactor Pie.animation.spec.tsx to use shared helpers
test/polar/Pie/Pie.animation.spec.tsx
Removes local implementations of both animation helpers (~165 lines), updates imports to pull them from test/helper/, and adds separate imports for expectPieSectorAngles/expectPieSectors.
New PieWithLegend.animation.spec.tsx suite
test/polar/Pie/PieWithLegend.animation.spec.tsx
Adds a Vitest suite rendering a PieChart with a Pie and Legend; tests cover no-sector initial render, onAnimationStart call timing, onAnimationEnd call timing after completeAnimation(), animated d-path snapshots via expectAnimatedPiePaths, and animated angle sequences via expectAnimatedPieAngles.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • ckifer
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is missing several required template sections including Motivation/Context, How It Was Tested, and the Types/Checklist sections, though it does address the Related Issue. Complete the PR description by adding the Motivation and Context section, How This Has Been Tested section, and fill out the Types of changes and Checklist sections from the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a test for Pie and Legend animation interaction.
Linked Issues check ✅ Passed The PR adds test coverage for Pie animation with Legend (#7254), specifically testing that animation runs correctly and onAnimationEnd fires after completion, directly addressing the reported issue.
Out of Scope Changes check ✅ Passed All changes are focused on adding animation tests and refactoring shared test helpers for Pie charts, which are directly related to the linked issue about Pie animation behavior.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pie-legend-animation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@PavelVanecek
PavelVanecek marked this pull request as ready for review June 23, 2026 12:36
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/helper/expectAnimatedPieAngles.tsx`:
- Around line 9-32: The expectAnimatedPieAngles function accepts a steps
parameter that can be negative, which when used to calculate stepSize will cause
the animation progress loop to run indefinitely and hang tests. Add validation
at the beginning of the expectAnimatedPieAngles function to ensure the steps
parameter is positive (greater than 0), similar to validation patterns applied
elsewhere in the codebase, and throw an appropriate error if the validation
fails.

In `@test/helper/expectAnimatedPiePaths.ts`:
- Around line 10-35: The `steps` parameter in this function is unvalidated and
can accept negative values, which causes the loop to run indefinitely since
`stepSize` becomes negative (making `animationProgress` decrease forever while
remaining less than 1). Add validation at the beginning of the function to
ensure `steps` is greater than zero, either by throwing an error for invalid
values or by using an assertion. This validation should occur before the
`stepSize` calculation to prevent the infinite loop condition in the for loop
that iterates while `animationProgress < 1`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dfacb4a7-61aa-494e-a336-b46c7f18d300

📥 Commits

Reviewing files that changed from the base of the PR and between 337edbd and 6edbaa8.

📒 Files selected for processing (4)
  • test/helper/expectAnimatedPieAngles.tsx
  • test/helper/expectAnimatedPiePaths.ts
  • test/polar/Pie/Pie.animation.spec.tsx
  • test/polar/Pie/PieWithLegend.animation.spec.tsx

Comment on lines +9 to +32
steps: number = 5,
): Promise<ReadonlyArray<ReadonlyArray<{ startAngle: number; endAngle: number }>>> {
assertNotNull(container);
/*
* Pie sectors at 0% progress do not render at all, so we
* start from 0.1 so that the sectors have rendered a little bit.
*/
let animationProgress = 0.1;
await animationManager.setAnimationProgress(animationProgress);
const stepSize = (1 - animationProgress) / steps;
const initialPieSectors = selectPieSectors(container);
const initialAngles = getPieSectorAngles(initialPieSectors);
const initialAttributes = Array.from(initialPieSectors).map(sector => ({
fill: sector.getAttribute('fill'),
stroke: sector.getAttribute('stroke'),
}));

const anglesDuringAnimation: { startAngle: number; endAngle: number }[][] = [];
for (animationProgress += stepSize; animationProgress < 1; animationProgress += stepSize) {
// eslint-disable-next-line no-await-in-loop
await animationManager.setAnimationProgress(animationProgress);
const currentPieSectors = selectPieSectors(container);
const currentAngles = getPieSectorAngles(currentPieSectors);
anglesDuringAnimation.push(currentAngles);

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Apply the same steps validation here to avoid hangs.

This helper has the same unbounded progress loop pattern; steps < 0 can keep the loop running forever and block test completion.

Suggested fix
 export async function expectAnimatedPieAngles(
   container: Element,
   animationManager: MockAnimationManager,
   steps: number = 5,
 ): Promise<ReadonlyArray<ReadonlyArray<{ startAngle: number; endAngle: number }>>> {
+  if (!Number.isInteger(steps) || steps <= 0) {
+    throw new Error(`steps must be a positive integer, received: ${steps}`);
+  }
   assertNotNull(container);
@@
-  for (animationProgress += stepSize; animationProgress < 1; animationProgress += stepSize) {
+  for (let i = 1; i < steps; i += 1) {
+    animationProgress = 0.1 + i * stepSize;
     // eslint-disable-next-line no-await-in-loop
     await animationManager.setAnimationProgress(animationProgress);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
steps: number = 5,
): Promise<ReadonlyArray<ReadonlyArray<{ startAngle: number; endAngle: number }>>> {
assertNotNull(container);
/*
* Pie sectors at 0% progress do not render at all, so we
* start from 0.1 so that the sectors have rendered a little bit.
*/
let animationProgress = 0.1;
await animationManager.setAnimationProgress(animationProgress);
const stepSize = (1 - animationProgress) / steps;
const initialPieSectors = selectPieSectors(container);
const initialAngles = getPieSectorAngles(initialPieSectors);
const initialAttributes = Array.from(initialPieSectors).map(sector => ({
fill: sector.getAttribute('fill'),
stroke: sector.getAttribute('stroke'),
}));
const anglesDuringAnimation: { startAngle: number; endAngle: number }[][] = [];
for (animationProgress += stepSize; animationProgress < 1; animationProgress += stepSize) {
// eslint-disable-next-line no-await-in-loop
await animationManager.setAnimationProgress(animationProgress);
const currentPieSectors = selectPieSectors(container);
const currentAngles = getPieSectorAngles(currentPieSectors);
anglesDuringAnimation.push(currentAngles);
export async function expectAnimatedPieAngles(
container: Element,
animationManager: MockAnimationManager,
steps: number = 5,
): Promise<ReadonlyArray<ReadonlyArray<{ startAngle: number; endAngle: number }>>> {
if (!Number.isInteger(steps) || steps <= 0) {
throw new Error(`steps must be a positive integer, received: ${steps}`);
}
assertNotNull(container);
/*
* Pie sectors at 0% progress do not render at all, so we
* start from 0.1 so that the sectors have rendered a little bit.
*/
let animationProgress = 0.1;
await animationManager.setAnimationProgress(animationProgress);
const stepSize = (1 - animationProgress) / steps;
const initialPieSectors = selectPieSectors(container);
const initialAngles = getPieSectorAngles(initialPieSectors);
const initialAttributes = Array.from(initialPieSectors).map(sector => ({
fill: sector.getAttribute('fill'),
stroke: sector.getAttribute('stroke'),
}));
const anglesDuringAnimation: { startAngle: number; endAngle: number }[][] = [];
for (let i = 1; i < steps; i += 1) {
animationProgress = 0.1 + i * stepSize;
// eslint-disable-next-line no-await-in-loop
await animationManager.setAnimationProgress(animationProgress);
const currentPieSectors = selectPieSectors(container);
const currentAngles = getPieSectorAngles(currentPieSectors);
anglesDuringAnimation.push(currentAngles);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/helper/expectAnimatedPieAngles.tsx` around lines 9 - 32, The
expectAnimatedPieAngles function accepts a steps parameter that can be negative,
which when used to calculate stepSize will cause the animation progress loop to
run indefinitely and hang tests. Add validation at the beginning of the
expectAnimatedPieAngles function to ensure the steps parameter is positive
(greater than 0), similar to validation patterns applied elsewhere in the
codebase, and throw an appropriate error if the validation fails.

Comment on lines +10 to +35
steps: number = 5,
): Promise<ReadonlyArray<ReadonlyArray<string>>> {
assertNotNull(container);
/*
* Pie sectors at 0% progress do not render at all, so we
* start from 0.1 so that the sectors have rendered a little bit.
*/
let animationProgress = 0.1;
await animationManager.setAnimationProgress(animationProgress);
const stepSize = (1 - animationProgress) / steps;
const initialPieSectors = selectPieSectors(container);
expect(initialPieSectors.length).toBeGreaterThan(0);
const getD = (sector: Element) => {
const trimmed = trim(sector.getAttribute('d'));
assertNotNull(trimmed);
return trimmed;
};
const initialPathDs = Array.from(initialPieSectors).map(getD);
const initialAttributes = Array.from(initialPieSectors).map(sector => ({
fill: sector.getAttribute('fill'),
stroke: sector.getAttribute('stroke'),
}));

const pathDsDuringAnimation: string[][] = [];
for (animationProgress += stepSize; animationProgress < 1; animationProgress += stepSize) {
// eslint-disable-next-line no-await-in-loop

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard steps to prevent non-terminating animation loops.

steps is unvalidated, so negative values make the loop run indefinitely (animationProgress decreases forever while < 1 stays true). This can hang the test process.

Suggested fix
 export async function expectAnimatedPiePaths(
   container: Element,
   animationManager: MockAnimationManager,
   steps: number = 5,
 ): Promise<ReadonlyArray<ReadonlyArray<string>>> {
+  if (!Number.isInteger(steps) || steps <= 0) {
+    throw new Error(`steps must be a positive integer, received: ${steps}`);
+  }
   assertNotNull(container);
@@
-  for (animationProgress += stepSize; animationProgress < 1; animationProgress += stepSize) {
+  for (let i = 1; i < steps; i += 1) {
+    animationProgress = 0.1 + i * stepSize;
     // eslint-disable-next-line no-await-in-loop
     await animationManager.setAnimationProgress(animationProgress);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/helper/expectAnimatedPiePaths.ts` around lines 10 - 35, The `steps`
parameter in this function is unvalidated and can accept negative values, which
causes the loop to run indefinitely since `stepSize` becomes negative (making
`animationProgress` decrease forever while remaining less than 1). Add
validation at the beginning of the function to ensure `steps` is greater than
zero, either by throwing an error for invalid values or by using an assertion.
This validation should occur before the `stepSize` calculation to prevent the
infinite loop condition in the for loop that iterates while `animationProgress <
1`.

@chromatic-com

chromatic-com Bot commented Jun 23, 2026

Copy link
Copy Markdown

Tip

All tests passed and all changes approved!

🟢 UI Tests: 198 tests unchanged
🟢 UI Review: 198 stories published -- no changes
Storybook icon Storybook Publish: 198 stories published

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.13%. Comparing base (6dbe130) to head (6edbaa8).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7488      +/-   ##
==========================================
+ Coverage   88.06%   88.13%   +0.07%     
==========================================
  Files         604      606       +2     
  Lines       13994    14077      +83     
  Branches     3523     3531       +8     
==========================================
+ Hits        12324    12407      +83     
  Misses       1483     1483              
  Partials      187      187              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown
Contributor

@PavelVanecek
PavelVanecek merged commit ad1590f into main Jun 23, 2026
65 checks passed
@PavelVanecek
PavelVanecek deleted the pie-legend-animation branch June 23, 2026 12:49
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.

Pie animation is interrupted and onAnimationEnd fires immediately when Legend is present

1 participant