mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-21 15:46:43 +00:00
fix: untag reviewed image names during prune (#1836)
This commit is contained in:
@@ -126,16 +126,25 @@ function PrunePlanPreview({
|
||||
{plan.items.length} {plan.items.length === 1 ? 'item' : 'items'}
|
||||
{plan.reclaimableBytes > 0 ? ` · ${formatBytes(plan.reclaimableBytes)}` : ''}
|
||||
</p>
|
||||
<ul className="max-h-40 overflow-y-auto space-y-1 font-mono text-[12px] text-stat-subtitle/90">
|
||||
{shown.map((item) => (
|
||||
<li key={`${item.target}:${item.id}`} className="block truncate">
|
||||
<span className="text-stat-subtitle/60">{item.target}</span>
|
||||
{' · '}
|
||||
{item.name}
|
||||
{item.sizeBytes != null && item.sizeBytes > 0 ? ` · ${formatBytes(item.sizeBytes)}` : ''}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<ScrollArea type="hover" className="max-h-40">
|
||||
<ul className="space-y-1 font-mono text-[12px] text-stat-subtitle/90">
|
||||
{shown.map((item) => (
|
||||
<li key={`${item.target}:${item.id}`} className="block">
|
||||
<span className="block truncate">
|
||||
<span className="text-stat-subtitle/60">{item.target}</span>
|
||||
{' · '}
|
||||
{item.name}
|
||||
{item.sizeBytes != null && item.sizeBytes > 0 ? ` · ${formatBytes(item.sizeBytes)}` : ''}
|
||||
</span>
|
||||
{item.target === 'images' && item.image.references
|
||||
.filter((ref) => ref !== item.name)
|
||||
.map((ref) => (
|
||||
<span key={ref} className="block break-all">{ref}</span>
|
||||
))}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</ScrollArea>
|
||||
{remaining > 0 && (
|
||||
<p className="text-xs text-stat-subtitle/70">and {remaining} more</p>
|
||||
)}
|
||||
|
||||
@@ -241,6 +241,43 @@ describe('ResourcesView', () => {
|
||||
expect(body.planFingerprint).toBe('fp-test');
|
||||
});
|
||||
|
||||
it('discloses extra repository tags in the prune confirm list', async () => {
|
||||
mockedFetch.mockImplementation((url: string, opts?: RequestInit) => {
|
||||
if (url === '/system/prune/plan' && opts?.method === 'POST') {
|
||||
return Promise.resolve(jsonResponse(samplePrunePlan({
|
||||
items: [{
|
||||
target: 'images',
|
||||
id: 'img-multi',
|
||||
name: 'ghcr.io/example/very-long-app-name:1.5.2-build.1844',
|
||||
sizeBytes: 1000,
|
||||
managed: false,
|
||||
reason: 'Image is not used by any container',
|
||||
image: {
|
||||
references: [
|
||||
'ghcr.io/example/very-long-app-name:1.5.2-build.1844',
|
||||
'ghcr.io/example/very-long-app-name:latest',
|
||||
],
|
||||
},
|
||||
}],
|
||||
})));
|
||||
}
|
||||
if (url === '/system/resources') {
|
||||
return Promise.resolve(jsonResponse({ images: [], volumes: [], networks: [] }));
|
||||
}
|
||||
return Promise.resolve(jsonResponse({}));
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
render(<ResourcesView />);
|
||||
await waitFor(() => expect(mockedFetch).toHaveBeenCalledWith('/system/resources'));
|
||||
await user.click(screen.getByRole('button', { name: /Prune Unused Images/ }));
|
||||
|
||||
const extraRef = await screen.findByText('ghcr.io/example/very-long-app-name:latest');
|
||||
expect(extraRef).toHaveClass('break-all');
|
||||
expect(extraRef).not.toHaveClass('truncate');
|
||||
expect(screen.getByText(/1\.5\.2-build\.1844/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('surfaces the server error on a failed prune instead of a false success (M-2)', async () => {
|
||||
mockedFetch.mockImplementation((url: string, opts?: RequestInit) => {
|
||||
if (url === '/system/prune/plan' && opts?.method === 'POST') {
|
||||
|
||||
Reference in New Issue
Block a user