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
16 changes: 16 additions & 0 deletions frontend/src/__tests__/dashboard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,22 @@ describe('Dashboard Module', () => {
expect(upcomingList?.innerHTML).toContain('data-action="cancel-purchase"');
});

test('row-level cancel button is the outlined destructive variant, not the filled one', async () => {
(api.getDashboardSummary as jest.Mock).mockResolvedValue({ potential_monthly_savings: 1000, by_service: {} });
(api.getUpcomingPurchases as jest.Mock).mockResolvedValue({
purchases: [{
execution_id: 'exec-1', plan_id: 'plan-1', plan_name: 'Test Plan', provider: 'aws', service: 'ec2',
step_number: 1, total_steps: 4, estimated_savings: 100, scheduled_date: '2024-02-15',
}],
});

await loadDashboard();

const cancelBtn = document.querySelector<HTMLButtonElement>('#upcoming-list [data-action="cancel-purchase"]');
expect(cancelBtn?.classList.contains('btn-danger')).toBe(true);
expect(cancelBtn?.classList.contains('danger')).toBe(false);
});

test('handles missing canvas element gracefully', async () => {
document.body.innerHTML = `
<div id="summary"></div>
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,10 @@ function renderUpcomingPurchases(purchases: UpcomingPurchase[]): void {
const cancelBtn = document.createElement('button');
cancelBtn.dataset['action'] = 'cancel-purchase';
cancelBtn.dataset['id'] = String(p.execution_id);
cancelBtn.className = 'danger';
// Outlined destructive (issue #695): the filled red `danger` style made
// Cancel the loudest element on every row. It still reads as
// destructive and fills on hover; the confirm dialog guards the action.
cancelBtn.className = 'btn-danger';
cancelBtn.textContent = 'Cancel';
actions.appendChild(cancelBtn);
}
Expand Down
16 changes: 16 additions & 0 deletions frontend/tests-e2e/upcoming-purchases-layout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,19 @@ test('the savings label keeps one line without overflowing the card', async ({ p
}
}
});

// Issue #695: the row's Cancel was a filled red block, the loudest element on
// Home. It stays visibly destructive (red border and text) but is outlined,
// so the paint check below runs against the real stylesheet, not the class.
test('the row Cancel button is an outlined destructive action, not a filled block', async ({ page }) => {
await openHome(page);
const cancel = page.locator('.upcoming-card [data-action="cancel-purchase"]').first();
const paint = await cancel.evaluate((el) => {
const cs = getComputedStyle(el);
return { bg: cs.backgroundColor, fg: cs.color, border: cs.borderTopColor };
});
const filledRed = 'rgb(217, 48, 37)'; // --cudly-error
expect(paint.bg).not.toBe(filledRed);
expect(paint.fg).not.toBe('rgb(255, 255, 255)');
expect(paint.border).toBe(filledRed);
});
Loading