Display server weight in service detail view

This commit is contained in:
Murat Aslan
2026-03-30 17:58:22 +03:00
committed by GitHub
parent f15b836c86
commit 9a8ff969ac
70 changed files with 614 additions and 657 deletions
+1 -1
View File
@@ -49,7 +49,7 @@
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.2.1",
"@testing-library/user-event": "^14.5.2",
"@traefiklabs/faency": "12.0.4",
"@traefik-labs/faency": "12.0.7",
"@types/lodash": "^4.17.16",
"@types/node": "^22.15.18",
"@types/react": "^18.2.0",
+1 -1
View File
@@ -1,4 +1,4 @@
import { globalCss, Box, darkTheme, FaencyProvider, lightTheme } from '@traefiklabs/faency'
import { globalCss, Box, darkTheme, FaencyProvider, lightTheme } from '@traefik-labs/faency'
import { Suspense, useContext, useEffect } from 'react'
import { HelmetProvider } from 'react-helmet-async'
import { HashRouter, Navigate, Route, Routes as RouterRoutes, useLocation } from 'react-router-dom'
+1 -1
View File
@@ -1,4 +1,4 @@
import { CSS, Text } from '@traefiklabs/faency'
import { CSS, Text } from '@traefik-labs/faency'
import { useContext } from 'react'
import CopyButton from 'components/buttons/CopyButton'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Card, styled } from '@traefiklabs/faency'
import { Card, styled } from '@traefik-labs/faency'
const ScrollableCard = styled(Card, {
width: '100%',
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { motion } from 'framer-motion'
import { FiLoader } from 'react-icons/fi'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AccessibleIcon, Button } from '@traefiklabs/faency'
import { AccessibleIcon, Button } from '@traefik-labs/faency'
import { FiMoon, FiSun } from 'react-icons/fi'
import { AutoThemeIcon } from 'components/icons/AutoThemeIcon'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Box, Button, Flex, styled, Text } from '@traefiklabs/faency'
import { Box, Button, Flex, styled, Text } from '@traefik-labs/faency'
import { AnimatePresence, motion } from 'framer-motion'
import { ReactNode, useEffect } from 'react'
import { FiX } from 'react-icons/fi'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { useContext } from 'react'
import { Toast } from './Toast'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Button, Flex, Text, Tooltip as FaencyTooltip } from '@traefiklabs/faency'
import { Button, Flex, Text, Tooltip as FaencyTooltip } from '@traefik-labs/faency'
import { MouseEvent, ReactNode, useMemo, useState } from 'react'
import { FiCheck, FiCopy } from 'react-icons/fi'
+1 -1
View File
@@ -1,4 +1,4 @@
import { CSS, Text } from '@traefiklabs/faency'
import { CSS, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import Tooltip from 'components/Tooltip'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex, Button, CSS, AccessibleIcon } from '@traefiklabs/faency'
import { Flex, Button, CSS, AccessibleIcon } from '@traefik-labs/faency'
import React, { useState } from 'react'
import { FiCheck, FiCopy } from 'react-icons/fi'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Button, Flex, Text } from '@traefiklabs/faency'
import { Button, Flex, Text } from '@traefik-labs/faency'
import { ComponentProps, ReactNode } from 'react'
type IconButtonProps = ComponentProps<typeof Button> & {
@@ -1,4 +1,4 @@
import { Button } from '@traefiklabs/faency'
import { Button } from '@traefik-labs/faency'
import { useCallback, useEffect, useState } from 'react'
export const ScrollTopButton = () => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { styled, Flex, Label } from '@traefiklabs/faency'
import { styled, Flex, Label } from '@traefik-labs/faency'
import { ComponentProps } from 'react'
import SortIcon from 'components/icons/SortIcon'
+1 -1
View File
@@ -1,4 +1,4 @@
import { config, Flex } from '@traefiklabs/faency'
import { config, Flex } from '@traefik-labs/faency'
import { useEffect, useState } from 'react'
import { CustomIconProps } from 'components/icons'
+1 -1
View File
@@ -1,4 +1,4 @@
import { CSS, Flex, VariantProps } from '@traefiklabs/faency'
import { CSS, Flex, VariantProps } from '@traefik-labs/faency'
import { HTMLAttributes } from 'react'
export type CustomIconProps = HTMLAttributes<SVGElement> & {
@@ -1,4 +1,4 @@
import { Box } from '@traefiklabs/faency'
import { Box } from '@traefik-labs/faency'
import { HTMLAttributes, useMemo } from 'react'
import Consul from 'components/icons/providers/Consul'
@@ -1,4 +1,4 @@
import { Card, Flex, H1, Skeleton, Text } from '@traefiklabs/faency'
import { Card, Flex, H1, Skeleton, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import MiddlewareDefinition from './MiddlewareDefinition'
@@ -1,4 +1,4 @@
import { Badge, CSS, Flex, styled, Text } from '@traefiklabs/faency'
import { Badge, CSS, Flex, styled, Text } from '@traefik-labs/faency'
import { ReactNode } from 'react'
import { BsToggleOff, BsToggleOn } from 'react-icons/bs'
@@ -1,4 +1,4 @@
import { Card, CSS, Flex, Grid, H2, Skeleton, styled, Text } from '@traefiklabs/faency'
import { Card, CSS, Flex, Grid, H2, Skeleton, styled, Text } from '@traefik-labs/faency'
import { Fragment, ReactNode, useMemo } from 'react'
import ScrollableCard from 'components/ScrollableCard'
@@ -1,4 +1,4 @@
import { Box, Card, Flex, Grid, Skeleton as FaencySkeleton, Text } from '@traefiklabs/faency'
import { Box, Card, Flex, Grid, Skeleton as FaencySkeleton, Text } from '@traefik-labs/faency'
import ResourceCard from 'components/resources/ResourceCard'
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTr, Flex, Text } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTr, Flex, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import Status from './Status'
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTr, Badge, Flex, Text } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTr, Badge, Flex, Text } from '@traefik-labs/faency'
import Tooltip from 'components/Tooltip'
@@ -1,4 +1,4 @@
import { Card, CSS, Flex, Text } from '@traefiklabs/faency'
import { Card, CSS, Flex, Text } from '@traefik-labs/faency'
import { ReactNode } from 'react'
type ResourceCardProps = {
@@ -1,4 +1,4 @@
import { Card, Flex, Skeleton } from '@traefiklabs/faency'
import { Card, Flex, Skeleton } from '@traefik-labs/faency'
import { FiAlertTriangle } from 'react-icons/fi'
import { SectionTitle } from './DetailsCard'
@@ -1,4 +1,4 @@
import { Box, Flex, styled, Text } from '@traefiklabs/faency'
import { Box, Flex, styled, Text } from '@traefik-labs/faency'
import { ReactNode } from 'react'
import { colorByStatus, iconByStatus } from 'components/resources/Status'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Box, CSS } from '@traefiklabs/faency'
import { Box, CSS } from '@traefik-labs/faency'
import { ReactNode } from 'react'
import { FiAlertCircle, FiAlertTriangle, FiCheckCircle, FiLoader } from 'react-icons/fi'
@@ -1,4 +1,4 @@
import { Box, Card, Flex, H3, Skeleton, styled, Text } from '@traefiklabs/faency'
import { Box, Card, Flex, H3, Skeleton, styled, Text } from '@traefik-labs/faency'
import { Chart as ChartJs, ArcElement, Tooltip } from 'chart.js'
import { ReactNode, useEffect, useMemo, useState } from 'react'
import { Doughnut } from 'react-chartjs-2'
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { orderBy } from 'lodash'
import { useContext, useEffect, useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
@@ -1,4 +1,4 @@
import { Flex, H1, Skeleton, Text } from '@traefiklabs/faency'
import { Flex, H1, Skeleton, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import { DetailsCardSkeleton } from 'components/resources/DetailsCard'
@@ -1,4 +1,4 @@
import { Card, Flex, styled, Link, Tooltip, Box, Text, Skeleton } from '@traefiklabs/faency'
import { Card, Flex, styled, Link, Tooltip, Box, Text, Skeleton } from '@traefik-labs/faency'
import { useMemo } from 'react'
import { FiArrowRight, FiGlobe, FiLayers, FiLogIn, FiZap } from 'react-icons/fi'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Badge, Box, Card, Flex } from '@traefiklabs/faency'
import { Badge, Box, Card, Flex } from '@traefik-labs/faency'
import { useMemo } from 'react'
import TlsIcon from './TlsIcon'
@@ -1,4 +1,4 @@
import { Flex, Text } from '@traefiklabs/faency'
import { Flex, Text } from '@traefik-labs/faency'
import { FiGlobe } from 'react-icons/fi'
import { getProviderFromName } from './Servers'
+44 -51
View File
@@ -1,4 +1,4 @@
import { Flex, Text } from '@traefiklabs/faency'
import { Flex, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import { FiGlobe } from 'react-icons/fi'
@@ -16,29 +16,18 @@ type ServersProps = {
type Server = {
url?: string
address?: string
weight?: number
}
type ServerStatus = {
[server: string]: string
}
function getServerStatusList(data: Service.Details): ServerStatus {
const serversList: ServerStatus = {}
data.loadBalancer?.servers?.forEach((server: Server) => {
const serverKey = server.address || server.url
if (serverKey) {
serversList[serverKey] = 'DOWN'
}
})
if (data.serverStatus) {
Object.entries(data.serverStatus).forEach(([server, status]) => {
serversList[server] = status
})
function getServerStatusList(data: Service.Details) {
if (!data?.loadBalancer?.servers) {
return []
}
return serversList
return data.loadBalancer?.servers?.map((server: Server) => ({
url: server.address || server.url,
status: data.serverStatus?.[server.address || server.url || '-'] || 'DOWN',
weight: server.weight ?? 1,
}))
}
export const getProviderFromName = (serviceName: string, defaultProvider: string): string => {
@@ -47,7 +36,7 @@ export const getProviderFromName = (serviceName: string, defaultProvider: string
}
const Servers = ({ data, protocol }: ServersProps) => {
const serversList = getServerStatusList(data)
const serversList = useMemo(() => getServerStatusList(data), [data])
const isTcp = useMemo(() => protocol === 'tcp', [protocol])
const isUdp = useMemo(() => protocol === 'udp', [protocol])
@@ -57,35 +46,39 @@ const Servers = ({ data, protocol }: ServersProps) => {
return (
<Flex direction="column" gap={2}>
<SectionTitle icon={<FiGlobe size={20} />} title="Servers" />
<PaginatedTable
data={Object.entries(serversList).map(([server, status]) => ({
server,
status,
}))}
columns={[
...(isUdp ? [] : [{ key: 'status' as const, header: 'Status' }]),
{ key: 'server' as const, header: isTcp ? 'Address' : 'URL' },
]}
testId="servers-list"
renderCell={(key, value) => {
if (key === 'status') {
return (
<Flex align="center" gap={2}>
<ResourceStatus status={value === 'UP' ? 'enabled' : 'disabled'} />
<Text css={{ color: value === 'UP' ? colorByStatus.success : colorByStatus.disabled }}>{value}</Text>
</Flex>
)
}
if (key === 'server') {
return (
<Tooltip label={value} action="copy">
<Text>{value}</Text>
</Tooltip>
)
}
return <Text>{value}</Text>
}}
/>
{serversList?.length > 0 && (
<PaginatedTable
data={serversList?.map(({ url, status, weight }) => ({
server: url,
status,
weight,
}))}
columns={[
...(isUdp ? [] : [{ key: 'status' as const, header: 'Status' }]),
{ key: 'server' as const, header: isTcp ? 'Address' : 'URL' },
...(isUdp ? [] : [{ key: 'weight' as const, header: 'Weight' }]),
]}
testId={`${protocol}-servers-list`}
renderCell={(key, value) => {
if (key === 'status') {
return (
<Flex align="center" gap={2}>
<ResourceStatus status={value === 'UP' ? 'enabled' : 'disabled'} />
<Text css={{ color: value === 'UP' ? colorByStatus.success : colorByStatus.disabled }}>{value}</Text>
</Flex>
)
}
if (key === 'server') {
return (
<Tooltip label={value as string} action="copy">
<Text>{value}</Text>
</Tooltip>
)
}
return <Text>{value}</Text>
}}
/>
)}
</Flex>
)
}
@@ -1,4 +1,4 @@
import { Badge } from '@traefiklabs/faency'
import { Badge } from '@traefik-labs/faency'
import { useMemo } from 'react'
import ProviderIcon from 'components/icons/providers'
@@ -1,4 +1,4 @@
import { Box, Flex, H1, Skeleton, Text } from '@traefiklabs/faency'
import { Box, Flex, H1, Skeleton, Text } from '@traefik-labs/faency'
import MirrorServices from './MirrorServices'
import Servers from './Servers'
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { FiGlobe } from 'react-icons/fi'
import { getProviderFromName } from './utils'
@@ -9,7 +9,7 @@ import {
VariantProps,
AriaThead,
AriaTh,
} from '@traefiklabs/faency'
} from '@traefik-labs/faency'
import { ReactNode } from 'react'
type AriaTableSkeletonProps = {
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTr, VariantProps, styled } from '@traefiklabs/faency'
import { AriaTr, VariantProps, styled } from '@traefik-labs/faency'
import { ComponentProps, forwardRef, ReactNode } from 'react'
import { useHrefWithReturnTo } from 'hooks/use-href-with-return-to'
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaThead, AriaTr, Box, Button, Flex, Text } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaThead, AriaTr, Box, Button, Flex, Text } from '@traefik-labs/faency'
import { ReactNode, useEffect, useRef, useState } from 'react'
import { FiChevronLeft, FiChevronRight, FiChevronsLeft, FiChevronsRight } from 'react-icons/fi'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTh, CSS, Flex, Label } from '@traefiklabs/faency'
import { AriaTh, CSS, Flex, Label } from '@traefik-labs/faency'
import { useCallback, useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Box, Button, Flex, TextField, InputHandle } from '@traefiklabs/faency'
import { Box, Button, Flex, TextField, InputHandle } from '@traefik-labs/faency'
import { isUndefined, omitBy } from 'lodash'
import { useCallback, useRef, useState } from 'react'
import { FiSearch, FiXCircle } from 'react-icons/fi'
@@ -1,4 +1,4 @@
import { AriaTd, AriaTr } from '@traefiklabs/faency'
import { AriaTd, AriaTr } from '@traefik-labs/faency'
import { stringify } from 'query-string'
import { ReactNode } from 'react'
import useSWRInfinite, { SWRInfiniteConfiguration } from 'swr/infinite'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex, styled } from '@traefiklabs/faency'
import { Flex, styled } from '@traefik-labs/faency'
import breakpoints from 'utils/breakpoints'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTd, Flex, Text } from '@traefiklabs/faency'
import { AriaTd, Flex, Text } from '@traefik-labs/faency'
import { FiAlertTriangle } from 'react-icons/fi'
type EmptyPlaceholderProps = {
+1 -1
View File
@@ -1,4 +1,4 @@
import { Box, Button, Text } from '@traefiklabs/faency'
import { Box, Button, Text } from '@traefik-labs/faency'
import { FallbackProps } from 'react-error-boundary'
const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex, globalCss, styled } from '@traefiklabs/faency'
import { Flex, globalCss, styled } from '@traefik-labs/faency'
import { ReactNode, useMemo, useState } from 'react'
import { useLocation } from 'react-router-dom'
+1 -1
View File
@@ -10,7 +10,7 @@ import {
Text,
Tooltip,
VisuallyHidden,
} from '@traefiklabs/faency'
} from '@traefik-labs/faency'
import { useContext, useEffect, useMemo, useState } from 'react'
import { BsChevronDoubleRight, BsChevronDoubleLeft } from 'react-icons/bs'
import { matchPath, useHref } from 'react-router'
+1 -1
View File
@@ -12,7 +12,7 @@ import {
Link,
Text,
Tooltip,
} from '@traefiklabs/faency'
} from '@traefik-labs/faency'
import { useContext, useMemo } from 'react'
import { Helmet } from 'react-helmet-async'
import { FiBookOpen, FiChevronLeft, FiGithub, FiHeart, FiHelpCircle } from 'react-icons/fi'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Box, Button, Flex, H1, Text } from '@traefiklabs/faency'
import { Box, Button, Flex, H1, Text } from '@traefik-labs/faency'
import { useNavigate } from 'react-router-dom'
import PageTitle from 'layout/PageTitle'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Card, CSS, Flex, Grid, H2, Text } from '@traefiklabs/faency'
import { Card, CSS, Flex, Grid, H2, Text } from '@traefik-labs/faency'
import { ReactNode, useMemo } from 'react'
import useSWR from 'swr'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Box, Flex } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Box, Flex } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+45 -3
View File
@@ -89,9 +89,10 @@ describe('<HttpServicePage />', () => {
expect(serviceDetails.innerHTML).toContain('Pass host header')
expect(serviceDetails.innerHTML).toContain('True')
const serversList = getByTestId('servers-list')
const serversList = getByTestId('http-servers-list')
expect(serversList.childNodes.length).toBe(1)
expect(serversList.innerHTML).toContain('http://10.0.1.12:80')
expect(serversList.innerHTML).toContain('1')
const routersTable = getByTestId('routers-table')
expect(routersTable.querySelectorAll('a[role="row"]')).toHaveLength(2)
@@ -107,6 +108,47 @@ describe('<HttpServicePage />', () => {
}).toThrow('Unable to find an element by: [data-testid="mirror-services"]')
})
it('should render a service with server weights', async () => {
const mockData = {
loadBalancer: {
servers: [
{
url: 'http://10.0.1.12:80',
weight: 3,
},
{
url: 'http://10.0.1.13:80',
weight: 5,
},
],
passHostHeader: true,
},
status: 'enabled',
usedBy: [],
serverStatus: {
'http://10.0.1.12:80': 'UP',
'http://10.0.1.13:80': 'UP',
},
name: 'service-weighted',
provider: 'docker',
type: 'loadbalancer',
routers: [],
}
const { getByTestId } = renderWithProviders(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<ServiceDetail name="mock-service" data={mockData as any} error={undefined} protocol="http" />,
{ route: '/http/services/mock-service', withPage: true },
)
const serversList = getByTestId('http-servers-list')
expect(serversList.childNodes.length).toBe(2)
expect(serversList.innerHTML).toContain('http://10.0.1.12:80')
expect(serversList.innerHTML).toContain('http://10.0.1.13:80')
expect(serversList.innerHTML).toContain('3')
expect(serversList.innerHTML).toContain('5')
})
it('should render a service with health check', async () => {
const mockData = {
loadBalancer: {
@@ -217,7 +259,7 @@ describe('<HttpServicePage />', () => {
}).toThrow('Unable to find an element by: [data-testid="health-check"]')
expect(() => {
getByTestId('servers-list')
}).toThrow('Unable to find an element by: [data-testid="servers-list"]')
getByTestId('http-servers-list')
}).toThrow('Unable to find an element by: [data-testid="http-servers-list"]')
})
})
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex, Text } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Box, Flex, Image, Link, Text } from '@traefiklabs/faency'
import { Box, Flex, Image, Link, Text } from '@traefik-labs/faency'
import { useMemo, useEffect, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useParams } from 'react-router-dom'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Badge, Box, Flex, Text } from '@traefiklabs/faency'
import { Badge, Box, Flex, Text } from '@traefik-labs/faency'
import { useContext, useState } from 'react'
import { BsChevronRight } from 'react-icons/bs'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { useId } from 'react'
import { CustomIconProps } from 'components/icons'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { CustomIconProps } from 'components/icons'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { useId } from 'react'
import { CustomIconProps } from 'components/icons'
+1 -1
View File
@@ -1,4 +1,4 @@
import { Flex } from '@traefiklabs/faency'
import { Flex } from '@traefik-labs/faency'
import { useId } from 'react'
import { CustomIconProps } from 'components/icons'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Box, Flex } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Box, Flex } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+49 -2
View File
@@ -101,9 +101,10 @@ describe('<TcpServicePage />', () => {
expect(healthCheck.innerHTML).toContain('Expect')
expect(healthCheck.innerHTML).toContain('PONG')
const serversList = getByTestId('servers-list')
const serversList = getByTestId('tcp-servers-list')
expect(serversList.childNodes.length).toBe(1)
expect(serversList.innerHTML).toContain('http://10.0.1.12:80')
expect(serversList.innerHTML).toContain('1')
const routersTable = getByTestId('routers-table')
expect(routersTable.querySelectorAll('a[role="row"]')).toHaveLength(1)
@@ -113,6 +114,11 @@ describe('<TcpServicePage />', () => {
it('should render the service servers from the serverStatus property', async () => {
const mockData = {
loadBalancer: {
servers: [
{
address: 'http://10.0.1.12:81',
},
],
terminationDelay: 10,
},
status: 'enabled',
@@ -154,7 +160,7 @@ describe('<TcpServicePage />', () => {
{ route: '/tcp/services/mock-service', withPage: true },
)
const serversList = getByTestId('servers-list')
const serversList = getByTestId('tcp-servers-list')
expect(serversList.childNodes.length).toBe(1)
expect(serversList.innerHTML).toContain('http://10.0.1.12:81')
@@ -185,6 +191,47 @@ describe('<TcpServicePage />', () => {
}).toThrow('Unable to find an element by: [data-testid="routers-table"]')
})
it('should render the service with server weights', async () => {
const mockData = {
loadBalancer: {
servers: [
{
address: '10.0.1.12:80',
weight: 3,
},
{
address: '10.0.1.13:80',
weight: 7,
},
],
terminationDelay: 10,
},
serverStatus: {
'10.0.1.12:80': 'UP',
'10.0.1.13:80': 'UP',
},
status: 'enabled',
usedBy: [],
name: 'service-weighted-servers',
provider: 'docker',
type: 'loadbalancer',
routers: [],
}
const { getByTestId } = renderWithProviders(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<ServiceDetail name="mock-service" data={mockData as any} error={undefined} protocol="tcp" />,
{ route: '/tcp/services/mock-service', withPage: true },
)
const serversList = getByTestId('tcp-servers-list')
expect(serversList.childNodes.length).toBe(2)
expect(serversList.innerHTML).toContain('10.0.1.12:80')
expect(serversList.innerHTML).toContain('10.0.1.13:80')
expect(serversList.innerHTML).toContain('3')
expect(serversList.innerHTML).toContain('7')
})
it('should render weighted services', async () => {
const mockData = {
weighted: {
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex, Text } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+7 -2
View File
@@ -79,7 +79,7 @@ describe('<UdpServicePage />', () => {
expect(serviceDetails.innerHTML).toContain('Termination delay')
expect(serviceDetails.innerHTML).toContain('10 ms')
const serversList = getByTestId('servers-list')
const serversList = getByTestId('udp-servers-list')
expect(serversList.childNodes.length).toBe(1)
expect(serversList.innerHTML).toContain('http://10.0.1.12:80')
@@ -91,6 +91,11 @@ describe('<UdpServicePage />', () => {
it('should render the service servers from the serverStatus property', async () => {
const mockData = {
loadBalancer: {
servers: [
{
address: 'http://10.0.1.12:81',
},
],
terminationDelay: 10,
},
status: 'enabled',
@@ -132,7 +137,7 @@ describe('<UdpServicePage />', () => {
{ route: '/udp/services/mock-service', withPage: true },
)
const serversList = getByTestId('servers-list')
const serversList = getByTestId('udp-servers-list')
expect(serversList.childNodes.length).toBe(1)
expect(serversList.innerHTML).toContain('http://10.0.1.12:81')
+1 -1
View File
@@ -1,4 +1,4 @@
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex, Text } from '@traefiklabs/faency'
import { AriaTable, AriaTbody, AriaTd, AriaTfoot, AriaThead, AriaTr, Flex, Text } from '@traefik-labs/faency'
import { useMemo } from 'react'
import useInfiniteScroll from 'react-infinite-scroll-hook'
import { useSearchParams } from 'react-router-dom'
+1 -1
View File
@@ -1,5 +1,5 @@
import { cleanup, render } from '@testing-library/react'
import { FaencyProvider } from '@traefiklabs/faency'
import { FaencyProvider } from '@traefik-labs/faency'
import { HelmetProvider } from 'react-helmet-async'
import { MemoryRouter } from 'react-router-dom'
import { SWRConfig } from 'swr'
+404 -534
View File
File diff suppressed because it is too large Load Diff