diff --git a/plugins/kiali/dev/__fixtures__/general/istioValidations.json b/plugins/kiali/dev/__fixtures__/general/istioValidations.json index 2e03262db2..612b8f42d7 100644 --- a/plugins/kiali/dev/__fixtures__/general/istioValidations.json +++ b/plugins/kiali/dev/__fixtures__/general/istioValidations.json @@ -32,8 +32,8 @@ }, "travel-control": { "errors": 0, - "objectCount": 0, - "warnings": 0 + "objectCount": 1, + "warnings": 1 }, "travel-portal": { "errors": 0, diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/health/app.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/health/app.json index 84a00bf3c8..a73a7af661 100644 --- a/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/health/app.json +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-agency/health/app.json @@ -50,7 +50,7 @@ "desiredReplicas": 1, "currentReplicas": 1, "availableReplicas": 1, - "syncedProxies": 1 + "syncedProxies": 0 } ], "requests": { diff --git a/plugins/kiali/dev/__fixtures__/namespaces/travel-control/health/app.json b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/health/app.json index 2800f2b95a..79a6902cd0 100644 --- a/plugins/kiali/dev/__fixtures__/namespaces/travel-control/health/app.json +++ b/plugins/kiali/dev/__fixtures__/namespaces/travel-control/health/app.json @@ -4,7 +4,7 @@ { "name": "control", "desiredReplicas": 1, - "currentReplicas": 1, + "currentReplicas": 0, "availableReplicas": 1, "syncedProxies": 1 } diff --git a/plugins/kiali/src/components/About/AboutUIModal.tsx b/plugins/kiali/src/components/About/AboutUIModal.tsx index 7db9af15a9..b3c973bbd6 100644 --- a/plugins/kiali/src/components/About/AboutUIModal.tsx +++ b/plugins/kiali/src/components/About/AboutUIModal.tsx @@ -86,7 +86,12 @@ export const AboutUIModal = (props: AboutUIModalProps) => { {name} - + {additionalInfo} @@ -158,19 +163,19 @@ export const AboutUIModal = (props: AboutUIModalProps) => { Kiali - + {coreVersion || 'Unknown'} Kiali Container - + {containerVersion || 'Unknown'} Service Mesh - + {meshVersion || 'Unknown'} diff --git a/plugins/kiali/src/components/MessageCenter/AlertDrawer.tsx b/plugins/kiali/src/components/MessageCenter/AlertDrawer.tsx index 2b5d3b19fa..2ce0118ecd 100644 --- a/plugins/kiali/src/components/MessageCenter/AlertDrawer.tsx +++ b/plugins/kiali/src/components/MessageCenter/AlertDrawer.tsx @@ -61,7 +61,7 @@ const noNotificationsMessage = ( export const AlertDrawer = (props: AlertDrawerProps) => { return ( - + @@ -74,6 +74,7 @@ export const AlertDrawer = (props: AlertDrawerProps) => { } + data-test="message-center-summary" > {group.title} {getUnreadMessageLabel(group.messages)} diff --git a/plugins/kiali/src/components/MessageCenter/AlertDrawerGroup.tsx b/plugins/kiali/src/components/MessageCenter/AlertDrawerGroup.tsx index 839be0834f..9a59f99384 100644 --- a/plugins/kiali/src/components/MessageCenter/AlertDrawerGroup.tsx +++ b/plugins/kiali/src/components/MessageCenter/AlertDrawerGroup.tsx @@ -59,7 +59,10 @@ export const AlertDrawerGroup = (props: AlertDrawerGroupProps) => { return ( - + {group.messages.length === 0 && noNotificationsMessage} {getMessages().map(message => ( @@ -67,10 +70,18 @@ export const AlertDrawerGroup = (props: AlertDrawerGroupProps) => { {group.showActions && group.messages.length > 0 && ( - - diff --git a/plugins/kiali/src/components/MessageCenter/AlertDrawerMessage.tsx b/plugins/kiali/src/components/MessageCenter/AlertDrawerMessage.tsx index 6861cbf894..bbe02dbebd 100644 --- a/plugins/kiali/src/components/MessageCenter/AlertDrawerMessage.tsx +++ b/plugins/kiali/src/components/MessageCenter/AlertDrawerMessage.tsx @@ -46,7 +46,7 @@ export const AlertDrawerMessage = (props: AlertDrawerMessageProps) => { return ( - + {getIcon(props.message.type)}{' '} {props.message.seen ? ( props.message.content @@ -60,11 +60,17 @@ export const AlertDrawerMessage = (props: AlertDrawerMessageProps) => { onClick={() => toggleMessageDetail(props.message)} expandIcon={} > - + {props.message.showDetail ? 'Hide Detail' : 'Show Detail'} - +
                 {props.message.detail}
               
diff --git a/plugins/kiali/src/components/MessageCenter/MessageCenter.tsx b/plugins/kiali/src/components/MessageCenter/MessageCenter.tsx index 3c3c09e5ba..b28704433b 100644 --- a/plugins/kiali/src/components/MessageCenter/MessageCenter.tsx +++ b/plugins/kiali/src/components/MessageCenter/MessageCenter.tsx @@ -89,7 +89,11 @@ export const MessageCenter = (props: { color?: string }) => { */ return ( <> - { color="primary" icon={} label={homeCluster?.name} + data-test="home-cluster" /> @@ -35,6 +36,7 @@ export const KialiHeader = () => { flexDirection: 'row', justifyContent: 'space-between', }} + data-test="user" > User : diff --git a/plugins/kiali/src/pages/Kiali/Header/NamespaceSelector.tsx b/plugins/kiali/src/pages/Kiali/Header/NamespaceSelector.tsx index d7df38c588..57f0acee9d 100644 --- a/plugins/kiali/src/pages/Kiali/Header/NamespaceSelector.tsx +++ b/plugins/kiali/src/pages/Kiali/Header/NamespaceSelector.tsx @@ -56,6 +56,7 @@ export const NamespaceSelector = (props: { page?: boolean }) => { onChange={handleChange} renderValue={selected => (selected as string[]).join(', ')} MenuProps={MenuProps} + data-test="namespace-selector" > {(kialiState.namespaces.items || []).map(ns => ( diff --git a/plugins/kiali/src/pages/Overview/OverviewCard/NamespaceLabels.tsx b/plugins/kiali/src/pages/Overview/OverviewCard/NamespaceLabels.tsx index aba4c70b89..b12094e302 100644 --- a/plugins/kiali/src/pages/Overview/OverviewCard/NamespaceLabels.tsx +++ b/plugins/kiali/src/pages/Overview/OverviewCard/NamespaceLabels.tsx @@ -13,7 +13,7 @@ export const NamespaceLabels = (props: NamespaceLabelsprops) => { ? `${Object.entries(props.labels).length}` : 'No'; const tooltipTitle = ( -
    +
      {Object.entries(props.labels || []).map(([key, value]) => (
    • {key}={value} @@ -27,7 +27,7 @@ export const NamespaceLabels = (props: NamespaceLabelsprops) => { {labelsLength} label{labelsLength !== '1' ? 's' : ''} - + diff --git a/plugins/kiali/src/pages/Overview/OverviewCard/OverviewCard.tsx b/plugins/kiali/src/pages/Overview/OverviewCard/OverviewCard.tsx index e141afd26a..548aacf9a0 100644 --- a/plugins/kiali/src/pages/Overview/OverviewCard/OverviewCard.tsx +++ b/plugins/kiali/src/pages/Overview/OverviewCard/OverviewCard.tsx @@ -106,12 +106,13 @@ export const OverviewCard = (props: OverviewCardProps) => { errors={validations.errors} warnings={validations.warnings} objectCount={validations.objectCount} + data-test="validation-summary" /> ); }; return ( - + {!props.entity && } {!props.entity && isMultiCluster && props.namespace.cluster && ( diff --git a/plugins/kiali/src/pages/Overview/OverviewStatus.tsx b/plugins/kiali/src/pages/Overview/OverviewStatus.tsx index 30218ad873..12f76d784d 100644 --- a/plugins/kiali/src/pages/Overview/OverviewStatus.tsx +++ b/plugins/kiali/src/pages/Overview/OverviewStatus.tsx @@ -43,8 +43,10 @@ export class OverviewStatus extends React.Component { items.push(`and ${length - items.length} more...`); } const tooltipContent = ( -
      - {this.props.status.name} +
      + + {this.props.status.name} + {items.map((app, idx) => { return (
      { + const ns_card = await page.locator(`data-test=overview-card-${ns}`); + await page.click('[aria-label="Health for"]'); + await page.click(`[data-value=${type}]`); + + const icon = await ns_card.locator('data-test=overview-app-health'); + await icon.hover(); + const list = await page.locator('data-test=overview-status'); + + // Wait for the list to appear + await page.waitForSelector('data-test=overview-status'); + + let i = 0; + for (const object of Object.entries(objects)) { + if (i === 5) { + break; + } + await icon.hover({ force: true }).then(async () => { + await expect(list).toContainText(`${object[1].name}`); + }); + i++; + } + const expected = type === 'app' ? 'application' : type; + await expect( + ns_card.locator(`data-test=overview-type-${type}`), + ).toContainText(`${Object.entries(objects).length} ${expected}s`); +} + +function filterByIstioInjectionEnabled(jsonArray: any): any { + return jsonArray.filter( + json => + (json.labels && json.labels['istio-injection'] === 'enabled') || + (json.name && json.name === 'istio-system'), + ); +} +const visibleNamespaces = filterByIstioInjectionEnabled(NAMESPACES); + +test.describe('Kiali page', () => { + let page: Page; + let common: Common; + test.describe('overview', () => { + test.beforeAll(async ({ browser }) => { + const context = await browser.newContext(); + page = await context.newPage(); + common = new Common(page); + await common.loginAsGuest(); + }); + test.beforeEach(async () => { + page.reload(); + }); + + test('Home cluster is visible', async () => { + await expect(page.locator('data-test=home-cluster')).toHaveText( + CONFIG.clusters.Kubernetes.name, + ); + }); + + test('About page can be opened', async () => { + await page.click('data-test=help-button'); + await expect(page.locator('data-test=Kiali')).toContainText( + STATUS.status['Kiali version'], + ); + await expect(page.locator('data-test=Kiali')).toContainText( + STATUS.status['Kiali commit hash'], + ); + await expect(page.locator('data-test=Kiali container')).toContainText( + STATUS.status['Kiali container version'], + ); + await expect(page.locator('data-test=Service Mesh')).toContainText( + STATUS.status['Mesh name'], + ); + await expect(page.locator('data-test=Service Mesh')).toContainText( + STATUS.status['Mesh version'], + ); + + for (const external of STATUS.externalServices) { + let text; + if (external.version) { + text = external.version; + } else { + text = 'N/A'; + } + await expect(page.locator(`data-test=${external.name}`)).toContainText( + text, + ); + } + }); + + test('MessageCenter can be opened', async () => { + await page.click('data-test=message-center'); + await expect( + page.locator('data-test=message-center-modal'), + ).toBeVisible(); + await expect(page.locator('data-test=message-center-summary')).toHaveText( + 'Notifications 2 Unread Messages', + ); + await page.click('data-test=message-center-summary'); + await page.locator( + 'data-test=message-center-messages [data-test=drawer-message]', + ); + }); + + test('Detail of a message in MessageCenter can be opened', async () => { + await page.click('data-test=message-center'); + await page.click('data-test=message-center-summary'); + await page.click('data-test=show-message-detail'); + await expect(page.locator('data-test=message-detail').first()).toHaveText( + 'grafana URL is not set in Kiali configuration', + ); + }); + + test('Detail of a message in MessageCenter can be closed', async () => { + await page.click('data-test=message-center'); + await page.click('data-test=message-center-summary'); + await page.click('data-test=show-message-detail'); + await page.click('data-test=hide-message-detail'); + await expect( + page.locator('data-test=message-detail').first(), + ).toBeHidden(); + }); + + test('Messages can be marked as read', async () => { + await page.click('data-test=message-center'); + await page.click('data-test=message-center-summary'); + await page.click('data-test=mark-as-read'); + await expect(page.locator('data-test=message-center-summary')).toHaveText( + 'Notifications 0 Unread Messages', + ); + }); + + test('Messages can be cleared', async () => { + await page.click('data-test=message-center'); + await page.click('data-test=message-center-summary'); + await page.click('data-test=clear-all'); + await expect(page.locator('data-test=message-center-summary')).toHaveText( + 'Notifications 0 Unread Messages', + ); + await expect( + page.locator('data-test=message-center-messages'), + ).toHaveText('No Messages Available'); + await expect(page.locator('data-test=drawer-message')).toHaveCount(0); + }); + + test('User is visible', async () => { + if (CONFIG.authStrategy === 'anonymous') { + await expect(page.locator('data-test=user')).toHaveText( + 'User : anonymous', + ); + } + // There could be an else branch with non-anonymous auth strategies, but there is not enough test data in the fixtures for this. + }); + + test('Check namespaces visible to Kiali', async () => { + await page.click('data-test=namespace-selector'); + for (const ns of visibleNamespaces) { + await expect(page.locator(`ul[role="listbox"]`)).toContainText(ns.name); + } + await expect(page.locator('ul[role="listbox"] > li')).toHaveCount( + visibleNamespaces.length, + ); + }); + + test('No namespaces are selected', async () => { + await page.click('data-test=namespace-selector'); + for (const ns of visibleNamespaces) { + await page.click(`ul[role="listbox"] > li:has-text("${ns.name}")`); + } + for (const ns of visibleNamespaces) { + await expect( + page.locator(`data-test=overview-card-${ns.name}`), + ).toHaveCount(0); + } + }); + + test('All namespaces are selected', async () => { + for (const ns of visibleNamespaces) { + await expect( + page.locator(`data-test=overview-card-${ns.name}`), + ).toHaveCount(1); + } + }); + + test('Namespace card should have labels', async () => { + const ns = visibleNamespaces[0]; + const ns_card = await page.locator(`data-test=overview-card-${ns.name}`); + const icon = await ns_card.locator('data-test=labels-info-icon'); + await icon.hover(); + const list = await page.locator('data-test=namespace-labels'); + + // Wait for the list to appear + await page.waitForSelector('data-test=namespace-labels'); + + for (const [key, value] of Object.entries(ns.labels)) { + await icon.hover({ force: true }).then(async () => { + await expect(list).toContainText(`${key}=${value}`); + }); + } + await expect(ns_card.locator('#labels_info')).toContainText( + `${Object.entries(ns.labels).length} labels`, + ); + }); + + test('Apps should be reported in the namespace card', async () => { + const ns = BOOKINFO_APPS.namespace.name; + await checkReportedItems('app', ns, BOOKINFO_APPS.applications, page); + }); + + test('Services should be reported in the namespace card', async () => { + const ns = BOOKINFO_SERVICES.namespace.name; + await checkReportedItems('service', ns, BOOKINFO_SERVICES.services, page); + }); + + test('Workloads should be reported in the namespace card', async () => { + const ns = BOOKINFO_WORKLOADS.namespace.name; + await checkReportedItems( + 'workload', + ns, + BOOKINFO_WORKLOADS.workloads, + page, + ); + }); + + test('Healthy apps should be reported in the namespace card', async () => { + const ns_card = await page.locator(`data-test=overview-card-bookinfo`); + const icon = await ns_card.locator('data-test=overview-app-health'); + await icon.hover(); + await page.waitForSelector('data-test=overview-status'); + await expect(ns_card.locator('data-test=Healthy-status')).toBeDefined(); + }); + + test('Degraded apps should be reported in the namespace card', async () => { + const ns_card = await page.locator( + `data-test=overview-card-travel-agency`, + ); + const icon = await ns_card + .locator('[aria-label="Overview status"]') + .first(); + await icon.hover(); + await page.waitForSelector('data-test=overview-status'); + await expect(ns_card.locator('data-test=Degraded-status')).toBeDefined(); + }); + + test('Failed apps should be reported in the namespace card', async () => { + const ns_card = await page.locator( + `data-test=overview-card-travel-control`, + ); + const icon = await ns_card.locator('[aria-label="Overview status"]'); + await icon.hover(); + await page.waitForSelector('data-test=overview-status'); + await expect(ns_card.locator('data-test=Failure-status')).toBeDefined(); + }); + + test('Istio config with success should be reported in the namespace card', async () => { + const ns_card = await page.locator( + `data-test=overview-card-travel-agency`, + ); + await expect( + ns_card.locator('data-test=validation-icon-correct'), + ).toBeVisible(); + }); + + test('Istio config with warning should be reported in the namespace card', async () => { + const ns_card = await page.locator( + `data-test=overview-card-travel-control`, + ); + await expect( + ns_card.locator('data-test=validation-icon-warning'), + ).toBeVisible(); + }); + + test('Istio config with error should be reported in the namespace card', async () => { + const ns_card = await page.locator(`data-test=overview-card-bookinfo`); + await expect( + ns_card.locator('data-test=validation-icon-error'), + ).toBeVisible(); + }); + }); +});