Fix policy automations table dropping rows for multi-host automation runs (#50684)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #50683

The policy details page's Automation runs table showed the correct run
count but rendered only one row when a single automation run covered
multiple hosts (e.g. a failing-policies webhook batch): rows are
(activity, host) pairs, so batch rows share an activity id, and the
table's default row.id keying collapsed the duplicates.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

#### Before

<img width="810" height="274" alt="Screenshot 2026-08-06 at 11 36 27 AM"
src="https://github.com/user-attachments/assets/c55a54c0-5d9b-4945-b712-c2449f3a4a8c"
/>


#### After

<img width="1467" height="510" alt="Screenshot 2026-08-06 at 11 23
41 AM"
src="https://github.com/user-attachments/assets/d0e560a3-0270-49aa-989d-0812fd156813"
/>



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed policy automation activity tables so activities with the same ID
are displayed as separate rows for each host.
  * Corrected total run counts shown for host-specific activity rows.


<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Nico
2026-08-06 12:14:42 -03:00
committed by GitHub
parent a6b541d029
commit efa8775ea7
3 changed files with 42 additions and 0 deletions
@@ -0,0 +1 @@
- Fixed the policy automations table showing only one host for automation runs that cover multiple hosts.
@@ -155,6 +155,40 @@ describe("PolicyAutomationsActivitiesTable", () => {
expect(screen.getByPlaceholderText("Search hosts")).toBeInTheDocument();
});
it("renders one row per host for a batch activity sharing an activity id", async () => {
(policiesAPI.getAutomationActivities as jest.Mock).mockResolvedValue(
mockResponse([
mockActivity({
id: 41,
type: ActivityType.RanAutomationWebhook,
details: { policy_id: 123 },
host_id: 1,
host_display_name: "batch-host-a",
}),
mockActivity({
id: 41,
type: ActivityType.RanAutomationWebhook,
details: { policy_id: 123 },
host_id: 2,
host_display_name: "batch-host-b",
}),
])
);
render(
<PolicyAutomationsActivitiesTable
policy={mockPolicy}
currentAutomatedPolicies={[]}
canResetPolicy={false}
/>
);
// Both (activity, host) rows must render even though they share id 41.
expect(await screen.findByText("batch-host-a")).toBeInTheDocument();
expect(screen.getByText("batch-host-b")).toBeInTheDocument();
expect(screen.getByText("2 runs")).toBeInTheDocument();
});
it("shows the Reset policy button only when allowed", async () => {
(policiesAPI.getAutomationActivities as jest.Mock).mockResolvedValue(
mockResponse([mockActivity()], 1)
@@ -259,6 +259,13 @@ const PolicyAutomationsActivitiesTable = ({
<TableContainer
columnConfigs={columnConfigs}
data={data?.activities ?? []}
// Each row is one (activity, host) pair, so batch automations (e.g.
// one webhook POST covering many hosts) return multiple rows sharing
// the same activity id. The default row id (row.id) would collapse
// them into a single rendered row.
getRowId={(row: IPolicyAutomationActivity) =>
`${row.id}-${row.host_id}`
}
isLoading={isLoading}
manualSortBy
pageIndex={page}