ccd5897915
F1: single-container "Update now" and bulk "Update" now require PortainerStackUpdate when the resolved path is a stack, disabling the action with a tooltip / skipping it rather than letting the click 403. F2: resolveContainerUpdatePath only matches a Docker Compose stack; a same-named swarm/kubernetes stack is treated as external. F3: SecondaryActions no longer renders an empty ButtonGroup when all of recreate/duplicate/update-now are hidden. F4: bulk update reports an explicit no-op toast and counts containers vs stacks honestly in the success summary. F5: bulk toasts use trimmed container names (no leading slash). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
67 lines
1.8 KiB
TypeScript
67 lines
1.8 KiB
TypeScript
import { Stack, StackType } from '@/react/common/stacks/types';
|
|
import { COMPOSE_STACK_NAME_LABEL } from '@/react/constants';
|
|
|
|
import { groupContainersForUpdate } from './groupContainersForUpdate';
|
|
import { ContainerUpdateContext } from './types';
|
|
|
|
function buildContext(
|
|
overrides: Partial<ContainerUpdateContext>
|
|
): ContainerUpdateContext {
|
|
return {
|
|
id: 'c1',
|
|
name: 'container',
|
|
image: 'nginx:latest',
|
|
environmentId: 3,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
const stack = {
|
|
Id: 7,
|
|
Name: 'my-stack',
|
|
EndpointId: 3,
|
|
Type: StackType.DockerCompose,
|
|
} as Stack;
|
|
|
|
describe('groupContainersForUpdate', () => {
|
|
it('redeploys a stack only once when several of its containers are selected', () => {
|
|
const contexts = [
|
|
buildContext({
|
|
id: 'a',
|
|
labels: { [COMPOSE_STACK_NAME_LABEL]: 'my-stack' },
|
|
}),
|
|
buildContext({
|
|
id: 'b',
|
|
labels: { [COMPOSE_STACK_NAME_LABEL]: 'my-stack' },
|
|
}),
|
|
];
|
|
|
|
const result = groupContainersForUpdate(contexts, [stack]);
|
|
|
|
expect(result.stacks).toHaveLength(1);
|
|
expect(result.stacks[0].stackId).toBe(7);
|
|
expect(result.standalone).toHaveLength(0);
|
|
expect(result.external).toHaveLength(0);
|
|
});
|
|
|
|
it('partitions standalone, stack and external containers', () => {
|
|
const contexts = [
|
|
buildContext({ id: 'standalone', labels: {} }),
|
|
buildContext({
|
|
id: 'stack',
|
|
labels: { [COMPOSE_STACK_NAME_LABEL]: 'my-stack' },
|
|
}),
|
|
buildContext({
|
|
id: 'external',
|
|
labels: { [COMPOSE_STACK_NAME_LABEL]: 'not-in-portainer' },
|
|
}),
|
|
];
|
|
|
|
const result = groupContainersForUpdate(contexts, [stack]);
|
|
|
|
expect(result.standalone.map((c) => c.id)).toEqual(['standalone']);
|
|
expect(result.stacks.map((s) => s.stackId)).toEqual([7]);
|
|
expect(result.external.map((c) => c.id)).toEqual(['external']);
|
|
});
|
|
});
|