mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-21 23:56:39 +00:00
feat: show node and fleet targets on schedule timeline pills (#1480)
Timeline pills and the mobile schedule list now identify what each scheduled run acts on instead of repeating the task name. Pills stay compact (firing time plus a category-aware target) and carry the full detail on hover: - Stack actions show the stack name. - Fleet snapshots show "Entire fleet". - Fleet auto-updates and node-scoped prune/scan show the selected node. - The hover tooltip adds the action label, task name, and node. The mobile list resolves node names too, so prune and scan rows name the node rather than the literal "system". A shared scheduleTargetDescriptor helper removes the target-label logic that was duplicated across the desktop and mobile views. The lifecycle lane is renamed "Stack lifecycle" to match the action-picker category wording.
This commit is contained in:
@@ -306,11 +306,48 @@ describe('ScheduledOperationsView', () => {
|
||||
it('renders the five registry category lanes in the timeline view', async () => {
|
||||
render(<ScheduledOperationsView />);
|
||||
// Timeline is the default view; the lane track always renders.
|
||||
for (const lane of ['Lifecycle', 'Updates', 'Security', 'Maintenance', 'Backups']) {
|
||||
for (const lane of ['Stack lifecycle', 'Updates', 'Security', 'Maintenance', 'Backups']) {
|
||||
expect(await screen.findByText(lane)).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
it('labels timeline pills with a category-aware target and a detailed tooltip', async () => {
|
||||
const soon = Date.now() + 2 * 60 * 60 * 1000;
|
||||
tasksFixture = [
|
||||
makeTask({ id: 1, name: 'Nightly Snapshot', target_type: 'fleet', action: 'snapshot', node_id: null, next_runs: [soon] }),
|
||||
makeTask({ id: 2, name: 'Nightly Prune', target_type: 'system', action: 'prune', node_id: 1, next_runs: [soon] }),
|
||||
];
|
||||
render(<ScheduledOperationsView />);
|
||||
|
||||
// Snapshot pill reads "Entire fleet"; prune pill reads its node name.
|
||||
expect(await screen.findByText('Entire fleet')).toBeInTheDocument();
|
||||
expect(await screen.findByText('hub')).toBeInTheDocument();
|
||||
|
||||
// Tooltips carry the full action label, with the node when the task has one.
|
||||
const prunePill = screen.getByText('hub').closest('button');
|
||||
expect(prunePill).toHaveAttribute('title', expect.stringContaining('Prune Node Resources'));
|
||||
expect(prunePill).toHaveAttribute('title', expect.stringContaining('hub'));
|
||||
const snapshotPill = screen.getByText('Entire fleet').closest('button');
|
||||
expect(snapshotPill).toHaveAttribute('title', expect.stringContaining('Create Fleet Snapshot'));
|
||||
});
|
||||
|
||||
it('names the node on a fleet auto-update pill and composes the full tooltip', async () => {
|
||||
const soon = Date.now() + 2 * 60 * 60 * 1000;
|
||||
const hhmm = (ts: number) => {
|
||||
const d = new Date(ts);
|
||||
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
||||
};
|
||||
tasksFixture = [
|
||||
makeTask({ id: 1, name: 'Fleet Update', target_type: 'fleet', action: 'update', node_id: 1, next_runs: [soon] }),
|
||||
];
|
||||
render(<ScheduledOperationsView />);
|
||||
|
||||
expect(await screen.findByText('All stacks · hub')).toBeInTheDocument();
|
||||
// Tooltip locks the ordered shape: action · name · time · node.
|
||||
const pill = screen.getByText('All stacks · hub').closest('button');
|
||||
expect(pill).toHaveAttribute('title', `Auto-update All Stacks on Node · Fleet Update · ${hhmm(soon)} · hub`);
|
||||
});
|
||||
|
||||
it('offers every registry action in the create picker', async () => {
|
||||
render(<ScheduledOperationsView />);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user