mirror of
https://github.com/portainer/portainer.git
synced 2025-08-04 21:35:23 +02:00
chore(data-cy): require data-cy attributes [EE-6880] (#11453)
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
Some checks are pending
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
This commit is contained in:
parent
3cad13388c
commit
d38085a560
538 changed files with 2571 additions and 595 deletions
|
@ -42,6 +42,7 @@ export function NodeSelector({
|
|||
value={value}
|
||||
onChange={onChange}
|
||||
options={nodesQuery.data || []}
|
||||
data-cy="docker-agent-node-selector"
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
|
|
|
@ -36,6 +36,7 @@ function goToParent(onClick: () => void): FileData {
|
|||
color="link"
|
||||
icon={CornerLeftUp}
|
||||
className="!m-0 !p-0"
|
||||
data-cy="component-goToParentButton"
|
||||
>
|
||||
Go to parent
|
||||
</Button>
|
||||
|
@ -95,6 +96,7 @@ export function FilesTable({
|
|||
onDelete,
|
||||
})}
|
||||
disableSelect
|
||||
data-cy="files-datatable"
|
||||
renderTableActions={() => {
|
||||
if (!isUploadAllowed) {
|
||||
return null;
|
||||
|
@ -103,7 +105,12 @@ export function FilesTable({
|
|||
return (
|
||||
<Authorized authorizations="DockerAgentBrowsePut">
|
||||
<div className="flex flex-row items-center">
|
||||
<Button color="light" icon={Upload} as="label">
|
||||
<Button
|
||||
color="light"
|
||||
icon={Upload}
|
||||
as="label"
|
||||
data-cy="docker-agent-file-upload-button"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
className="hidden"
|
||||
|
|
|
@ -26,6 +26,7 @@ export function ActionsCell({
|
|||
onClick={() => meta.onDownload(item.Name)}
|
||||
icon={Download}
|
||||
className="!m-0"
|
||||
data-cy={`docker-files-download-${item.Name}`}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
|
@ -38,6 +39,7 @@ export function ActionsCell({
|
|||
icon={Edit}
|
||||
onClick={() => meta.setIsEdit(item.Name, true)}
|
||||
className="!m-0"
|
||||
data-cy={`docker-files-rename-${item.Name}`}
|
||||
>
|
||||
Rename
|
||||
</Button>
|
||||
|
@ -49,6 +51,7 @@ export function ActionsCell({
|
|||
icon={Trash2}
|
||||
onClick={() => meta.onDelete(item.Name)}
|
||||
className="!m-0"
|
||||
data-cy={`docker-files-delete-${item.Name}`}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
|
|
|
@ -42,6 +42,7 @@ export function NameCell({
|
|||
className="!ml-0 p-0"
|
||||
onClick={() => meta.onBrowse(name)}
|
||||
icon={Folder}
|
||||
data-cy={`file-browse-${name}`}
|
||||
>
|
||||
{name}
|
||||
</Button>
|
||||
|
@ -86,11 +87,22 @@ function EditForm({
|
|||
value={values.name}
|
||||
onChange={(e) => setFieldValue('name', e.target.value)}
|
||||
className="input-sm w-auto"
|
||||
data-cy={`file-rename-${originalName}`}
|
||||
/>
|
||||
|
||||
<Button color="none" type="reset" icon={X} />
|
||||
<Button
|
||||
color="none"
|
||||
type="reset"
|
||||
icon={X}
|
||||
data-cy={`file-reset-button-${originalName}`}
|
||||
/>
|
||||
|
||||
<Button color="none" type="submit" icon={Check} />
|
||||
<Button
|
||||
color="none"
|
||||
type="submit"
|
||||
icon={Check}
|
||||
data-cy={`file-submit-button-${originalName}`}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
|
|
@ -50,19 +50,23 @@ export function ConfigsDatatable({ dataset, onRefresh, onRemoveClick }: Props) {
|
|||
</TableSettingsMenu>
|
||||
)}
|
||||
disableSelect={!hasWriteAccessQuery.authorized}
|
||||
data-cy="configs-datatable"
|
||||
renderTableActions={(selectedRows) =>
|
||||
hasWriteAccessQuery.authorized && (
|
||||
<div className="flex items-center gap-3">
|
||||
<Authorized authorizations="DockerConfigDelete">
|
||||
<DeleteButton
|
||||
disabled={selectedRows.length === 0}
|
||||
data-cy="remove-docker-configs-button"
|
||||
onConfirmed={() => onRemoveClick(selectedRows)}
|
||||
confirmMessage="Do you want to remove the selected config(s)?"
|
||||
/>
|
||||
</Authorized>
|
||||
|
||||
<Authorized authorizations="DockerConfigCreate">
|
||||
<AddButton>Add config</AddButton>
|
||||
<AddButton data-cy="add-docker-config-button">
|
||||
Add config
|
||||
</AddButton>
|
||||
</Authorized>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -10,7 +10,11 @@ import { DockerConfig } from '../../types';
|
|||
const columnHelper = createColumnHelper<DockerConfig>();
|
||||
|
||||
export const columns = [
|
||||
buildNameColumn<DockerConfig>('Name', 'docker.configs.config'),
|
||||
buildNameColumn<DockerConfig>(
|
||||
'Name',
|
||||
'docker.configs.config',
|
||||
'docker-configs-name'
|
||||
),
|
||||
columnHelper.accessor('CreatedAt', {
|
||||
header: 'Creation Date',
|
||||
cell: ({ getValue }) => {
|
||||
|
|
|
@ -84,6 +84,7 @@ export function BaseForm({
|
|||
setFieldValue('name', name);
|
||||
}}
|
||||
placeholder="e.g. myContainer"
|
||||
data-cy="container-name-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -108,6 +109,7 @@ export function BaseForm({
|
|||
setFieldValue('alwaysPull', alwaysPull)
|
||||
}
|
||||
labelClass="col-sm-3 col-lg-2"
|
||||
data-cy="always-pull-switch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -121,6 +123,7 @@ export function BaseForm({
|
|||
<div className="col-sm-12">
|
||||
<SwitchField
|
||||
label="Create a container webhook"
|
||||
data-cy="container-webhook-switch"
|
||||
tooltip="Create a webhook (or callback URI) to automate the recreate this container. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and recreate this container."
|
||||
checked={values.enableWebhook}
|
||||
onChange={(enableWebhook) =>
|
||||
|
@ -140,6 +143,7 @@ export function BaseForm({
|
|||
<div className="col-sm-12">
|
||||
<SwitchField
|
||||
label="Publish all exposed ports to random host ports"
|
||||
data-cy="publish-all-ports-switch"
|
||||
tooltip="When enabled, Portainer will let Docker automatically map a random port on the host to each one defined in the image Dockerfile."
|
||||
checked={values.publishAllPorts}
|
||||
onChange={(publishAllPorts) =>
|
||||
|
@ -179,6 +183,7 @@ export function BaseForm({
|
|||
<div className="col-sm-12">
|
||||
<SwitchField
|
||||
label="Auto remove"
|
||||
data-cy="container-auto-remove-switch"
|
||||
tooltip="When enabled, Portainer will automatically remove the container when it exits. This is useful when you want to use the container only once."
|
||||
checked={values.autoRemove}
|
||||
onChange={(autoRemove) => setFieldValue('autoRemove', autoRemove)}
|
||||
|
@ -191,6 +196,7 @@ export function BaseForm({
|
|||
<div className="col-sm-12">
|
||||
<LoadingButton
|
||||
loadingText="Deployment in progress..."
|
||||
data-cy="deploy-container-button"
|
||||
isLoading={isLoading}
|
||||
disabled={!isValid}
|
||||
>
|
||||
|
|
|
@ -50,6 +50,7 @@ export function PortsMappingField({
|
|||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
tooltip="When a range of ports on the host and a single port on the container is specified, Docker will randomly choose a single available port in the defined range and forward that to the container port."
|
||||
data-cy="docker-containers-ports-mapping"
|
||||
/>
|
||||
{typeof errors === 'string' && (
|
||||
<div className="form-group col-md-12">
|
||||
|
@ -73,6 +74,7 @@ function Item({
|
|||
<div className="flex items-center gap-2">
|
||||
<InputLabeled
|
||||
size="small"
|
||||
data-cy={`hostPort-${index}`}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
value={item.hostPort}
|
||||
|
@ -97,6 +99,7 @@ function Item({
|
|||
placeholder="e.g. 80"
|
||||
className="w-1/2"
|
||||
id={`containerPort-${index}`}
|
||||
data-cy={`containerPort-${index}`}
|
||||
/>
|
||||
|
||||
<ButtonSelector<Protocol>
|
||||
|
|
|
@ -19,6 +19,7 @@ export function CapabilitiesTab({
|
|||
<div key={cap.key} className="w-1/3 text-center">
|
||||
<SwitchField
|
||||
labelClass="col-sm-6"
|
||||
data-cy="docker-container-capability-switch"
|
||||
tooltip={cap.description}
|
||||
checked={values.includes(cap.key)}
|
||||
label={cap.key}
|
||||
|
|
|
@ -61,6 +61,7 @@ export function CommandsTab({
|
|||
value={values.workingDir}
|
||||
onChange={(e) => setFieldValue('workingDir', e.target.value)}
|
||||
placeholder="e.g. /myapp"
|
||||
data-cy="working-dir-input"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
|
@ -73,6 +74,7 @@ export function CommandsTab({
|
|||
value={values.user}
|
||||
onChange={(e) => setFieldValue('user', e.target.value)}
|
||||
placeholder="e.g. nginx"
|
||||
data-cy="user-input"
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,8 @@ import { ReactNode } from 'react';
|
|||
import { mixed } from 'yup';
|
||||
import { ContainerConfig } from 'docker-types/generated/1.41';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { FormControl } from '@@/form-components/FormControl';
|
||||
|
||||
const consoleSettingTypes = ['tty', 'interactive', 'both', 'none'] as const;
|
||||
|
@ -28,6 +30,7 @@ export function ConsoleSettings({
|
|||
</>
|
||||
}
|
||||
selected={value}
|
||||
data-cy="container-console-interactive-tty"
|
||||
/>
|
||||
<Item
|
||||
value="interactive"
|
||||
|
@ -38,6 +41,7 @@ export function ConsoleSettings({
|
|||
</>
|
||||
}
|
||||
selected={value}
|
||||
data-cy="container-console-interactive"
|
||||
/>
|
||||
<Item
|
||||
value="tty"
|
||||
|
@ -48,12 +52,14 @@ export function ConsoleSettings({
|
|||
</>
|
||||
}
|
||||
selected={value}
|
||||
data-cy="container-console-tty"
|
||||
/>
|
||||
<Item
|
||||
value="none"
|
||||
onChange={handleChange}
|
||||
label={<>None</>}
|
||||
selected={value}
|
||||
data-cy="container-console-none"
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
|
@ -68,16 +74,18 @@ function Item({
|
|||
selected,
|
||||
onChange,
|
||||
label,
|
||||
'data-cy': dataCy,
|
||||
}: {
|
||||
value: ConsoleSetting;
|
||||
selected: ConsoleSetting;
|
||||
onChange(value: ConsoleSetting): void;
|
||||
label: ReactNode;
|
||||
}) {
|
||||
} & AutomationTestingProps) {
|
||||
return (
|
||||
<label className="radio-inline !m-0 w-1/2">
|
||||
<input
|
||||
type="radio"
|
||||
data-cy={dataCy}
|
||||
name="container_console"
|
||||
value={value}
|
||||
checked={value === selected}
|
||||
|
|
|
@ -52,6 +52,7 @@ export function LoggerConfig({
|
|||
value={value.type}
|
||||
onChange={(type) => onChange({ ...value, type: type || '' })}
|
||||
options={pluginOptions}
|
||||
data-cy="docker-logging-driver-selector"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -82,6 +83,7 @@ export function LoggerConfig({
|
|||
itemBuilder={() => ({ option: '', value: '' })}
|
||||
disabled={isDisabled}
|
||||
errors={errors?.options}
|
||||
data-cy="docker-logging-options"
|
||||
/>
|
||||
</FormSection>
|
||||
);
|
||||
|
@ -95,6 +97,7 @@ function Item({
|
|||
item: { option, value },
|
||||
onChange,
|
||||
error,
|
||||
index,
|
||||
}: ItemProps<{ option: string; value: string }>) {
|
||||
return (
|
||||
<div>
|
||||
|
@ -105,6 +108,7 @@ function Item({
|
|||
value={option}
|
||||
onChange={(e) => handleChange({ option: e.target.value })}
|
||||
placeholder="e.g. FOO"
|
||||
data-cy={`docker-logging-option_${index}`}
|
||||
/>
|
||||
</InputGroup>
|
||||
<InputGroup className="w-1/2">
|
||||
|
@ -113,6 +117,7 @@ function Item({
|
|||
value={value}
|
||||
onChange={(e) => handleChange({ value: e.target.value })}
|
||||
placeholder="e.g bar"
|
||||
data-cy={`docker-logging-value_${index}`}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
|
|
|
@ -21,6 +21,7 @@ export function OverridableInput({
|
|||
<InputGroup.ButtonWrapper>
|
||||
<Button
|
||||
color="light"
|
||||
data-cy={`docker-container-default-${id}`}
|
||||
size="medium"
|
||||
className={clsx('!ml-0', { active: !override })}
|
||||
onClick={() => onChange(null)}
|
||||
|
@ -29,6 +30,7 @@ export function OverridableInput({
|
|||
</Button>
|
||||
<Button
|
||||
color="light"
|
||||
data-cy={`docker-container-override-${id}`}
|
||||
size="medium"
|
||||
className={clsx({ active: override })}
|
||||
onClick={() => onChange('')}
|
||||
|
@ -38,6 +40,7 @@ export function OverridableInput({
|
|||
</InputGroup.ButtonWrapper>
|
||||
<InputGroup.Input
|
||||
disabled={!override}
|
||||
data-cy={`docker-container-input-${id}`}
|
||||
value={value || ''}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
id={id}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ItemProps } from '@@/form-components/InputList';
|
|||
|
||||
import { Label } from './types';
|
||||
|
||||
export function Item({ item, onChange, error }: ItemProps<Label>) {
|
||||
export function Item({ item, onChange, error, index }: ItemProps<Label>) {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex w-full gap-4">
|
||||
|
@ -12,6 +12,7 @@ export function Item({ item, onChange, error }: ItemProps<Label>) {
|
|||
<InputGroup.Addon>name</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={item.name}
|
||||
data-cy={`label-name_${index}`}
|
||||
onChange={(e) => onChange({ ...item, name: e.target.value })}
|
||||
placeholder="e.g. com.example.foo"
|
||||
/>
|
||||
|
@ -20,6 +21,7 @@ export function Item({ item, onChange, error }: ItemProps<Label>) {
|
|||
<InputGroup.Addon>value</InputGroup.Addon>
|
||||
<InputGroup.Input
|
||||
value={item.value}
|
||||
data-cy={`label-value${index}`}
|
||||
onChange={(e) => onChange({ ...item, value: e.target.value })}
|
||||
placeholder="e.g. bar"
|
||||
/>
|
||||
|
|
|
@ -21,6 +21,7 @@ export function LabelsTab({
|
|||
value={values}
|
||||
item={Item}
|
||||
itemBuilder={() => ({ name: '', value: '' })}
|
||||
data-cy="docker-container-labels"
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ export function ContainerSelector({
|
|||
onChange={onChange}
|
||||
options={containersQuery.data || []}
|
||||
isLoading={containersQuery.isLoading}
|
||||
data-cy="docker-container-selector"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ export function NetworkTab({
|
|||
value={values.hostname}
|
||||
onChange={(e) => setFieldValue('hostname', e.target.value)}
|
||||
placeholder="e.g. web01"
|
||||
data-cy="docker-container-hostname-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -52,6 +53,7 @@ export function NetworkTab({
|
|||
value={values.domain}
|
||||
onChange={(e) => setFieldValue('domain', e.target.value)}
|
||||
placeholder="e.g. example.com"
|
||||
data-cy="docker-container-domain-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -60,6 +62,7 @@ export function NetworkTab({
|
|||
value={values.macAddress}
|
||||
onChange={(e) => setFieldValue('macAddress', e.target.value)}
|
||||
placeholder="e.g. 12-34-56-78-9a-bc"
|
||||
data-cy="docker-container-mac-address-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -68,6 +71,7 @@ export function NetworkTab({
|
|||
value={values.ipv4Address}
|
||||
onChange={(e) => setFieldValue('ipv4Address', e.target.value)}
|
||||
placeholder="e.g. 172.20.0.7"
|
||||
data-cy="docker-container-ipv4-address-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -76,6 +80,7 @@ export function NetworkTab({
|
|||
value={values.ipv6Address}
|
||||
onChange={(e) => setFieldValue('ipv6Address', e.target.value)}
|
||||
placeholder="e.g. a:b:c:d::1234"
|
||||
data-cy="docker-container-ipv6-address-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -84,6 +89,7 @@ export function NetworkTab({
|
|||
value={values.primaryDns}
|
||||
onChange={(e) => setFieldValue('primaryDns', e.target.value)}
|
||||
placeholder="e.g. 1.1.1.1, 2606:4700:4700::1111"
|
||||
data-cy="docker-container-primary-dns-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -92,6 +98,7 @@ export function NetworkTab({
|
|||
value={values.secondaryDns}
|
||||
onChange={(e) => setFieldValue('secondaryDns', e.target.value)}
|
||||
placeholder="e.g. 1.0.0.1, 2606:4700:4700::1001"
|
||||
data-cy="docker-container-secondary-dns-input"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
@ -104,6 +111,7 @@ export function NetworkTab({
|
|||
errors={errors?.hostsFileEntries}
|
||||
item={HostsFileEntryItem}
|
||||
itemBuilder={() => ''}
|
||||
data-cy="docker-container-hosts-file-entries"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -115,6 +123,7 @@ function HostsFileEntryItem({
|
|||
disabled,
|
||||
error,
|
||||
readOnly,
|
||||
index,
|
||||
}: ItemProps<string>) {
|
||||
return (
|
||||
<div>
|
||||
|
@ -125,6 +134,7 @@ function HostsFileEntryItem({
|
|||
onChange={(e) => onChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
data-cy={`docker-container-hosts-file-entry_${index}`}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
|
|
|
@ -31,16 +31,18 @@ export function DevicesField({
|
|||
label="Devices"
|
||||
errors={errors}
|
||||
itemBuilder={() => ({ pathOnHost: '', pathInContainer: '' })}
|
||||
data-cy="docker-container-devices"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function Item({ item, onChange, error }: ItemProps<Device>) {
|
||||
function Item({ item, onChange, error, index }: ItemProps<Device>) {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex w-full gap-4">
|
||||
<InputLabeled
|
||||
value={item.pathOnHost}
|
||||
data-cy={`device-path-on-host_${index}`}
|
||||
onChange={(e) => onChange({ ...item, pathOnHost: e.target.value })}
|
||||
label="host"
|
||||
placeholder="e.g. /dev/tty0"
|
||||
|
@ -49,6 +51,7 @@ function Item({ item, onChange, error }: ItemProps<Device>) {
|
|||
/>
|
||||
<InputLabeled
|
||||
value={item.pathInContainer}
|
||||
data-cy={`device-path-on-container_${index}`}
|
||||
onChange={(e) =>
|
||||
onChange({ ...item, pathInContainer: e.target.value })
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ export function EditResourcesForm({
|
|||
<div className="col-sm-12 flex items-center gap-4">
|
||||
<LoadingButton
|
||||
isLoading={updateMutation.isLoading}
|
||||
data-cy="update-limits-button"
|
||||
disabled={isImageInvalid || !dirty}
|
||||
loadingText="Update in progress..."
|
||||
type="button"
|
||||
|
|
|
@ -136,6 +136,7 @@ export function GpuFieldset({
|
|||
onChange={toggleEnableGpu}
|
||||
className="ml-2"
|
||||
disabled={!enableGpuManagement}
|
||||
data-cy="docker-containers-gpu-enabled-switch"
|
||||
/>
|
||||
</div>
|
||||
{enableGpuManagement && values.enabled && (
|
||||
|
@ -150,6 +151,7 @@ export function GpuFieldset({
|
|||
onChange={onChangeSelectedGpus}
|
||||
options={options}
|
||||
components={{ MultiValueRemove }}
|
||||
data-cy="docker-containers-gpu-select"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -170,6 +172,7 @@ export function GpuFieldset({
|
|||
options={NvidiaCapabilitiesOptions}
|
||||
components={{ Option }}
|
||||
onChange={onChangeSelectedCaps}
|
||||
data-cy="docker-containers-gpu-capabilities-select"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -69,6 +69,7 @@ export function ResourceFieldset({
|
|||
min={0}
|
||||
max={maxCpu}
|
||||
step={0.1}
|
||||
dataCy="k8sNamespaceCreate-resourceCpu"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormSection>
|
||||
|
|
|
@ -99,6 +99,7 @@ export function ResourcesTab({
|
|||
setFieldValue('sharedMemorySize', e.target.valueAsNumber)
|
||||
}
|
||||
className="w-32"
|
||||
data-cy="shared-memory-size"
|
||||
/>
|
||||
<div className="small text-muted">
|
||||
Size of /dev/shm (<b>MB</b>)
|
||||
|
|
|
@ -30,6 +30,7 @@ export function RuntimeSection({
|
|||
<div className="col-sm-12">
|
||||
<SwitchField
|
||||
labelClass="col-sm-2"
|
||||
data-cy="docker-privileged-switch"
|
||||
label="Privileged mode"
|
||||
checked={values.privileged}
|
||||
onChange={(privileged) => handleChange({ privileged })}
|
||||
|
@ -43,6 +44,7 @@ export function RuntimeSection({
|
|||
<div className="col-sm-12">
|
||||
<SwitchField
|
||||
labelClass="col-sm-2"
|
||||
data-cy="docker-init-switch"
|
||||
label="Init"
|
||||
checked={values.init}
|
||||
onChange={(init) => handleChange({ init })}
|
||||
|
|
|
@ -26,6 +26,7 @@ export function RuntimeSelector({
|
|||
options={infoQuery.data || []}
|
||||
isLoading={infoQuery.isLoading}
|
||||
disabled={infoQuery.isLoading}
|
||||
data-cy="docker-runtime-selector"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -30,11 +30,12 @@ export function SysctlsField({
|
|||
label="Sysctls"
|
||||
errors={errors}
|
||||
itemBuilder={() => ({ name: '', value: '' })}
|
||||
data-cy="docker-container-sysctls"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function Item({ item, onChange, error }: ItemProps<Sysctls>) {
|
||||
function Item({ item, onChange, error, index }: ItemProps<Sysctls>) {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex w-full gap-4">
|
||||
|
@ -45,6 +46,7 @@ function Item({ item, onChange, error }: ItemProps<Sysctls>) {
|
|||
placeholder="e.g. FOO"
|
||||
className="w-1/2"
|
||||
size="small"
|
||||
data-cy={`docker-container-sysctl-name_${index}`}
|
||||
/>
|
||||
<InputLabeled
|
||||
value={item.value}
|
||||
|
@ -53,6 +55,7 @@ function Item({ item, onChange, error }: ItemProps<Sysctls>) {
|
|||
placeholder="e.g. bar"
|
||||
className="w-1/2"
|
||||
size="small"
|
||||
data-cy={`docker-container-sysctl-value_${index}`}
|
||||
/>
|
||||
</div>
|
||||
{error && <FormError>{Object.values(error)[0]}</FormError>}
|
||||
|
|
|
@ -31,6 +31,7 @@ export function Item({
|
|||
size="small"
|
||||
className="flex-1"
|
||||
id={`container-path-${index}`}
|
||||
data-cy={`container-path_${index}`}
|
||||
/>
|
||||
|
||||
{allowBindMounts && (
|
||||
|
@ -73,6 +74,7 @@ export function Item({
|
|||
value={volume.name}
|
||||
onChange={(e) => setValue({ name: e.target.value })}
|
||||
id={`host-path-${index}`}
|
||||
data-cy={`host-path_${index}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ export function VolumeSelector({
|
|||
value={selectedValue}
|
||||
onChange={(vol) => onChange(vol?.Name)}
|
||||
inputId={inputId}
|
||||
data-cy="docker-containers-volume-selector"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ export function VolumesTab({
|
|||
name: '',
|
||||
readOnly: false,
|
||||
})}
|
||||
data-cy="docker-container-volumes"
|
||||
/>
|
||||
</InputContext.Provider>
|
||||
);
|
||||
|
|
|
@ -29,6 +29,7 @@ function ConfirmRecreationModal({ onSubmit, cannotPullImage }: Props) {
|
|||
</p>
|
||||
<SwitchField
|
||||
name="pullLatest"
|
||||
data-cy="recreate-pull-latest-switch"
|
||||
label="Re-pull image"
|
||||
checked={pullLatest}
|
||||
onChange={setPullLatest}
|
||||
|
@ -44,10 +45,18 @@ function ConfirmRecreationModal({ onSubmit, cannotPullImage }: Props) {
|
|||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button onClick={() => onSubmit()} color="default">
|
||||
<Button
|
||||
onClick={() => onSubmit()}
|
||||
color="default"
|
||||
data-cy="cancel-recreate"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => onSubmit({ pullLatest })} color="danger">
|
||||
<Button
|
||||
onClick={() => onSubmit({ pullLatest })}
|
||||
color="danger"
|
||||
data-cy="confirm-recreate"
|
||||
>
|
||||
Recreate
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
|
|
|
@ -55,6 +55,7 @@ export function ConnectNetworkForm({
|
|||
</div>
|
||||
<LoadingButton
|
||||
loadingText="Joining network..."
|
||||
data-cy="connect-network-button"
|
||||
isLoading={connectMutation.isLoading}
|
||||
>
|
||||
Join Network
|
||||
|
|
|
@ -62,6 +62,7 @@ export function ContainerNetworksDatatable({
|
|||
table: 'container-networks',
|
||||
containerId: container.Id,
|
||||
})}
|
||||
data-cy="container-networks-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ function Cell({
|
|||
<Authorized authorizations="DockerNetworkDisconnect">
|
||||
<LoadingButton
|
||||
color="dangerlight"
|
||||
data-cy="disconnect-network-button"
|
||||
isLoading={disconnectMutation.isLoading}
|
||||
loadingText="Leaving network..."
|
||||
type="button"
|
||||
|
|
|
@ -8,7 +8,11 @@ import { actions } from './actions';
|
|||
export const columns = [
|
||||
buildExpandColumn<TableNetwork>(),
|
||||
{
|
||||
...buildNameColumn<TableNetwork>('name', 'docker.networks.network'),
|
||||
...buildNameColumn<TableNetwork>(
|
||||
'name',
|
||||
'docker.networks.network',
|
||||
'docker-networks-name'
|
||||
),
|
||||
header: 'Network',
|
||||
},
|
||||
columnHelper.accessor((item) => item.IPAddress || '-', {
|
||||
|
|
|
@ -25,7 +25,7 @@ export function HealthStatus({ health }: Props) {
|
|||
<TableContainer>
|
||||
<TableTitle label="Container health" icon={Server} />
|
||||
|
||||
<DetailsTable>
|
||||
<DetailsTable dataCy="health-status-table">
|
||||
<DetailsTable.Row label="Status">
|
||||
<div className="vertical-center">
|
||||
<Icon
|
||||
|
|
|
@ -70,6 +70,7 @@ export function ContainersDatatable({
|
|||
isLoading={containersQuery.isLoading}
|
||||
isRowSelectable={(row) => !row.original.IsPortainer}
|
||||
initialTableState={getColumnVisibilityState(tableState.hiddenColumns)}
|
||||
data-cy="docker-containers-datatable"
|
||||
renderTableSettings={(tableInstance) => (
|
||||
<>
|
||||
<ColumnVisibilityMenu<DockerContainer>
|
||||
|
|
|
@ -82,6 +82,7 @@ export function ContainersDatatableActions({
|
|||
<Authorized authorizations="DockerContainerStart">
|
||||
<Button
|
||||
color="light"
|
||||
data-cy="start-docker-container-button"
|
||||
onClick={() => onStartClick(selectedItems)}
|
||||
disabled={selectedItemCount === 0 || !hasStoppedItemsSelected}
|
||||
icon={Play}
|
||||
|
@ -93,6 +94,7 @@ export function ContainersDatatableActions({
|
|||
<Authorized authorizations="DockerContainerStop">
|
||||
<Button
|
||||
color="light"
|
||||
data-cy="stop-docker-container-button"
|
||||
onClick={() => onStopClick(selectedItems)}
|
||||
disabled={selectedItemCount === 0 || !hasRunningItemsSelected}
|
||||
icon={Square}
|
||||
|
@ -104,6 +106,7 @@ export function ContainersDatatableActions({
|
|||
<Authorized authorizations="DockerContainerKill">
|
||||
<Button
|
||||
color="light"
|
||||
data-cy="kill-docker-container-button"
|
||||
onClick={() => onKillClick(selectedItems)}
|
||||
disabled={selectedItemCount === 0 || hasStoppedItemsSelected}
|
||||
icon={Slash}
|
||||
|
@ -115,6 +118,7 @@ export function ContainersDatatableActions({
|
|||
<Authorized authorizations="DockerContainerRestart">
|
||||
<Button
|
||||
color="light"
|
||||
data-cy="restart-docker-container-button"
|
||||
onClick={() => onRestartClick(selectedItems)}
|
||||
disabled={selectedItemCount === 0}
|
||||
icon={RefreshCw}
|
||||
|
@ -126,6 +130,7 @@ export function ContainersDatatableActions({
|
|||
<Authorized authorizations="DockerContainerPause">
|
||||
<Button
|
||||
color="light"
|
||||
data-cy="pause-docker-container-button"
|
||||
onClick={() => onPauseClick(selectedItems)}
|
||||
disabled={selectedItemCount === 0 || !hasRunningItemsSelected}
|
||||
icon={Pause}
|
||||
|
@ -137,6 +142,7 @@ export function ContainersDatatableActions({
|
|||
<Authorized authorizations="DockerContainerUnpause">
|
||||
<Button
|
||||
color="light"
|
||||
data-cy="resume-docker-container-button"
|
||||
onClick={() => onResumeClick(selectedItems)}
|
||||
disabled={selectedItemCount === 0 || !hasPausedItemsSelected}
|
||||
icon={Play}
|
||||
|
@ -148,6 +154,7 @@ export function ContainersDatatableActions({
|
|||
<Authorized authorizations="DockerContainerDelete">
|
||||
<Button
|
||||
color="dangerlight"
|
||||
data-cy="remove-docker-container-button"
|
||||
onClick={() => onRemoveClick(selectedItems)}
|
||||
disabled={selectedItemCount === 0}
|
||||
icon={Trash2}
|
||||
|
@ -159,7 +166,9 @@ export function ContainersDatatableActions({
|
|||
{isAddActionVisible && (
|
||||
<div className="space-left">
|
||||
<Authorized authorizations="DockerContainerCreate">
|
||||
<AddButton>Add container</AddButton>
|
||||
<AddButton data-cy="add-docker-container-button">
|
||||
Add container
|
||||
</AddButton>
|
||||
</Authorized>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -16,7 +16,8 @@ export function ContainersDatatableSettings({
|
|||
return (
|
||||
<>
|
||||
<Checkbox
|
||||
id="settings-container-truncate-nae"
|
||||
id="settings-container-truncate-name"
|
||||
data-cy="settings-container-truncate-name"
|
||||
label="Truncate container name"
|
||||
checked={settings.truncateContainerName > 0}
|
||||
onChange={() =>
|
||||
|
|
|
@ -36,6 +36,7 @@ function LogsDisabledInfoPanel() {
|
|||
<Link
|
||||
to="docker.containers.new"
|
||||
params={{ from: containerId, nodeName }}
|
||||
data-cy="redeploy-container-link"
|
||||
>
|
||||
redeploy your container
|
||||
</Link>{' '}
|
||||
|
|
|
@ -53,6 +53,7 @@ export function ProcessesDatatable({
|
|||
disableSelect
|
||||
isLoading={!dataset}
|
||||
emptyContentLabel="No processes found."
|
||||
data-cy="docker-container-stats-processes-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ export async function confirmContainerDeletion(title: string) {
|
|||
{
|
||||
confirmButton: buildConfirmButton('Remove', 'danger'),
|
||||
modalType: ModalType.Destructive,
|
||||
'data-cy': 'confirm-container-delete-button',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ export function ContainerQuickActions({
|
|||
to="docker.containers.container.logs"
|
||||
params={{ id: containerId, nodeName }}
|
||||
title="Logs"
|
||||
data-cy={`container-logs-${containerId}`}
|
||||
>
|
||||
<Icon icon={FileText} className="space-right" />
|
||||
</Link>
|
||||
|
@ -57,6 +58,7 @@ export function ContainerQuickActions({
|
|||
to="docker.containers.container.inspect"
|
||||
params={{ id: containerId, nodeName }}
|
||||
title="Inspect"
|
||||
data-cy={`container-inspect-${containerId}`}
|
||||
>
|
||||
<Icon icon={Info} className="space-right" />
|
||||
</Link>
|
||||
|
@ -69,6 +71,7 @@ export function ContainerQuickActions({
|
|||
to="docker.containers.container.stats"
|
||||
params={{ id: containerId, nodeName }}
|
||||
title="Stats"
|
||||
data-cy={`container-stats-${containerId}`}
|
||||
>
|
||||
<Icon icon={BarChart} className="space-right" />
|
||||
</Link>
|
||||
|
@ -81,6 +84,7 @@ export function ContainerQuickActions({
|
|||
to="docker.containers.container.exec"
|
||||
params={{ id: containerId, nodeName }}
|
||||
title="Exec Console"
|
||||
data-cy={`container-exec-${containerId}`}
|
||||
>
|
||||
<Icon icon={Terminal} className="space-right" />
|
||||
</Link>
|
||||
|
@ -93,6 +97,7 @@ export function ContainerQuickActions({
|
|||
to="docker.containers.container.attach"
|
||||
params={{ id: containerId, nodeName }}
|
||||
title="Attach Console"
|
||||
data-cy={`container-attach-${containerId}`}
|
||||
>
|
||||
<Icon icon={Paperclip} className="space-right" />
|
||||
</Link>
|
||||
|
|
|
@ -44,6 +44,7 @@ export function NetworkSelector({
|
|||
isLoading={networksQuery.isLoading}
|
||||
bindToBody
|
||||
placeholder="Select a network"
|
||||
data-cy="docker-network-selector"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ export function EventsDatatable({ dataset }: { dataset: Array<DockerEvent> }) {
|
|||
titleIcon={Clock}
|
||||
disableSelect
|
||||
emptyContentLabel="No event available."
|
||||
data-cy="docker-events-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ interface Props {
|
|||
onChange(value: Gpu[]): void;
|
||||
}
|
||||
|
||||
function Item({ item, onChange }: ItemProps<Gpu>) {
|
||||
function Item({ item, onChange, index }: ItemProps<Gpu>) {
|
||||
return (
|
||||
<div className="flex flex-grow gap-2">
|
||||
<InputGroup size="small" className="flex-grow">
|
||||
|
@ -28,6 +28,7 @@ function Item({ item, onChange }: ItemProps<Gpu>) {
|
|||
onChange={(e) => {
|
||||
onChange({ ...item, name: e.target.value });
|
||||
}}
|
||||
data-cy={`docker-gpu-name_${index}`}
|
||||
/>
|
||||
</InputGroup>
|
||||
|
||||
|
@ -39,6 +40,7 @@ function Item({ item, onChange }: ItemProps<Gpu>) {
|
|||
onChange={(e) => {
|
||||
onChange({ ...item, value: e.target.value });
|
||||
}}
|
||||
data-cy={`docker-gpu-value_${index}`}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
|
@ -55,6 +57,7 @@ export function GpusList({ value, onChange }: Props) {
|
|||
itemBuilder={() => ({ value: '', name: '' })}
|
||||
addLabel="Add GPU"
|
||||
item={Item}
|
||||
data-cy="docker-containers-gpus"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ export function DockerfileDetails({ image }: Props) {
|
|||
return (
|
||||
<TableContainer>
|
||||
<TableTitle label="Dockerfile details" icon={List} />
|
||||
<DetailsTable>
|
||||
<DetailsTable dataCy="dockerfile-details-table">
|
||||
<DetailsTable.Row label="CMD">
|
||||
<code>{image.Command ? joinCommand(image.Command) : '-'}</code>
|
||||
</DetailsTable.Row>
|
||||
|
|
|
@ -26,13 +26,22 @@ function RegistrySelectPrompt({ onSubmit, defaultValue, registries }: Props) {
|
|||
onChange={setRegistryId}
|
||||
value={registryId}
|
||||
options={options}
|
||||
data-cy="registry-select-selector"
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button onClick={() => onSubmit()} color="default">
|
||||
<Button
|
||||
onClick={() => onSubmit()}
|
||||
color="default"
|
||||
data-cy="registry-select-cancel-button"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={() => onSubmit(registryId)} color="primary">
|
||||
<Button
|
||||
onClick={() => onSubmit(registryId)}
|
||||
color="primary"
|
||||
data-cy="registry-select-update-button"
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
|
|
|
@ -64,6 +64,7 @@ export function ImagesDatatable({
|
|||
<Datatable
|
||||
title="Images"
|
||||
titleIcon={List}
|
||||
data-cy="docker-images-datatable"
|
||||
renderTableActions={(selectedItems) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<RemoveButtonMenu selectedItems={selectedItems} onRemove={onRemove} />
|
||||
|
@ -130,6 +131,7 @@ function RemoveButtonMenu({
|
|||
color="dangerlight"
|
||||
disabled={selectedItems.length === 0}
|
||||
icon={ChevronDown}
|
||||
data-cy="image-toggleRemoveButtonMenu"
|
||||
>
|
||||
<span className="sr-only">Toggle Dropdown</span>
|
||||
</MenuButton>
|
||||
|
@ -169,7 +171,10 @@ function ImportExportButtons({
|
|||
data-cy="image-importImageButton"
|
||||
icon={Upload}
|
||||
disabled={isExportInProgress}
|
||||
props={{ to: 'docker.images.import' }}
|
||||
props={{
|
||||
to: 'docker.images.import',
|
||||
'data-cy': 'image-importImageLink',
|
||||
}}
|
||||
>
|
||||
Import
|
||||
</Button>
|
||||
|
|
|
@ -39,6 +39,7 @@ export function MacvlanNodesSelector({
|
|||
isLoading={!dataset}
|
||||
emptyContentLabel="No node available"
|
||||
settingsManager={tableState}
|
||||
data-cy="macvlan-nodes-selector-datatable"
|
||||
extendTableOptions={mergeOptions(
|
||||
withMeta({
|
||||
table: 'nodes',
|
||||
|
|
|
@ -56,6 +56,7 @@ export function NetworkContainersTable({
|
|||
nodeName,
|
||||
}}
|
||||
title={container.Name}
|
||||
data-cy={`networkDetails-containerName-${container.Name}`}
|
||||
>
|
||||
{container.Name}
|
||||
</Link>
|
||||
|
|
|
@ -21,6 +21,7 @@ export function NestedNetworksDatatable({
|
|||
columns={columns}
|
||||
dataset={dataset}
|
||||
aria-label="Networks table"
|
||||
data-cy="docker-networks-nested-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ export function NetworksDatatable({ dataset, onRemove, onRefresh }: Props) {
|
|||
>
|
||||
<DeleteButton
|
||||
disabled={selectedRows.length === 0}
|
||||
data-cy="network-removeNetworkButton"
|
||||
confirmMessage="Do you want to remove the selected network(s)?"
|
||||
onConfirmed={() => onRemove(selectedRows)}
|
||||
/>
|
||||
|
@ -102,6 +103,7 @@ export function NetworksDatatable({ dataset, onRemove, onRefresh }: Props) {
|
|||
</TableSettingsMenu>
|
||||
)}
|
||||
getRowId={(row) => `${row.Name}-${row.Id}`}
|
||||
data-cy="networks-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ export const name = columnHelper.accessor('Name', {
|
|||
to=".network"
|
||||
params={{ id: item.Id, nodeName: item.NodeName }}
|
||||
title={item.Name}
|
||||
data-cy={`network-link-${item.Name}`}
|
||||
>
|
||||
{truncate(item.Name, 40)}
|
||||
</Link>
|
||||
|
|
|
@ -24,7 +24,7 @@ import { createOwnershipColumn } from '../../components/datatable/createOwnershi
|
|||
const columnHelper = createColumnHelper<SecretViewModel>();
|
||||
|
||||
const columns = [
|
||||
buildNameColumn<SecretViewModel>('Name', '.secret'),
|
||||
buildNameColumn<SecretViewModel>('Name', '.secret', 'docker-secrets-name'),
|
||||
columnHelper.accessor((item) => isoDate(item.CreatedAt), {
|
||||
header: 'Creation Date',
|
||||
}),
|
||||
|
@ -69,6 +69,7 @@ export function SecretsDatatable({
|
|||
disableSelect={!hasWriteAccessQuery.authorized}
|
||||
settingsManager={tableState}
|
||||
emptyContentLabel="No secret available."
|
||||
data-cy="docker-secrets-datatable"
|
||||
renderTableActions={(selectedItems) =>
|
||||
hasWriteAccessQuery.authorized && (
|
||||
<TableActions selectedItems={selectedItems} onRemove={onRemove} />
|
||||
|
|
|
@ -64,7 +64,7 @@ export function PortsMappingField({
|
|||
isValid={!errors}
|
||||
>
|
||||
{values.length > 0 ? (
|
||||
<Table>
|
||||
<Table data-cy="service-published-ports-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Host port</th>
|
||||
|
@ -123,6 +123,7 @@ function Item({
|
|||
onChange={(value) => handleChange('hostPort', value)}
|
||||
id={`hostPort-${index}`}
|
||||
label="host"
|
||||
data-cy={`hostPort-${index}`}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -132,6 +133,7 @@ function Item({
|
|||
onChange={(value) => handleChange('containerPort', value)}
|
||||
id={`containerPort-${index}`}
|
||||
label="container"
|
||||
data-cy={`containerPort-${index}`}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -154,6 +156,7 @@ function Item({
|
|||
]}
|
||||
disabled={disabled}
|
||||
aria-label="publish mode"
|
||||
data-cy={`publishMode-${index}`}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -161,6 +164,7 @@ function Item({
|
|||
icon={Trash2}
|
||||
color="dangerlight"
|
||||
onClick={() => onRemove()}
|
||||
data-cy={`remove-port-${index}`}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { InputLabeled } from '@@/form-components/Input/InputLabeled';
|
||||
import { Checkbox } from '@@/form-components/Checkbox';
|
||||
|
||||
|
@ -10,6 +12,7 @@ export function RangeOrNumberField({
|
|||
readOnly,
|
||||
id,
|
||||
label,
|
||||
'data-cy': dataCy,
|
||||
}: {
|
||||
value: Range | number | undefined;
|
||||
onChange: (value: Range | number | undefined) => void;
|
||||
|
@ -17,10 +20,10 @@ export function RangeOrNumberField({
|
|||
readOnly?: boolean;
|
||||
id: string;
|
||||
label: string;
|
||||
}) {
|
||||
} & AutomationTestingProps) {
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<RangeCheckbox value={value} onChange={onChange} />
|
||||
<RangeCheckbox value={value} onChange={onChange} data-cy={dataCy} />
|
||||
{isRange(value) ? (
|
||||
<RangeInput
|
||||
value={value}
|
||||
|
@ -29,6 +32,7 @@ export function RangeOrNumberField({
|
|||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
id={id}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
) : (
|
||||
<InputLabeled
|
||||
|
@ -42,6 +46,7 @@ export function RangeOrNumberField({
|
|||
value={value || ''}
|
||||
type="number"
|
||||
onChange={(e) => onChange(getNumber(e.target.valueAsNumber))}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -55,6 +60,7 @@ function RangeInput({
|
|||
readOnly,
|
||||
id,
|
||||
label,
|
||||
'data-cy': dataCy,
|
||||
}: {
|
||||
value: Range;
|
||||
onChange: (value: Range) => void;
|
||||
|
@ -62,7 +68,7 @@ function RangeInput({
|
|||
readOnly?: boolean;
|
||||
id: string;
|
||||
label: string;
|
||||
}) {
|
||||
} & AutomationTestingProps) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="font-normal m-0">{label}</label>
|
||||
|
@ -77,6 +83,7 @@ function RangeInput({
|
|||
readOnly={readOnly}
|
||||
id={id}
|
||||
type="number"
|
||||
data-cy={`${dataCy}-start`}
|
||||
/>
|
||||
|
||||
<InputLabeled
|
||||
|
@ -90,6 +97,7 @@ function RangeInput({
|
|||
readOnly={readOnly}
|
||||
id={id}
|
||||
type="number"
|
||||
data-cy={`${dataCy}-end`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -106,10 +114,11 @@ function getNumber(value: number) {
|
|||
function RangeCheckbox({
|
||||
value,
|
||||
onChange,
|
||||
'data-cy': dataCy,
|
||||
}: {
|
||||
value: Range | number | undefined;
|
||||
onChange: (value: Range | number | undefined) => void;
|
||||
}) {
|
||||
} & AutomationTestingProps) {
|
||||
const isValueRange = isRange(value);
|
||||
return (
|
||||
<Checkbox
|
||||
|
@ -122,6 +131,7 @@ function RangeCheckbox({
|
|||
onChange(value.start);
|
||||
}
|
||||
}}
|
||||
data-cy={`${dataCy}-range-checkbox`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,13 @@ export function ServiceWidget({
|
|||
<Widget aria-label={title}>
|
||||
<Widget.Title icon={titleIcon} title={title}>
|
||||
<Authorized authorizations="DockerServiceUpdate">
|
||||
<Button color="secondary" size="small" onClick={onAdd} icon={Plus}>
|
||||
<Button
|
||||
color="secondary"
|
||||
size="small"
|
||||
onClick={onAdd}
|
||||
icon={Plus}
|
||||
data-cy="service-add-button"
|
||||
>
|
||||
{labelForAddButton}
|
||||
</Button>
|
||||
</Authorized>
|
||||
|
@ -51,6 +57,7 @@ export function ServiceWidget({
|
|||
type="button"
|
||||
onClick={onSubmit}
|
||||
disabled={!hasChanges || !isValid}
|
||||
data-cy="service-apply-changes-button"
|
||||
>
|
||||
Apply changes
|
||||
</Button>
|
||||
|
@ -61,6 +68,7 @@ export function ServiceWidget({
|
|||
size="small"
|
||||
color="default"
|
||||
icon={ChevronDown}
|
||||
data-cy="service-reset-changes-button"
|
||||
>
|
||||
<span className="sr-only">Toggle Dropdown</span>
|
||||
</MenuButton>
|
||||
|
|
|
@ -32,6 +32,7 @@ export function TasksDatatable({
|
|||
dataset={dataset}
|
||||
emptyContentLabel="No task available."
|
||||
extendTableOptions={withMeta({ table: 'tasks', serviceName })}
|
||||
data-cy="docker-service-tasks-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -40,11 +40,17 @@ function Cell({
|
|||
to="docker.containers.container"
|
||||
params={{ id: item.Container.Id, nodeName: item.Container.NodeName }}
|
||||
className="monospaced"
|
||||
data-cy="docker-container-task-link"
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
) : (
|
||||
<Link to="docker.tasks.task" params={{ id: value }} className="monospaced">
|
||||
<Link
|
||||
to="docker.tasks.task"
|
||||
params={{ id: value }}
|
||||
className="monospaced"
|
||||
data-cy="docker-task-link"
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
);
|
||||
|
|
|
@ -108,6 +108,7 @@ export function ServicesDatatable({
|
|||
}),
|
||||
withGlobalFilter(filter)
|
||||
)}
|
||||
data-cy="services-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ export function TableActions({
|
|||
|
||||
{isAddActionVisible && (
|
||||
<Authorized authorizations="DockerServiceCreate">
|
||||
<AddButton>Add service</AddButton>
|
||||
<AddButton data-cy="docker-add-service-button">Add service</AddButton>
|
||||
</Authorized>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -25,6 +25,7 @@ export function TasksDatatable({
|
|||
search={search}
|
||||
emptyContentLabel="No task matching filter."
|
||||
aria-label="Tasks table"
|
||||
data-cy="docker-service-tasks-nested-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ function Cell({
|
|||
to="docker.containers.container"
|
||||
params={{ id: item.Container.Id, nodeName: item.Container.NodeName }}
|
||||
className="monospaced"
|
||||
data-cy="docker-task-container-link"
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
|
@ -38,6 +39,7 @@ function Cell({
|
|||
to="docker.tasks.task"
|
||||
params={{ id: item.Id }}
|
||||
className="monospaced"
|
||||
data-cy="docker-task-link"
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
|
|
|
@ -18,7 +18,11 @@ export function useColumns(isStackColumnVisible?: boolean) {
|
|||
() =>
|
||||
_.compact([
|
||||
buildExpandColumn<ServiceViewModel>(),
|
||||
buildNameColumn<ServiceViewModel>('Name', 'docker.services.service'),
|
||||
buildNameColumn<ServiceViewModel>(
|
||||
'Name',
|
||||
'docker.services.service',
|
||||
'docker-services-name'
|
||||
),
|
||||
isStackColumnVisible &&
|
||||
columnHelper.accessor((item) => item.StackName || '-', {
|
||||
header: 'Stack',
|
||||
|
|
|
@ -45,9 +45,15 @@ export function ScaleForm({
|
|||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus
|
||||
/>
|
||||
<Button color="none" icon={X} onClick={() => onClose()} />
|
||||
<Button
|
||||
color="none"
|
||||
icon={X}
|
||||
onClick={() => onClose()}
|
||||
data-cy={`scale-service-cancel-button-${service.Name}`}
|
||||
/>
|
||||
<LoadingButton
|
||||
isLoading={mutation.isLoading}
|
||||
data-cy={`scale-service-submit-button-${service.Name}`}
|
||||
disabled={
|
||||
values.replicas === service.Replicas ||
|
||||
values.replicas < 0 ||
|
||||
|
|
|
@ -14,7 +14,12 @@ export function ScaleServiceButton({ service }: { service: ServiceViewModel }) {
|
|||
if (!isEdit) {
|
||||
return (
|
||||
<Authorized authorizations="DockerServiceUpdate">
|
||||
<Button color="none" icon={Minimize2} onClick={() => setIsEdit(true)}>
|
||||
<Button
|
||||
color="none"
|
||||
icon={Minimize2}
|
||||
onClick={() => setIsEdit(true)}
|
||||
data-cy="scale-service-button"
|
||||
>
|
||||
Scale
|
||||
</Button>
|
||||
</Authorized>
|
||||
|
|
|
@ -28,6 +28,7 @@ export function TaskTableQuickActions({
|
|||
to="docker.tasks.task.logs"
|
||||
params={{ id: taskId }}
|
||||
title="Logs"
|
||||
data-cy="docker-task-logs-link"
|
||||
>
|
||||
<Icon icon={FileText} className="space-right" />
|
||||
</Link>
|
||||
|
@ -36,7 +37,12 @@ export function TaskTableQuickActions({
|
|||
|
||||
{state.showQuickActionInspect && (
|
||||
<Authorized authorizations="DockerTaskInspect">
|
||||
<Link to="docker.tasks.task" params={{ id: taskId }} title="Inspect">
|
||||
<Link
|
||||
to="docker.tasks.task"
|
||||
params={{ id: taskId }}
|
||||
title="Inspect"
|
||||
data-cy="docker-task-inspect-link"
|
||||
>
|
||||
<Icon icon={Info} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
|
|
|
@ -7,6 +7,7 @@ export async function confirmServiceForceUpdate(message: string) {
|
|||
message,
|
||||
confirmButton: buildConfirmButton('Update'),
|
||||
modalType: ModalType.Warn,
|
||||
'data-cy': 'confirm-service-force-update',
|
||||
});
|
||||
|
||||
return result ? { pullLatest: result.value } : undefined;
|
||||
|
|
|
@ -70,6 +70,7 @@ export function StacksDatatable({
|
|||
),
|
||||
}}
|
||||
extendTableOptions={withGlobalFilter(globalFilterFn)}
|
||||
data-cy="docker-stacks-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ export function TableSettingsMenus({
|
|||
<Authorized authorizations="EndpointResourcesAccess">
|
||||
<Checkbox
|
||||
id="setting_all_orphaned_stacks"
|
||||
data-cy="show-all-orphaned-stacks"
|
||||
label="Show all orphaned stacks"
|
||||
checked={tableState.showOrphanedStacks}
|
||||
onChange={(e) => {
|
||||
|
|
|
@ -81,6 +81,7 @@ function NameLink({ item }: { item: DecoratedStack }) {
|
|||
external: true,
|
||||
}}
|
||||
title={name}
|
||||
data-cy="docker-external-stack-link"
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
|
@ -103,6 +104,7 @@ function NameLink({ item }: { item: DecoratedStack }) {
|
|||
orphanedRunning: item.OrphanedRunning,
|
||||
}}
|
||||
title={name}
|
||||
data-cy={`docker-stack-link-${item.Name}`}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
|
|
|
@ -65,6 +65,7 @@ export function NodesDatatable({
|
|||
/>
|
||||
</TableSettingsMenu>
|
||||
)}
|
||||
data-cy="swarm-nodes-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,11 @@ function Cell({
|
|||
}
|
||||
|
||||
return (
|
||||
<Link to="docker.nodes.node" params={{ id: item.Id }}>
|
||||
<Link
|
||||
to="docker.nodes.node"
|
||||
params={{ id: item.Id }}
|
||||
data-cy={`node-link-${item.Id}`}
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
);
|
||||
|
|
|
@ -67,6 +67,7 @@ export function VolumesDatatable({
|
|||
table: 'volumes',
|
||||
isBrowseVisible,
|
||||
})}
|
||||
data-cy="docker-volumes-datatable"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ function Cell({
|
|||
nodeName: item.NodeName,
|
||||
}}
|
||||
className="monospaced"
|
||||
data-cy={`volume-link-${name}`}
|
||||
>
|
||||
{truncate(name, 40)}
|
||||
</Link>
|
||||
|
@ -102,7 +103,9 @@ function Cell({
|
|||
id: item.Id,
|
||||
nodeName: item.NodeName,
|
||||
},
|
||||
'data-cy': `volume-browse-link-${name}`,
|
||||
}}
|
||||
data-cy={`volume-browse-button-${name}`}
|
||||
>
|
||||
browse
|
||||
</Button>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue