From 3bf4a3974e72c0f6569b594534b2ea3f1a0f9926 Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Tue, 23 Sep 2025 16:32:00 -0300 Subject: [PATCH 1/9] feat: add k6 version information to probe model --- src/schemas/forms/ProbeSchema.ts | 3 ++ src/test/db/index.ts | 47 +++++++++++++++++++------------- src/test/fixtures/probes.ts | 21 ++++++++++++-- src/types.ts | 1 + 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/schemas/forms/ProbeSchema.ts b/src/schemas/forms/ProbeSchema.ts index a78a368b7..27da2bbe1 100644 --- a/src/schemas/forms/ProbeSchema.ts +++ b/src/schemas/forms/ProbeSchema.ts @@ -48,6 +48,9 @@ export const probeSchema: ZodType = z.object({ labels: labelsSchema, version: z.string(), deprecated: z.boolean(), + k6Version: z.string().optional(), + supportsBinaryProvisioning: z.boolean().optional(), + supportedChannels: z.array(z.string()).optional(), capabilities: z.object({ disableScriptedChecks: z.boolean(), disableBrowserChecks: z.boolean(), diff --git a/src/test/db/index.ts b/src/test/db/index.ts index 8374edc6e..55c6e51e4 100644 --- a/src/test/db/index.ts +++ b/src/test/db/index.ts @@ -33,25 +33,34 @@ const baseCheckModel = ({ sequence }: { sequence: number }) => ({ created: Math.floor(faker.date.past().getTime() / 1000), }); -const baseProbeModel = ({ sequence }: { sequence: number }) => ({ - id: sequence, - name: `${faker.lorem.word()}_${sequence}`, - public: faker.datatype.boolean(), - latitude: faker.location.latitude(), - longitude: faker.location.longitude(), - region: faker.helpers.arrayElement(['EMEA', 'AMER', 'APAC']), - labels: [{ name: faker.animal.petName(), value: faker.color.human() }], - online: true, - onlineChange: Math.floor(faker.date.past().getTime() / 1000), - version: faker.system.semver(), - deprecated: false, - modified: Math.floor(faker.date.recent().getTime() / 1000), - created: Math.floor(faker.date.past().getTime() / 1000), - capabilities: { - disableScriptedChecks: false, - disableBrowserChecks: false, - }, -}); +const baseProbeModel = ({ sequence }: { sequence: number }) => { + const supportsBinaryProvisioning = faker.datatype.boolean(); + + return { + id: sequence, + name: `${faker.lorem.word()}_${sequence}`, + public: faker.datatype.boolean(), + latitude: faker.location.latitude(), + longitude: faker.location.longitude(), + region: faker.helpers.arrayElement(['EMEA', 'AMER', 'APAC']), + labels: [{ name: faker.animal.petName(), value: faker.color.human() }], + online: true, + onlineChange: Math.floor(faker.date.past().getTime() / 1000), + version: faker.system.semver(), + deprecated: false, + modified: Math.floor(faker.date.recent().getTime() / 1000), + created: Math.floor(faker.date.past().getTime() / 1000), + k6Version: supportsBinaryProvisioning ? undefined : faker.system.semver(), // Legacy probes have static k6 version + supportsBinaryProvisioning, + supportedChannels: supportsBinaryProvisioning + ? faker.helpers.arrayElements(['v1', 'v2', 'fast'], { min: 1, max: 3 }) + : faker.helpers.arrayElements(['v1'], { min: 1, max: 1 }), // Legacy probes support limited channels + capabilities: { + disableScriptedChecks: false, + disableBrowserChecks: false, + }, + }; +}; const tlsConfig = () => ({ caCert: faker.helpers.maybe(() => faker.string.uuid()), diff --git a/src/test/fixtures/probes.ts b/src/test/fixtures/probes.ts index 7fdaf0777..cafeafaf6 100644 --- a/src/test/fixtures/probes.ts +++ b/src/test/fixtures/probes.ts @@ -7,17 +7,28 @@ export const PRIVATE_PROBE: Probe = db.probe.build({ { name: 'Mr', value: 'Orange' }, { name: 'chimi', value: 'churri' }, ], + k6Version: 'v0.54.1', + supportsBinaryProvisioning: false, + supportedChannels: ['v1'], }); export const PUBLIC_PROBE: Probe = db.probe.build({ public: true, online: false, + supportsBinaryProvisioning: true, + supportedChannels: ['v1', 'v2', 'fast'], }); -export const ONLINE_PROBE: Probe = db.probe.build({}); +export const ONLINE_PROBE: Probe = db.probe.build({ + supportsBinaryProvisioning: true, + supportedChannels: ['v1', 'v2'], +}); export const OFFLINE_PROBE: Probe = db.probe.build({ online: false, + k6Version: 'v1.2.3', + supportsBinaryProvisioning: false, + supportedChannels: ['v1'], }); export const SCRIPTED_DISABLED_PROBE: Probe = db.probe.build({ @@ -25,9 +36,15 @@ export const SCRIPTED_DISABLED_PROBE: Probe = db.probe.build({ disableScriptedChecks: true, disableBrowserChecks: true, }, + supportsBinaryProvisioning: true, + supportedChannels: ['v2'], }); -export const UNSELECTED_PRIVATE_PROBE: Probe = db.probe.build({}); +export const UNSELECTED_PRIVATE_PROBE: Probe = db.probe.build({ + k6Version: 'v2.3.4', + supportsBinaryProvisioning: false, + supportedChannels: ['v2'], +}); export const DEFAULT_PROBES = [PRIVATE_PROBE, PUBLIC_PROBE, UNSELECTED_PRIVATE_PROBE]; diff --git a/src/types.ts b/src/types.ts index ceb2f8185..25e2eb40b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -123,6 +123,7 @@ export interface Probe extends ExistingObject { deprecated: boolean; k6Version?: string; // For legacy probes: static version like "v0.54.1". For modern probes: may be empty supportsBinaryProvisioning?: boolean; // whether probe supports version management + supportedChannels?: string[]; // List of channel IDs that this probe supports capabilities: ProbeCapabilities; } From 0e30295db921a4d2b8a25f2cb91213bf514d7d75 Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Tue, 23 Sep 2025 16:32:28 -0300 Subject: [PATCH 2/9] feat: show k6 version in probe card and edit view --- src/components/ProbeCard/ProbeCard.tsx | 15 ++++++++++++++- src/components/ProbeStatus/ProbeStatus.tsx | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/components/ProbeCard/ProbeCard.tsx b/src/components/ProbeCard/ProbeCard.tsx index c570a8dbe..15c67b941 100644 --- a/src/components/ProbeCard/ProbeCard.tsx +++ b/src/components/ProbeCard/ProbeCard.tsx @@ -3,12 +3,13 @@ import { GrafanaTheme2 } from '@grafana/data'; import { Card, Link, LinkButton, Stack, TextLink, useStyles2 } from '@grafana/ui'; import { css } from '@emotion/css'; -import { type ExtendedProbe, type Label } from 'types'; +import { type ExtendedProbe, FeatureName,type Label } from 'types'; import { AppRoutes } from 'routing/types'; import { generateRoutePath } from 'routing/utils'; import { useCanEditProbe } from 'hooks/useCanEditProbe'; import { PROBE_REACHABILITY_DESCRIPTION } from 'components/constants'; import { DeprecationNotice } from 'components/DeprecationNotice/DeprecationNotice'; +import { FeatureFlag } from 'components/FeatureFlag'; import { SuccessRateGaugeProbe } from 'components/Gauges'; import { ProbeUsageLink } from '../ProbeUsageLink'; @@ -54,6 +55,18 @@ export const ProbeCard = ({ probe }: { probe: ExtendedProbe }) => {
Version: {probe.version}
+ + {({ isEnabled }) => + isEnabled ? ( + <> + {!probe.public && probe.k6Version &&
k6 version: {probe.k6Version}
} + {probe.supportsBinaryProvisioning && ( +
k6 version management: supported
+ )} + + ) : null + } +
diff --git a/src/components/ProbeStatus/ProbeStatus.tsx b/src/components/ProbeStatus/ProbeStatus.tsx index 036249795..41ef63943 100644 --- a/src/components/ProbeStatus/ProbeStatus.tsx +++ b/src/components/ProbeStatus/ProbeStatus.tsx @@ -13,12 +13,13 @@ import { } from '@grafana/ui'; import { css } from '@emotion/css'; -import { type ExtendedProbe } from 'types'; +import { type ExtendedProbe, FeatureName } from 'types'; import { formatDate } from 'utils'; import { useResetProbeToken } from 'data/useProbes'; import { useCanEditProbe } from 'hooks/useCanEditProbe'; import { PROBE_REACHABILITY_DESCRIPTION } from 'components/constants'; import { DeprecationNotice } from 'components/DeprecationNotice/DeprecationNotice'; +import { FeatureFlag } from 'components/FeatureFlag'; import { SuccessRateGaugeProbe } from 'components/Gauges'; import { ProbeUsageLink } from '../ProbeUsageLink'; @@ -98,6 +99,18 @@ export const ProbeStatus = ({ probe, onReset, readOnly }: ProbeStatusProps) => {
+ + {({ isEnabled }) => + isEnabled ? ( + <> + {!probe.public && probe.k6Version && } + {probe.supportsBinaryProvisioning && ( + + )} + + ) : null + } + Date: Tue, 23 Sep 2025 16:33:20 -0300 Subject: [PATCH 3/9] feat: show probe version in check create/edit form --- .../CheckEditor/CheckProbes/ProbesList.tsx | 149 ++++++++++++++---- 1 file changed, 114 insertions(+), 35 deletions(-) diff --git a/src/components/CheckEditor/CheckProbes/ProbesList.tsx b/src/components/CheckEditor/CheckProbes/ProbesList.tsx index c335c12c5..405f86240 100644 --- a/src/components/CheckEditor/CheckProbes/ProbesList.tsx +++ b/src/components/CheckEditor/CheckProbes/ProbesList.tsx @@ -1,9 +1,12 @@ import React, { useMemo } from 'react'; +import { useFormContext } from 'react-hook-form'; import { GrafanaTheme2 } from '@grafana/data'; import { Checkbox, Label, Stack, Text, TextLink, useStyles2 } from '@grafana/ui'; import { css } from '@emotion/css'; -import { ProbeWithMetadata } from 'types'; +import { CheckFormValues, FeatureName, ProbeWithMetadata } from 'types'; +import { useCurrentK6Version } from 'data/useK6Channels'; +import { useFeatureFlag } from 'hooks/useFeatureFlag'; import { DeprecationNotice } from 'components/DeprecationNotice/DeprecationNotice'; import { ProbeStatus } from 'components/ProbeCard/ProbeStatus'; @@ -21,6 +24,53 @@ export const ProbesList = ({ disabled?: boolean; }) => { const styles = useStyles2(getStyles); + const { getValues } = useFormContext(); + const { isEnabled: isVersionManagementEnabled } = useFeatureFlag(FeatureName.VersionManagement); + + const { selectedChannel, isScriptedOrBrowser } = useMemo(() => { + const values = getValues(); + const checkType = values.checkType; + const isScriptedOrBrowser = checkType === 'scripted' || checkType === 'browser'; + + let selectedChannel = ''; + if (checkType === 'scripted' && values.settings?.scripted?.channel) { + selectedChannel = values.settings.scripted.channel; + } else if (checkType === 'browser' && values.settings?.browser?.channel) { + selectedChannel = values.settings.browser.channel; + } + + return { selectedChannel, isScriptedOrBrowser }; + }, [getValues]); + + const { data: currentChannelVersion } = useCurrentK6Version( + isVersionManagementEnabled && isScriptedOrBrowser && Boolean(selectedChannel), + selectedChannel + ); + + const getProbeK6Version = (probe: ProbeWithMetadata) => { + if (!isVersionManagementEnabled || !isScriptedOrBrowser) { + return null; // Feature not enabled or not relevant for non-scripted/browser checks + } + + // For legacy probes (no binary provisioning), show their static k6 version + if (!probe.supportsBinaryProvisioning && probe.k6Version) { + return probe.k6Version; + } + + // For probes with binary provisioning, show the resolved channel version + if (probe.supportsBinaryProvisioning && currentChannelVersion) { + return currentChannelVersion; + } + + return null; + }; + + const isProbeCompatible = (probe: ProbeWithMetadata): boolean => { + if (!isVersionManagementEnabled || !isScriptedOrBrowser || !selectedChannel) { + return true; + } + return probe.supportedChannels?.includes(selectedChannel) ?? false; + }; const handleToggleAll = () => { if (allProbesSelected) { @@ -76,40 +126,50 @@ export const ProbesList = ({
- {probes.map((probe: ProbeWithMetadata) => ( -
- handleToggleProbe(probe)} - checked={selectedProbes.includes(probe.id!)} - disabled={disabled} - /> -
- -
- ))} + {probes.map((probe: ProbeWithMetadata) => { + const isCompatible = isProbeCompatible(probe); + const isProbeDisabled = disabled || !isCompatible; + const k6Version = getProbeK6Version(probe); + + return ( +
+ handleToggleProbe(probe)} + checked={selectedProbes.includes(probe.id!)} + disabled={isProbeDisabled} + /> +
+ + + ); + })} ); @@ -169,4 +229,23 @@ const getStyles = (theme: GrafanaTheme2) => ({ lineHeight: theme.typography.body.lineHeight, marginBottom: '0', }), + + k6Version: css({ + color: theme.colors.text.secondary, + fontSize: theme.typography.bodySmall.fontSize, + }), + + incompatibleItem: css({ + opacity: 0.5, + }), + + incompatibleLabel: css({ + color: theme.colors.text.disabled, + }), + + incompatibleText: css({ + color: theme.colors.warning.text, + fontSize: theme.typography.bodySmall.fontSize, + fontWeight: theme.typography.fontWeightMedium, + }), }); From 39b1bf1d59d39b96831cd3e8cc0d0019f23ea74d Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Tue, 23 Sep 2025 17:29:57 -0300 Subject: [PATCH 4/9] fix: tests --- .../CheckEditor/CheckProbes/ProbesList.tsx | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/components/CheckEditor/CheckProbes/ProbesList.tsx b/src/components/CheckEditor/CheckProbes/ProbesList.tsx index 405f86240..e702dafb5 100644 --- a/src/components/CheckEditor/CheckProbes/ProbesList.tsx +++ b/src/components/CheckEditor/CheckProbes/ProbesList.tsx @@ -139,34 +139,40 @@ export const ProbesList = ({ checked={selectedProbes.includes(probe.id!)} disabled={isProbeDisabled} /> - +
+
+ + {k6Version && ( +
+ (k6: {k6Version}) +
+ )} + ); })} @@ -230,6 +236,14 @@ const getStyles = (theme: GrafanaTheme2) => ({ marginBottom: '0', }), + probeContent: css({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + flex: 1, + gap: theme.spacing(1), + }), + k6Version: css({ color: theme.colors.text.secondary, fontSize: theme.typography.bodySmall.fontSize, From 2bd2c16de2e8a071749ead08f1db9941f55fdc9a Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Mon, 13 Oct 2025 17:19:31 -0300 Subject: [PATCH 5/9] fix: link k6Version and supportedChannels in probe fixtures --- src/test/db/index.ts | 20 +++++++++++++++++--- src/test/fixtures/probes.ts | 6 +++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/test/db/index.ts b/src/test/db/index.ts index 55c6e51e4..7e58eb331 100644 --- a/src/test/db/index.ts +++ b/src/test/db/index.ts @@ -36,6 +36,18 @@ const baseCheckModel = ({ sequence }: { sequence: number }) => ({ const baseProbeModel = ({ sequence }: { sequence: number }) => { const supportsBinaryProvisioning = faker.datatype.boolean(); + const availableChannels = ['v0', 'v1', 'v2']; + + const probeChannel = supportsBinaryProvisioning + ? null + : faker.helpers.arrayElement(availableChannels); + + const k6VersionsByChannel = { + v0: ['v0.54.1', 'v0.48.0', 'v0.52.3'], + v1: ['v1.0.0', 'v1.2.3', 'v1.5.1'], + v2: ['v2.0.0', 'v2.1.2', 'v2.3.4'] + }; + return { id: sequence, name: `${faker.lorem.word()}_${sequence}`, @@ -50,11 +62,13 @@ const baseProbeModel = ({ sequence }: { sequence: number }) => { deprecated: false, modified: Math.floor(faker.date.recent().getTime() / 1000), created: Math.floor(faker.date.past().getTime() / 1000), - k6Version: supportsBinaryProvisioning ? undefined : faker.system.semver(), // Legacy probes have static k6 version + k6Version: supportsBinaryProvisioning + ? undefined + : faker.helpers.arrayElement(k6VersionsByChannel[probeChannel as keyof typeof k6VersionsByChannel]), supportsBinaryProvisioning, supportedChannels: supportsBinaryProvisioning - ? faker.helpers.arrayElements(['v1', 'v2', 'fast'], { min: 1, max: 3 }) - : faker.helpers.arrayElements(['v1'], { min: 1, max: 1 }), // Legacy probes support limited channels + ? faker.helpers.arrayElements(availableChannels, { min: 1, max: 3 }) + : [probeChannel!], capabilities: { disableScriptedChecks: false, disableBrowserChecks: false, diff --git a/src/test/fixtures/probes.ts b/src/test/fixtures/probes.ts index cafeafaf6..f098f96da 100644 --- a/src/test/fixtures/probes.ts +++ b/src/test/fixtures/probes.ts @@ -9,14 +9,14 @@ export const PRIVATE_PROBE: Probe = db.probe.build({ ], k6Version: 'v0.54.1', supportsBinaryProvisioning: false, - supportedChannels: ['v1'], + supportedChannels: ['v0'], }); export const PUBLIC_PROBE: Probe = db.probe.build({ public: true, online: false, supportsBinaryProvisioning: true, - supportedChannels: ['v1', 'v2', 'fast'], + supportedChannels: ['v1', 'v2'], }); export const ONLINE_PROBE: Probe = db.probe.build({ @@ -28,7 +28,7 @@ export const OFFLINE_PROBE: Probe = db.probe.build({ online: false, k6Version: 'v1.2.3', supportsBinaryProvisioning: false, - supportedChannels: ['v1'], + supportedChannels: ['v0', 'v1'], }); export const SCRIPTED_DISABLED_PROBE: Probe = db.probe.build({ From f103d88df2292947b1fec729ff69ca4ac03f8080 Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Tue, 14 Oct 2025 12:40:33 -0300 Subject: [PATCH 6/9] fix: adapt probes response to latest design changes - both public and private probes now return `k6versions` prop - it details the supported channels and versions --- .../CheckEditor/CheckProbes/ProbesList.tsx | 23 ++-------- src/components/ProbeCard/ProbeCard.tsx | 24 +++++----- src/components/ProbeStatus/ProbeStatus.tsx | 36 ++++++++++----- .../TerraformConfig/terraformConfigUtils.ts | 1 + src/hooks/useTerraformConfig.test.tsx | 1 + src/schemas/forms/ProbeSchema.ts | 4 +- src/test/db/index.ts | 37 +++++++-------- src/test/fixtures/k6Channels.ts | 8 ++-- src/test/fixtures/probes.ts | 45 ++++++++++++------- src/test/fixtures/terraform.ts | 2 + src/types.ts | 4 +- 11 files changed, 101 insertions(+), 84 deletions(-) diff --git a/src/components/CheckEditor/CheckProbes/ProbesList.tsx b/src/components/CheckEditor/CheckProbes/ProbesList.tsx index e702dafb5..4c2ab5490 100644 --- a/src/components/CheckEditor/CheckProbes/ProbesList.tsx +++ b/src/components/CheckEditor/CheckProbes/ProbesList.tsx @@ -5,7 +5,6 @@ import { Checkbox, Label, Stack, Text, TextLink, useStyles2 } from '@grafana/ui' import { css } from '@emotion/css'; import { CheckFormValues, FeatureName, ProbeWithMetadata } from 'types'; -import { useCurrentK6Version } from 'data/useK6Channels'; import { useFeatureFlag } from 'hooks/useFeatureFlag'; import { DeprecationNotice } from 'components/DeprecationNotice/DeprecationNotice'; import { ProbeStatus } from 'components/ProbeCard/ProbeStatus'; @@ -42,34 +41,20 @@ export const ProbesList = ({ return { selectedChannel, isScriptedOrBrowser }; }, [getValues]); - const { data: currentChannelVersion } = useCurrentK6Version( - isVersionManagementEnabled && isScriptedOrBrowser && Boolean(selectedChannel), - selectedChannel - ); - const getProbeK6Version = (probe: ProbeWithMetadata) => { - if (!isVersionManagementEnabled || !isScriptedOrBrowser) { + if (!isVersionManagementEnabled || !isScriptedOrBrowser || !selectedChannel) { return null; // Feature not enabled or not relevant for non-scripted/browser checks } - // For legacy probes (no binary provisioning), show their static k6 version - if (!probe.supportsBinaryProvisioning && probe.k6Version) { - return probe.k6Version; - } - - // For probes with binary provisioning, show the resolved channel version - if (probe.supportsBinaryProvisioning && currentChannelVersion) { - return currentChannelVersion; - } - - return null; + return probe.k6Versions?.[selectedChannel] || null; }; const isProbeCompatible = (probe: ProbeWithMetadata): boolean => { if (!isVersionManagementEnabled || !isScriptedOrBrowser || !selectedChannel) { return true; } - return probe.supportedChannels?.includes(selectedChannel) ?? false; + // Probe is compatible if it has a k6 version for the selected channel (not null) + return probe.k6Versions?.[selectedChannel] !== null && probe.k6Versions?.[selectedChannel] !== undefined; }; const handleToggleAll = () => { diff --git a/src/components/ProbeCard/ProbeCard.tsx b/src/components/ProbeCard/ProbeCard.tsx index 15c67b941..4534f0ad7 100644 --- a/src/components/ProbeCard/ProbeCard.tsx +++ b/src/components/ProbeCard/ProbeCard.tsx @@ -3,7 +3,7 @@ import { GrafanaTheme2 } from '@grafana/data'; import { Card, Link, LinkButton, Stack, TextLink, useStyles2 } from '@grafana/ui'; import { css } from '@emotion/css'; -import { type ExtendedProbe, FeatureName,type Label } from 'types'; +import { type ExtendedProbe, FeatureName, type Label } from 'types'; import { AppRoutes } from 'routing/types'; import { generateRoutePath } from 'routing/utils'; import { useCanEditProbe } from 'hooks/useCanEditProbe'; @@ -12,6 +12,7 @@ import { DeprecationNotice } from 'components/DeprecationNotice/DeprecationNotic import { FeatureFlag } from 'components/FeatureFlag'; import { SuccessRateGaugeProbe } from 'components/Gauges'; +import { getFormattedK6Versions } from '../ProbeStatus/ProbeStatus'; import { ProbeUsageLink } from '../ProbeUsageLink'; import { ProbeDisabledCapabilities } from './ProbeDisabledCapabilities'; import { ProbeLabels } from './ProbeLabels'; @@ -56,16 +57,17 @@ export const ProbeCard = ({ probe }: { probe: ExtendedProbe }) => {
Version: {probe.version}
- {({ isEnabled }) => - isEnabled ? ( - <> - {!probe.public && probe.k6Version &&
k6 version: {probe.k6Version}
} - {probe.supportsBinaryProvisioning && ( -
k6 version management: supported
- )} - - ) : null - } + {({ isEnabled }) => { + if (!isEnabled) { + return null; + } + + const supportedVersions = getFormattedK6Versions(probe); + + return supportedVersions ? ( +
k6: {supportedVersions}
+ ) : null; + }}
diff --git a/src/components/ProbeStatus/ProbeStatus.tsx b/src/components/ProbeStatus/ProbeStatus.tsx index 41ef63943..421bcb612 100644 --- a/src/components/ProbeStatus/ProbeStatus.tsx +++ b/src/components/ProbeStatus/ProbeStatus.tsx @@ -24,6 +24,17 @@ import { SuccessRateGaugeProbe } from 'components/Gauges'; import { ProbeUsageLink } from '../ProbeUsageLink'; +export function getFormattedK6Versions(probe: { k6Versions?: Record }): string { + if (!probe.k6Versions) { + return ''; + } + + return Object.entries(probe.k6Versions) + .filter(([, version]) => version !== null) + .map(([, version]) => version) + .join(', '); +} + interface ProbeStatusProps { probe: ExtendedProbe; onReset: (token: string) => void; @@ -57,6 +68,8 @@ export const ProbeStatus = ({ probe, onReset, readOnly }: ProbeStatusProps) => { const neverModified = probe.created === probe.modified; const neverOnline = probe.onlineChange === probe.created && !probe.online; + const supportedVersions = getFormattedK6Versions(probe); + return (
@@ -96,20 +109,21 @@ export const ProbeStatus = ({ probe, onReset, readOnly }: ProbeStatusProps) => { )}
- +
- {({ isEnabled }) => - isEnabled ? ( - <> - {!probe.public && probe.k6Version && } - {probe.supportsBinaryProvisioning && ( - - )} - - ) : null - } + {({ isEnabled }) => { + if (!isEnabled || !supportedVersions) { + return null; + } + return ; + }} ({ region: probe.region, public: false, labels: labelsToTFLabels(probe.labels), + k6Versions: probe.k6Versions, disable_browser_checks: probe.capabilities.disableBrowserChecks, disable_scripted_checks: probe.capabilities.disableScriptedChecks, }); diff --git a/src/hooks/useTerraformConfig.test.tsx b/src/hooks/useTerraformConfig.test.tsx index cb99d6488..03218aade 100644 --- a/src/hooks/useTerraformConfig.test.tsx +++ b/src/hooks/useTerraformConfig.test.tsx @@ -379,6 +379,7 @@ describe('terraform config generation', () => { name: PRIVATE_PROBE.name, public: false, region: PRIVATE_PROBE.region, + k6Versions: PRIVATE_PROBE.k6Versions, disable_browser_checks: PRIVATE_PROBE.capabilities.disableBrowserChecks, disable_scripted_checks: PRIVATE_PROBE.capabilities.disableScriptedChecks, }, diff --git a/src/schemas/forms/ProbeSchema.ts b/src/schemas/forms/ProbeSchema.ts index 27da2bbe1..fd1d0b778 100644 --- a/src/schemas/forms/ProbeSchema.ts +++ b/src/schemas/forms/ProbeSchema.ts @@ -48,9 +48,7 @@ export const probeSchema: ZodType = z.object({ labels: labelsSchema, version: z.string(), deprecated: z.boolean(), - k6Version: z.string().optional(), - supportsBinaryProvisioning: z.boolean().optional(), - supportedChannels: z.array(z.string()).optional(), + k6Versions: z.record(z.string().nullable()).optional(), capabilities: z.object({ disableScriptedChecks: z.boolean(), disableBrowserChecks: z.boolean(), diff --git a/src/test/db/index.ts b/src/test/db/index.ts index 7e58eb331..3e178e4bc 100644 --- a/src/test/db/index.ts +++ b/src/test/db/index.ts @@ -34,20 +34,27 @@ const baseCheckModel = ({ sequence }: { sequence: number }) => ({ }); const baseProbeModel = ({ sequence }: { sequence: number }) => { - const supportsBinaryProvisioning = faker.datatype.boolean(); - const availableChannels = ['v0', 'v1', 'v2']; - - const probeChannel = supportsBinaryProvisioning - ? null - : faker.helpers.arrayElement(availableChannels); - + const k6VersionsByChannel = { v0: ['v0.54.1', 'v0.48.0', 'v0.52.3'], - v1: ['v1.0.0', 'v1.2.3', 'v1.5.1'], - v2: ['v2.0.0', 'v2.1.2', 'v2.3.4'] + v1: ['v1.0.0', 'v1.2.3', 'v1.5.1'], + v2: ['v2.0.0', 'v2.1.2', 'v2.3.4'], }; - + + const k6Versions: Record = {}; + availableChannels.forEach((channel) => { + const supportsChannel = faker.datatype.boolean(0.7); // 70% chance of supporting each channel + + if (supportsChannel) { + k6Versions[channel] = faker.helpers.arrayElement( + k6VersionsByChannel[channel as keyof typeof k6VersionsByChannel] + ); + } else { + k6Versions[channel] = null; + } + }); + return { id: sequence, name: `${faker.lorem.word()}_${sequence}`, @@ -62,13 +69,7 @@ const baseProbeModel = ({ sequence }: { sequence: number }) => { deprecated: false, modified: Math.floor(faker.date.recent().getTime() / 1000), created: Math.floor(faker.date.past().getTime() / 1000), - k6Version: supportsBinaryProvisioning - ? undefined - : faker.helpers.arrayElement(k6VersionsByChannel[probeChannel as keyof typeof k6VersionsByChannel]), - supportsBinaryProvisioning, - supportedChannels: supportsBinaryProvisioning - ? faker.helpers.arrayElements(availableChannels, { min: 1, max: 3 }) - : [probeChannel!], + k6Versions, capabilities: { disableScriptedChecks: false, disableBrowserChecks: false, @@ -283,7 +284,7 @@ export const db = { period: faker.helpers.arrayElement(['5m', '10m', '15m', '30m', '1h']), created: Math.floor(faker.date.past().getTime() / 1000), modified: Math.floor(faker.date.recent().getTime() / 1000), - status: "OK", + status: 'OK', runbookUrl: faker.helpers.maybe(() => faker.internet.url()), })), }; diff --git a/src/test/fixtures/k6Channels.ts b/src/test/fixtures/k6Channels.ts index c3330b269..d74bf7442 100644 --- a/src/test/fixtures/k6Channels.ts +++ b/src/test/fixtures/k6Channels.ts @@ -13,16 +13,16 @@ export const K6_CHANNELS: K6Channel[] = [ id: 'v1', name: 'v1', default: true, - deprecatedAfter: '2024-01-01T00:00:00Z', // Already deprecated - disabledAfter: '2026-12-31T00:00:00Z', + deprecatedAfter: '2026-01-01T00:00:00Z', + disabledAfter: '2029-12-31T00:00:00Z', manifest: 'k6>=1,k6<2', }, { id: 'v2', name: 'v2', default: false, - deprecatedAfter: '2026-12-31T00:00:00Z', - disabledAfter: '2027-12-31T00:00:00Z', + deprecatedAfter: '2100-12-31T00:00:00Z', + disabledAfter: '2500-12-31T00:00:00Z', manifest: 'k6>=2', }, ]; diff --git a/src/test/fixtures/probes.ts b/src/test/fixtures/probes.ts index f098f96da..2f2cb4899 100644 --- a/src/test/fixtures/probes.ts +++ b/src/test/fixtures/probes.ts @@ -7,28 +7,38 @@ export const PRIVATE_PROBE: Probe = db.probe.build({ { name: 'Mr', value: 'Orange' }, { name: 'chimi', value: 'churri' }, ], - k6Version: 'v0.54.1', - supportsBinaryProvisioning: false, - supportedChannels: ['v0'], + k6Versions: { + v0: 'v0.54.1', + v1: null, + v2: null, + }, }); export const PUBLIC_PROBE: Probe = db.probe.build({ public: true, online: false, - supportsBinaryProvisioning: true, - supportedChannels: ['v1', 'v2'], + k6Versions: { + v0: null, + v1: 'v1.2.3', + v2: 'v2.0.0', + }, }); export const ONLINE_PROBE: Probe = db.probe.build({ - supportsBinaryProvisioning: true, - supportedChannels: ['v1', 'v2'], + k6Versions: { + v0: null, + v1: 'v1.5.1', + v2: 'v2.1.2', + }, }); export const OFFLINE_PROBE: Probe = db.probe.build({ online: false, - k6Version: 'v1.2.3', - supportsBinaryProvisioning: false, - supportedChannels: ['v0', 'v1'], + k6Versions: { + v0: 'v0.48.0', + v1: 'v1.2.3', + v2: null, + }, }); export const SCRIPTED_DISABLED_PROBE: Probe = db.probe.build({ @@ -36,14 +46,19 @@ export const SCRIPTED_DISABLED_PROBE: Probe = db.probe.build({ disableScriptedChecks: true, disableBrowserChecks: true, }, - supportsBinaryProvisioning: true, - supportedChannels: ['v2'], + k6Versions: { + v0: null, + v1: null, + v2: 'v2.3.4', + }, }); export const UNSELECTED_PRIVATE_PROBE: Probe = db.probe.build({ - k6Version: 'v2.3.4', - supportsBinaryProvisioning: false, - supportedChannels: ['v2'], + k6Versions: { + v0: null, + v1: null, + v2: 'v2.3.4', + }, }); export const DEFAULT_PROBES = [PRIVATE_PROBE, PUBLIC_PROBE, UNSELECTED_PRIVATE_PROBE]; diff --git a/src/test/fixtures/terraform.ts b/src/test/fixtures/terraform.ts index 5d0fd2e9b..00a81fd18 100644 --- a/src/test/fixtures/terraform.ts +++ b/src/test/fixtures/terraform.ts @@ -16,6 +16,7 @@ export const TERRAFORM_PRIVATE_PROBES = { name: PRIVATE_PROBE.name, public: PRIVATE_PROBE.public, region: PRIVATE_PROBE.region, + k6Versions: PRIVATE_PROBE.k6Versions, disable_browser_checks: PRIVATE_PROBE.capabilities.disableBrowserChecks, disable_scripted_checks: PRIVATE_PROBE.capabilities.disableScriptedChecks, }, @@ -28,6 +29,7 @@ export const TERRAFORM_PRIVATE_PROBES = { name: UNSELECTED_PRIVATE_PROBE.name, public: UNSELECTED_PRIVATE_PROBE.public, region: UNSELECTED_PRIVATE_PROBE.region, + k6Versions: UNSELECTED_PRIVATE_PROBE.k6Versions, disable_browser_checks: UNSELECTED_PRIVATE_PROBE.capabilities.disableBrowserChecks, disable_scripted_checks: UNSELECTED_PRIVATE_PROBE.capabilities.disableScriptedChecks, }, diff --git a/src/types.ts b/src/types.ts index 25e2eb40b..c1cf0d2b1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -121,9 +121,7 @@ export interface Probe extends ExistingObject { labels: Label[]; version: string; deprecated: boolean; - k6Version?: string; // For legacy probes: static version like "v0.54.1". For modern probes: may be empty - supportsBinaryProvisioning?: boolean; // whether probe supports version management - supportedChannels?: string[]; // List of channel IDs that this probe supports + k6Versions?: Record; capabilities: ProbeCapabilities; } From c98a97431f6f021d13b0eb7531d45a6db3f93032 Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Tue, 14 Oct 2025 12:52:47 -0300 Subject: [PATCH 7/9] fix: don't add this new field to terraform --- src/components/TerraformConfig/terraformConfigUtils.ts | 1 - src/hooks/useTerraformConfig.test.tsx | 1 - src/test/fixtures/terraform.ts | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/components/TerraformConfig/terraformConfigUtils.ts b/src/components/TerraformConfig/terraformConfigUtils.ts index ec483d8b6..5d7de9498 100644 --- a/src/components/TerraformConfig/terraformConfigUtils.ts +++ b/src/components/TerraformConfig/terraformConfigUtils.ts @@ -226,7 +226,6 @@ export const probeToTF = (probe: Probe): TFProbe => ({ region: probe.region, public: false, labels: labelsToTFLabels(probe.labels), - k6Versions: probe.k6Versions, disable_browser_checks: probe.capabilities.disableBrowserChecks, disable_scripted_checks: probe.capabilities.disableScriptedChecks, }); diff --git a/src/hooks/useTerraformConfig.test.tsx b/src/hooks/useTerraformConfig.test.tsx index 03218aade..cb99d6488 100644 --- a/src/hooks/useTerraformConfig.test.tsx +++ b/src/hooks/useTerraformConfig.test.tsx @@ -379,7 +379,6 @@ describe('terraform config generation', () => { name: PRIVATE_PROBE.name, public: false, region: PRIVATE_PROBE.region, - k6Versions: PRIVATE_PROBE.k6Versions, disable_browser_checks: PRIVATE_PROBE.capabilities.disableBrowserChecks, disable_scripted_checks: PRIVATE_PROBE.capabilities.disableScriptedChecks, }, diff --git a/src/test/fixtures/terraform.ts b/src/test/fixtures/terraform.ts index 00a81fd18..5d0fd2e9b 100644 --- a/src/test/fixtures/terraform.ts +++ b/src/test/fixtures/terraform.ts @@ -16,7 +16,6 @@ export const TERRAFORM_PRIVATE_PROBES = { name: PRIVATE_PROBE.name, public: PRIVATE_PROBE.public, region: PRIVATE_PROBE.region, - k6Versions: PRIVATE_PROBE.k6Versions, disable_browser_checks: PRIVATE_PROBE.capabilities.disableBrowserChecks, disable_scripted_checks: PRIVATE_PROBE.capabilities.disableScriptedChecks, }, @@ -29,7 +28,6 @@ export const TERRAFORM_PRIVATE_PROBES = { name: UNSELECTED_PRIVATE_PROBE.name, public: UNSELECTED_PRIVATE_PROBE.public, region: UNSELECTED_PRIVATE_PROBE.region, - k6Versions: UNSELECTED_PRIVATE_PROBE.k6Versions, disable_browser_checks: UNSELECTED_PRIVATE_PROBE.capabilities.disableBrowserChecks, disable_scripted_checks: UNSELECTED_PRIVATE_PROBE.capabilities.disableScriptedChecks, }, From 760d36a1af97375451c28ccc6797a54b14bdef79 Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Tue, 14 Oct 2025 13:00:52 -0300 Subject: [PATCH 8/9] fix: display supported version in incompatible probes --- .../CheckEditor/CheckProbes/ProbesList.tsx | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/components/CheckEditor/CheckProbes/ProbesList.tsx b/src/components/CheckEditor/CheckProbes/ProbesList.tsx index 4c2ab5490..c445710a7 100644 --- a/src/components/CheckEditor/CheckProbes/ProbesList.tsx +++ b/src/components/CheckEditor/CheckProbes/ProbesList.tsx @@ -8,6 +8,7 @@ import { CheckFormValues, FeatureName, ProbeWithMetadata } from 'types'; import { useFeatureFlag } from 'hooks/useFeatureFlag'; import { DeprecationNotice } from 'components/DeprecationNotice/DeprecationNotice'; import { ProbeStatus } from 'components/ProbeCard/ProbeStatus'; +import { getFormattedK6Versions } from 'components/ProbeStatus/ProbeStatus'; export const ProbesList = ({ title, @@ -41,14 +42,6 @@ export const ProbesList = ({ return { selectedChannel, isScriptedOrBrowser }; }, [getValues]); - const getProbeK6Version = (probe: ProbeWithMetadata) => { - if (!isVersionManagementEnabled || !isScriptedOrBrowser || !selectedChannel) { - return null; // Feature not enabled or not relevant for non-scripted/browser checks - } - - return probe.k6Versions?.[selectedChannel] || null; - }; - const isProbeCompatible = (probe: ProbeWithMetadata): boolean => { if (!isVersionManagementEnabled || !isScriptedOrBrowser || !selectedChannel) { return true; @@ -57,6 +50,26 @@ export const ProbesList = ({ return probe.k6Versions?.[selectedChannel] !== null && probe.k6Versions?.[selectedChannel] !== undefined; }; + const getProbeK6Version = (probe: ProbeWithMetadata) => { + if (!isVersionManagementEnabled || !isScriptedOrBrowser) { + return null; // Feature not enabled or not relevant for non-scripted/browser checks + } + + if (!selectedChannel) { + return null; + } + + const isCompatible = isProbeCompatible(probe); + + if (isCompatible) { + // For compatible probes, show the version for the selected channel + return probe.k6Versions?.[selectedChannel] || null; + } else { + // For incompatible probes, show all supported versions + return getFormattedK6Versions(probe) || null; + } + }; + const handleToggleAll = () => { if (allProbesSelected) { onSelectionChange(selectedProbes.filter((id) => !probes.some((probe) => probe.id === id))); From 8a017c57abfe9d21af0e781e8d7d603f17605e07 Mon Sep 17 00:00:00 2001 From: Virginia Cepeda Date: Tue, 14 Oct 2025 13:14:00 -0300 Subject: [PATCH 9/9] fix: tests --- .../FormComponents/K6ChannelSelect.test.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/CheckEditor/FormComponents/K6ChannelSelect.test.tsx b/src/components/CheckEditor/FormComponents/K6ChannelSelect.test.tsx index fc4027308..33da88651 100644 --- a/src/components/CheckEditor/FormComponents/K6ChannelSelect.test.tsx +++ b/src/components/CheckEditor/FormComponents/K6ChannelSelect.test.tsx @@ -83,7 +83,7 @@ describe('K6ChannelSelect', () => { }); }); - it('should auto-select the first available channel when default is deprecated', async () => { + it('should select the default channel when available', async () => { mockFeatureToggles({ [FeatureName.VersionManagement]: true, }); @@ -96,15 +96,16 @@ describe('K6ChannelSelect', () => { await waitFor(() => { const combobox = screen.getByLabelText(/k6 version/i); - // v1 is the default but deprecated, so v2 should be selected as the first available - expect(combobox).toHaveValue('v2'); + // v1 is the default and available, so it should be selected + expect(combobox).toHaveValue('v1'); }); - // v1 should not be in the options since it's deprecated for new checks + // v1 should be in the options since it's not deprecated for new checks const combobox = screen.getByLabelText(/k6 version/i); const selectElement = combobox as HTMLSelectElement; const v1Option = Array.from(selectElement.options).find(opt => opt.value === 'v1'); - expect(v1Option).toBeUndefined(); + expect(v1Option).toBeDefined(); + expect(v1Option?.textContent).toContain('v1.x (default)'); }); it('should display error message when channels fail to load', async () => {