diff --git a/api/src/main/webui/src/components/kafka/ClusterSwitcher.tsx b/api/src/main/webui/src/components/kafka/ClusterSwitcher.tsx index caa38c1ec..030f86f89 100644 --- a/api/src/main/webui/src/components/kafka/ClusterSwitcher.tsx +++ b/api/src/main/webui/src/components/kafka/ClusterSwitcher.tsx @@ -143,7 +143,7 @@ export function ClusterSwitcher({ > {t('kafka.selectCluster')} - + {clusters.length} diff --git a/api/src/main/webui/src/components/kafka/groups/ResetOffset/CliCommandDisplay.tsx b/api/src/main/webui/src/components/kafka/groups/ResetOffset/CliCommandDisplay.tsx index 84258bec4..84e807f08 100644 --- a/api/src/main/webui/src/components/kafka/groups/ResetOffset/CliCommandDisplay.tsx +++ b/api/src/main/webui/src/components/kafka/groups/ResetOffset/CliCommandDisplay.tsx @@ -58,7 +58,7 @@ export function CliCommandDisplay({ } > - {command} + {command} ); diff --git a/api/src/main/webui/src/components/kafka/groups/ResetOffset/OffsetValueSelector.tsx b/api/src/main/webui/src/components/kafka/groups/ResetOffset/OffsetValueSelector.tsx index f1b550e7d..dd97c7a16 100644 --- a/api/src/main/webui/src/components/kafka/groups/ResetOffset/OffsetValueSelector.tsx +++ b/api/src/main/webui/src/components/kafka/groups/ResetOffset/OffsetValueSelector.tsx @@ -116,6 +116,7 @@ export function OffsetValueSelector({ const offsetToggle = ( setIsOffsetSelectOpen(!isOffsetSelectOpen)} isExpanded={isOffsetSelectOpen} diff --git a/api/src/main/webui/src/components/kafka/groups/ResetOffset/TopicPartitionSelector.tsx b/api/src/main/webui/src/components/kafka/groups/ResetOffset/TopicPartitionSelector.tsx index 424e493d8..0878fa9f6 100644 --- a/api/src/main/webui/src/components/kafka/groups/ResetOffset/TopicPartitionSelector.tsx +++ b/api/src/main/webui/src/components/kafka/groups/ResetOffset/TopicPartitionSelector.tsx @@ -83,6 +83,7 @@ export function TopicPartitionSelector({ const topicToggle = ( setIsTopicSelectOpen(!isTopicSelectOpen)} isExpanded={isTopicSelectOpen} @@ -130,6 +131,7 @@ export function TopicPartitionSelector({ const partitionToggle = ( setIsPartitionSelectOpen(!isPartitionSelectOpen)} isExpanded={isPartitionSelectOpen} diff --git a/api/src/main/webui/src/components/kafka/nodes/RebalanceModal.tsx b/api/src/main/webui/src/components/kafka/nodes/RebalanceModal.tsx index f04150364..882691ebf 100644 --- a/api/src/main/webui/src/components/kafka/nodes/RebalanceModal.tsx +++ b/api/src/main/webui/src/components/kafka/nodes/RebalanceModal.tsx @@ -60,7 +60,7 @@ export function RebalanceModal({ rebalance, isOpen, onClose }: RebalanceModalPro - + {optimizationResult?.dataToMoveMB || 0} MB @@ -128,7 +128,7 @@ export function RebalanceModal({ rebalance, isOpen, onClose }: RebalanceModalPro - + {optimizationResult?.monitoredPartitionsPercentage || 0} @@ -164,7 +164,7 @@ export function RebalanceModal({ rebalance, isOpen, onClose }: RebalanceModalPro - + {optimizationResult?.numReplicaMovements || 0} @@ -176,7 +176,7 @@ export function RebalanceModal({ rebalance, isOpen, onClose }: RebalanceModalPro - + {optimizationResult?.onDemandBalancednessScoreAfter || 0} @@ -188,7 +188,7 @@ export function RebalanceModal({ rebalance, isOpen, onClose }: RebalanceModalPro - + {optimizationResult?.onDemandBalancednessScoreBefore || 0} diff --git a/api/src/main/webui/src/components/kafka/nodes/RebalancesTable.tsx b/api/src/main/webui/src/components/kafka/nodes/RebalancesTable.tsx index 763ef76c0..dfe777a98 100644 --- a/api/src/main/webui/src/components/kafka/nodes/RebalancesTable.tsx +++ b/api/src/main/webui/src/components/kafka/nodes/RebalancesTable.tsx @@ -190,7 +190,7 @@ export function RebalancesTable({ {t('rebalancing.autoApprovalEnabled')} - + {rebalance.meta?.autoApproval === true ? 'true' : 'false'} @@ -224,7 +224,7 @@ export function RebalancesTable({ - + {rebalance.attributes.mode === 'full' ? ( t('rebalancing.fullMode') ) : ( diff --git a/api/src/main/webui/src/components/kafka/overview/ClusterCard.tsx b/api/src/main/webui/src/components/kafka/overview/ClusterCard.tsx index 045f72578..6c741f398 100644 --- a/api/src/main/webui/src/components/kafka/overview/ClusterCard.tsx +++ b/api/src/main/webui/src/components/kafka/overview/ClusterCard.tsx @@ -240,7 +240,7 @@ export function ClusterCard({ {/* Kafka Version */} -
+
{isLoading ? ( ) : ( @@ -280,6 +280,7 @@ export function ClusterCard({ {isLoading ? ( Array.from({ length: 5 }).map((_, i) => ( diff --git a/api/src/main/webui/src/components/kafka/overview/ErrorsAndWarnings.tsx b/api/src/main/webui/src/components/kafka/overview/ErrorsAndWarnings.tsx index 39081850d..ee8fe0146 100644 --- a/api/src/main/webui/src/components/kafka/overview/ErrorsAndWarnings.tsx +++ b/api/src/main/webui/src/components/kafka/overview/ErrorsAndWarnings.tsx @@ -38,6 +38,7 @@ export function ErrorsAndWarnings({ setIsExpanded(isOpen)} + toggleId="cluster-warnings-toggle" toggleContent={ {t('ClusterCard.cluster_errors_and_warnings')}{' '} diff --git a/api/src/main/webui/src/components/kafka/overview/RecentTopicsCard.tsx b/api/src/main/webui/src/components/kafka/overview/RecentTopicsCard.tsx index 4ba4e76f5..a2afa8593 100644 --- a/api/src/main/webui/src/components/kafka/overview/RecentTopicsCard.tsx +++ b/api/src/main/webui/src/components/kafka/overview/RecentTopicsCard.tsx @@ -96,7 +96,7 @@ export function RecentTopicsCard({ ) : ( <> <Divider /> - <List isPlain style={{ paddingTop: '1rem' }}> + <List isPlain style={{ paddingTop: '1rem' }} data-ouia-component-id="recent-topics-list"> {viewedTopics.map((topic) => ( <ListItem key={topic.topicId}> <Link diff --git a/api/src/main/webui/src/components/kafka/overview/ReconciliationControls.tsx b/api/src/main/webui/src/components/kafka/overview/ReconciliationControls.tsx index a12bf15ce..2c096c1bc 100644 --- a/api/src/main/webui/src/components/kafka/overview/ReconciliationControls.tsx +++ b/api/src/main/webui/src/components/kafka/overview/ReconciliationControls.tsx @@ -43,7 +43,7 @@ export function ReconciliationControls({ return ( <> {isReconciliationPaused && ( - <Banner color="yellow"> + <Banner color="yellow" data-ouia-component-id="reconciliation-paused-banner"> <Bullseye> <Flex spaceItems={{ default: 'spaceItemsMd' }}> <FlexItem spacer={{ default: 'spacerNone' }}> diff --git a/api/src/main/webui/src/components/kafka/overview/TopicsPartitionsCard.tsx b/api/src/main/webui/src/components/kafka/overview/TopicsPartitionsCard.tsx index a60971372..b01ffcce2 100644 --- a/api/src/main/webui/src/components/kafka/overview/TopicsPartitionsCard.tsx +++ b/api/src/main/webui/src/components/kafka/overview/TopicsPartitionsCard.tsx @@ -108,7 +108,7 @@ export function TopicsPartitionsCard({ > {/* Fully Replicated */} <FlexItem> - <Link to="../topics?status=FullyReplicated" style={{ textDecoration: 'none' }}> + <Link to="../topics?status=FullyReplicated" style={{ textDecoration: 'none' }} data-ouia-component-id="fully-replicated-status-link"> {isLoading ? ( <Skeleton shape="circle" width="1rem" style={{ display: 'inline-block' }} /> ) : ( @@ -130,7 +130,7 @@ export function TopicsPartitionsCard({ {/* Under-replicated */} <FlexItem> - <Link to="../topics?status=UnderReplicated" style={{ textDecoration: 'none' }}> + <Link to="../topics?status=UnderReplicated" style={{ textDecoration: 'none' }} data-ouia-component-id="under-replicated-status-link"> {isLoading ? ( <Skeleton shape="circle" width="1rem" style={{ display: 'inline-block' }} /> ) : ( @@ -152,7 +152,7 @@ export function TopicsPartitionsCard({ {/* Offline */} <FlexItem> - <Link to="../topics?status=Offline" style={{ textDecoration: 'none' }}> + <Link to="../topics?status=Offline" style={{ textDecoration: 'none' }} data-ouia-component-id="offline-status-link"> {isLoading ? ( <Skeleton shape="circle" width="1rem" style={{ display: 'inline-block' }} /> ) : ( diff --git a/api/src/main/webui/src/components/kafka/topics/AdvancedSearch.tsx b/api/src/main/webui/src/components/kafka/topics/AdvancedSearch.tsx index a2a63b909..33dc0ee23 100644 --- a/api/src/main/webui/src/components/kafka/topics/AdvancedSearch.tsx +++ b/api/src/main/webui/src/components/kafka/topics/AdvancedSearch.tsx @@ -241,6 +241,7 @@ export function AdvancedSearch({ value={query} onChange={(_, value) => setQuery(value)} placeholder={t('topics.messages.advancedSearch.queryHelper')} + ouiaId="has-words-input" /> <FormHelperText> <HelperText> @@ -267,6 +268,7 @@ export function AdvancedSearch({ onClick={() => setIsWhereOpen(!isWhereOpen)} isExpanded={isWhereOpen} style={{ width: '100%' }} + ouiaId="where-dropdown-button" > {where === 'value' ? t('topics.messages.advancedSearch.whereValue') : where === 'key' ? t('topics.messages.advancedSearch.whereKey') : @@ -320,6 +322,7 @@ export function AdvancedSearch({ onClick={() => setIsFromOpen(!isFromOpen)} isExpanded={isFromOpen} style={{ width: '100%' }} + ouiaId="messages-from-dropdown-button" > {fromCategory === 'offset' ? t('topics.messages.filter.offset') : fromCategory === 'timestamp' ? t('topics.messages.filter.timestamp') : @@ -391,6 +394,7 @@ export function AdvancedSearch({ onClick={() => setIsRetrieveOpen(!isRetrieveOpen)} isExpanded={isRetrieveOpen} style={{ width: '100%' }} + ouiaId="retrieve-type-dropdown-button" > {retrieveCategory === 'limit' ? 'Number of messages' : t('topics.messages.advancedSearch.continuously')} </MenuToggle> @@ -418,6 +422,7 @@ export function AdvancedSearch({ onClick={() => setIsLimitOpen(!isLimitOpen)} isExpanded={isLimitOpen} style={{ width: '100%' }} + ouiaId="retrieve-limit-dropdown-button" > {limit} </MenuToggle> @@ -464,6 +469,7 @@ export function AdvancedSearch({ onClick={() => setIsPartitionOpen(!isPartitionOpen)} isExpanded={isPartitionOpen} style={{ width: '100%' }} + ouiaId="partition-dropdown-button" > {partition !== undefined ? t('topics.messages.partitionOption', { value: partition }) diff --git a/api/src/main/webui/src/pages/kafka/KafkaLayout.tsx b/api/src/main/webui/src/pages/kafka/KafkaLayout.tsx index 97f49a9a4..85d8a42ef 100644 --- a/api/src/main/webui/src/pages/kafka/KafkaLayout.tsx +++ b/api/src/main/webui/src/pages/kafka/KafkaLayout.tsx @@ -296,7 +296,7 @@ export function KafkaLayout() { </BreadcrumbItem> )} {isGroupDetailPage && ( - <BreadcrumbItem> + <BreadcrumbItem data-ouia-component-id="breadcrumb-group-name"> {groupData?.attributes?.groupId} </BreadcrumbItem> )} diff --git a/api/src/main/webui/src/pages/kafka/nodes/NodesOverviewTab.tsx b/api/src/main/webui/src/pages/kafka/nodes/NodesOverviewTab.tsx index 71b5901b1..efd909e5e 100644 --- a/api/src/main/webui/src/pages/kafka/nodes/NodesOverviewTab.tsx +++ b/api/src/main/webui/src/pages/kafka/nodes/NodesOverviewTab.tsx @@ -310,7 +310,7 @@ export function NodesOverviewTab() { <HelpIcon /> </Tooltip> </DescriptionListTerm> - <DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="overview-total-nodes"> {formatNumber(totalNodes)} </DescriptionListDescription> </DescriptionListGroup> @@ -318,7 +318,7 @@ export function NodesOverviewTab() { <DescriptionListTerm> {t('nodes.distribution.controllerRole')} </DescriptionListTerm> - <DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="overview-controller-role"> {controllersWarning ? ( <Icon status="warning"> <ExclamationTriangleIcon /> @@ -335,7 +335,7 @@ export function NodesOverviewTab() { <DescriptionListTerm> {t('nodes.distribution.brokerRole')} </DescriptionListTerm> - <DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="overview-broker-role"> {brokersWarning ? ( <Icon status="warning"> <ExclamationTriangleIcon /> @@ -355,7 +355,7 @@ export function NodesOverviewTab() { <HelpIcon /> </Tooltip> </DescriptionListTerm> - <DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="overview-lead-controller"> {t('nodes.distribution.leadControllerValue', { leadController: leadControllerId, })} diff --git a/api/src/main/webui/src/pages/kafka/nodes/NodesPage.tsx b/api/src/main/webui/src/pages/kafka/nodes/NodesPage.tsx index 0bee1befa..385e063e2 100644 --- a/api/src/main/webui/src/pages/kafka/nodes/NodesPage.tsx +++ b/api/src/main/webui/src/pages/kafka/nodes/NodesPage.tsx @@ -55,7 +55,7 @@ export function NodesPage() { <Split hasGutter style={{ alignItems: 'center', display: 'flex' }}> <SplitItem style={{ display: 'flex', alignItems: 'center' }}>{t('nodes.title')}</SplitItem> <SplitItem style={{ display: 'flex', alignItems: 'center' }}> - <Label icon={isLoading ? <Spinner size="sm" /> : undefined}> + <Label icon={isLoading ? <Spinner size="sm" /> : undefined} data-ouia-component-id="nodes-total-badge"> {totalNodes} total </Label> </SplitItem> @@ -64,6 +64,7 @@ export function NodesPage() { <Label icon={isLoading ? <Spinner size="sm" /> : <CheckCircleIcon />} color="green" + data-ouia-component-id="nodes-healthy-badge" > {healthyNodes} </Label> @@ -74,6 +75,7 @@ export function NodesPage() { <Label icon={isLoading ? <Spinner size="sm" /> : <ExclamationTriangleIcon />} color="orange" + data-ouia-component-id="nodes-unhealthy-badge" > {unhealthyNodes} </Label> diff --git a/api/src/main/webui/src/pages/kafka/topics/TopicsPage.tsx b/api/src/main/webui/src/pages/kafka/topics/TopicsPage.tsx index 496b3f92b..143904a5a 100644 --- a/api/src/main/webui/src/pages/kafka/topics/TopicsPage.tsx +++ b/api/src/main/webui/src/pages/kafka/topics/TopicsPage.tsx @@ -52,7 +52,7 @@ export function TopicsPage() { <Split hasGutter style={{ alignItems: 'center', display: 'flex' }}> <SplitItem style={{ display: 'flex', alignItems: 'center' }}>{t('topics.title')}</SplitItem> <SplitItem style={{ display: 'flex', alignItems: 'center' }}> - <Label icon={isLoading ? <Spinner size="sm" /> : undefined}> + <Label icon={isLoading ? <Spinner size="sm" /> : undefined} data-ouia-component-id="topics-total-badge"> {totalItems} total </Label> </SplitItem> @@ -61,6 +61,7 @@ export function TopicsPage() { <Label icon={isLoading ? <Spinner size="sm" /> : <CheckCircleIcon />} color="green" + data-ouia-component-id="topics-fully-replicated-badge" > {statusSummary?.FullyReplicated ?? 0} </Label> @@ -71,6 +72,7 @@ export function TopicsPage() { <Label icon={isLoading ? <Spinner size="sm" /> : <ExclamationTriangleIcon />} color="orange" + data-ouia-component-id="topics-under-replicated-badge" > {(statusSummary?.UnderReplicated ?? 0) + (statusSummary?.PartiallyOffline ?? 0) + (statusSummary?.Unknown ?? 0)} </Label> @@ -81,6 +83,7 @@ export function TopicsPage() { <Label icon={isLoading ? <Spinner size="sm" /> : <ExclamationCircleIcon />} color="red" + data-ouia-component-id="topics-offline-badge" > {statusSummary?.Offline ?? 0} </Label> diff --git a/api/src/main/webui/src/pages/kafka/users/detail/UserDetailPage.tsx b/api/src/main/webui/src/pages/kafka/users/detail/UserDetailPage.tsx index 2bc7364a8..869e92dfd 100644 --- a/api/src/main/webui/src/pages/kafka/users/detail/UserDetailPage.tsx +++ b/api/src/main/webui/src/pages/kafka/users/detail/UserDetailPage.tsx @@ -119,15 +119,15 @@ export function UserDetailPage() { <DescriptionList isHorizontal columnModifier={{ default: '2Col' }}> <DescriptionListGroup> <DescriptionListTerm>{t('users.details.name')}</DescriptionListTerm> - <DescriptionListDescription>{name}</DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="user-detail-name">{name}</DescriptionListDescription> </DescriptionListGroup> <DescriptionListGroup> <DescriptionListTerm>{t('users.details.username')}</DescriptionListTerm> - <DescriptionListDescription>{username}</DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="user-detail-username">{username}</DescriptionListDescription> </DescriptionListGroup> <DescriptionListGroup> <DescriptionListTerm>{t('users.details.authentication')}</DescriptionListTerm> - <DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="user-detail-authentication"> {authenticationType} </DescriptionListDescription> </DescriptionListGroup> @@ -139,7 +139,7 @@ export function UserDetailPage() { </DescriptionListGroup> <DescriptionListGroup> <DescriptionListTerm>{t('users.details.creationTime')}</DescriptionListTerm> - <DescriptionListDescription> + <DescriptionListDescription data-ouia-component-id="user-detail-creation-time"> {creationTimestamp ? formatDateTime({ value: creationTimestamp }) : '-'} </DescriptionListDescription> </DescriptionListGroup> diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/ClusterOverviewPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/ClusterOverviewPageSelectors.java deleted file mode 100644 index 865af887c..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/ClusterOverviewPageSelectors.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class ClusterOverviewPageSelectors { - private ClusterOverviewPageSelectors() {} - - public static final String COPS_RECONCILIATION_PAUSED_NOTIFICATION = new CssBuilder(CssSelectors.PAGE_DIV) - .withChild() - .withElementDiv().withComponentPage().withChild() - .withElementDiv().withComponentPage().withSubComponentMainContainer().withChild() - .withElementMain().withComponentPage().withSubComponentMain().withChild() - .withElementDiv().withComponentBanner().withChild() - .withElementDiv().withLayoutBullseye() - .build(); - - public static final String COPS_RECONCILIATION_PAUSED_NOTIFICATION_RESUME_BUTTON = new CssBuilder(COPS_RECONCILIATION_PAUSED_NOTIFICATION) - .withChild() - .withElementDiv().withLayoutFlex().withChild() - .withElementDiv().nth(2).withChild() - .withElementButton().withComponentButton().withChild() - .withElementSpan().withComponentButton().withSubComponentText() - .build(); - - public static final String COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-card > div.pf-v6-c-card__body > div.pf-v6-l-flex:nth-of-type(1) > div:nth-of-type(2) > button.pf-v6-c-button"; - public static final String COPS_CLUSTER_CARD_KAFKA_NAME = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-card > div.pf-v6-c-card__body > div.pf-v6-l-flex:nth-of-type(2) > div.pf-v6-l-flex:nth-of-type(1) > div.pf-v6-l-flex:nth-of-type(1) > div > h2.pf-v6-c-title"; - - public static final String COPS_CLUSTER_CARD_KAFKA_DATA_BROKER_COUNT = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-card > div.pf-v6-c-card__body > div.pf-v6-l-flex:nth-of-type(2) > div.pf-v6-l-flex > div.pf-v6-l-flex:nth-of-type(2) > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > a"; - - public static final String COPS_CLUSTER_CARD_KAFKA_DATA_KAFKA_VERSION = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-card > div.pf-v6-c-card__body > div.pf-v6-l-flex:nth-of-type(2) > div.pf-v6-l-flex:nth-of-type(1) > div.pf-v6-l-flex:nth-of-type(2) > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > div:nth-of-type(1)"; - - public static final String COPS_CLUSTER_CARD_KAFKA_WARNINGS_DROPDOWN_BUTTON = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item > div.pf-v6-l-flex > div > div.pf-v6-c-card > div.pf-v6-c-card__body > div.pf-v6-l-flex > div > div.pf-v6-c-expandable-section > div.pf-v6-c-expandable-section__toggle > button"; - - public static final String COPS_CLUSTER_CARD_KAFKA_WARNING_DROPDOWN_LIST = "body > div:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer:nth-of-type(2) > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-card > div.pf-v6-c-card__body > div.pf-v6-l-flex:nth-of-type(2) > div:nth-of-type(2) > div.pf-v6-c-expandable-section > div.pf-v6-c-expandable-section__content:nth-of-type(2) > ul.pf-v6-c-data-list"; - public static final String COPS_CLUSTER_CARD_KAFKA_WARNING_MESSAGE_ITEMS = COPS_CLUSTER_CARD_KAFKA_WARNING_DROPDOWN_LIST + " > li.pf-v6-c-data-list__item > div.pf-v6-c-data-list__item-row > div.pf-v6-c-data-list__item-content > div.pf-v6-c-data-list__cell:nth-of-type(2) > div:nth-of-type(1) > span.pf-v6-c-truncate > span.pf-v6-c-truncate__start"; - public static final String COPS_RECENT_TOPICS_CARD_TABLE_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-card > div.pf-v6-c-card__expandable-content:nth-of-type(2) > div.pf-v6-c-card__body > ul.pf-v6-c-list > li"; - - public static String getTableRowItemLink(int nth) { - return new CssBuilder(COPS_RECENT_TOPICS_CARD_TABLE_ITEMS).nth(nth).build(); - } - - public static final String COPS_TOPICS_CARD_TOTAL_TOPICS = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-flex > div.pf-v6-l-flex:nth-of-type(1) > div > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-content > small.pf-v6-c-content--small > a"; - - public static final String COPS_TOPICS_CARD_TOTAL_PARTITIONS = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-flex > div.pf-v6-l-flex:nth-of-type(1) > div > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-content > small.pf-v6-c-content--small"; - - public static final String COPS_TOPICS_CARD_FULLY_REPLICATED = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-flex > div.pf-v6-l-flex:nth-of-type(2) > div:nth-of-type(1)"; - - public static final String COPS_TOPICS_CARD_UNDER_REPLICATED = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-flex > div.pf-v6-l-flex:nth-of-type(2) > div:nth-of-type(2)"; - - public static final String COPS_TOPICS_CARD_UNAVAILABLE = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-flex > div.pf-v6-l-flex:nth-of-type(2) > div:nth-of-type(3)"; - - public static final String COPS_DISK_SPACE_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(4) > div > div.pf-v6-c-chart > svg > g:nth-of-type(4) > text > tspan"; - - public static final String COPS_CPU_USAGE_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(7) > div > div.pf-v6-c-chart > svg > g:nth-of-type(4) > text > tspan"; - - public static final String COPS_MEMORY_USAGE_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(10) > div > div.pf-v6-c-chart > svg > g:nth-of-type(4) > text > tspan"; - - public static final String COPS_TOPIC_BYTES_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(3) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(3) > div > div > svg > g:nth-of-type(4) > text > tspan"; -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/CssBuilder.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/CssBuilder.java deleted file mode 100644 index 84e892400..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/CssBuilder.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright Console Authors. - * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ - -package com.github.streamshub.systemtests.locators; - -// To ignore checkstyle that would make this file twice as long because of the breaks after curly brackets -@SuppressWarnings("LeftCurly") -public class CssBuilder { - private final StringBuilder cssClass; - - // Page css selectors - private static final String PATTERNFLY = ".pf-v6"; - private static final String SUFFIX_CONTENT = "-c"; - private static final String SUFFIX_LAYOUT = "-l"; - - public CssBuilder() { - this.cssClass = new StringBuilder(); - } - - public CssBuilder(String existingSelector) { - this.cssClass = new StringBuilder(); - this.cssClass.append(existingSelector).append(" "); - } - - public void checkBase(String suffix) { - String base = PATTERNFLY + suffix; - if (!this.cssClass.toString().endsWith(base)) { - this.cssClass.append(base); - } - } - - // ------------------- - // Combinators - // ------------------- - public CssBuilder withChild() { - this.cssClass.append(" > "); - return this; - } - - public CssBuilder withDesc() { - this.cssClass.append(" "); - return this; - } - - public CssBuilder withId(String id) { - // Append nothing to make the element with class - this.cssClass.append("#").append(id); - return this; - } - - public CssBuilder nth(int nth) { - // Use nth-child instead of nth-of-type to properly count elements with specific classes - // nth-of-type counts by element tag (div, span, etc.) which doesn't work when siblings - // have the same tag but different classes - this.cssClass.append(":nth-child(" + nth + ")"); - return this; - } - - static String joinLocators(String... locators) { - return String.join(" ", locators); - } - - // This shortens the code by a lot - private CssBuilder withElement(String element) { - this.cssClass.append(element); - return this; - } - - private CssBuilder withComponent(String component) { - checkBase(SUFFIX_CONTENT); - this.cssClass.append("-").append(component); - return this; - } - - private CssBuilder withSubComponent(String subComponent) { - this.cssClass.append("__").append(subComponent); - return this; - } - - private CssBuilder withLayout(String layout) { - checkBase(SUFFIX_LAYOUT); - this.cssClass.append("-").append(layout); - return this; - } - - // ------------------- - // Constants - // ------------------- - public static final String HREF = "href"; - // ------------------- - // Elements - // ------------------- - public CssBuilder withElementBody() { return withElement("body"); } - public CssBuilder withElementDiv() { return withElement("div"); } - public CssBuilder withElementDl() { return withElement("dl"); } - public CssBuilder withElementDt() { return withElement("dt"); } - public CssBuilder withElementDd() { return withElement("dd"); } - public CssBuilder withElementFooter() { return withElement("footer"); } - public CssBuilder withElementLabel() { return withElement("label"); } - public CssBuilder withElementThead() { return withElement("thead"); } - public CssBuilder withElementTbody() { return withElement("tbody"); } - public CssBuilder withElementTr() { return withElement("tr"); } - public CssBuilder withElementTd() { return withElement("td"); } - public CssBuilder withElementTh() { return withElement("th"); } - public CssBuilder withElementH1() { return withElement("h1"); } - public CssBuilder withElementH2() { return withElement("h2"); } - public CssBuilder withElementH3() { return withElement("h3"); } - public CssBuilder withElementH4() { return withElement("h4"); } - public CssBuilder withElementSection() { return withElement("section"); } - public CssBuilder withElementMain() { return withElement("main"); } - public CssBuilder withElementNav() { return withElement("nav"); } - public CssBuilder withElementA() { return withElement("a"); } - public CssBuilder withElementOl() { return withElement("ol"); } - public CssBuilder withElementImg() { return withElement("img"); } - public CssBuilder withElementHeader() { return withElement("header"); } - public CssBuilder withElementSmall() { return withElement("small"); } - public CssBuilder withElementSpan() { return withElement("span"); } - public CssBuilder withElementInput() { return withElement("input"); } - public CssBuilder withElementP() { return withElement("p"); } - public CssBuilder withElementB() { return withElement("b"); } - public CssBuilder withElementUl() { return withElement("ul"); } - public CssBuilder withElementLi() { return withElement("li"); } - public CssBuilder withElementTable() { return withElement("table"); } - public CssBuilder withElementButton() { return withElement("button"); } - public CssBuilder withElementForm() { return withElement("form"); } - public CssBuilder withElementTime() { return withElement("time"); } - public CssBuilder withElementSvg() { return withElement("svg"); } - public CssBuilder withElementG() { return withElement("g"); } - public CssBuilder withElementText() { return withElement("text"); } - public CssBuilder withElementPre() { return withElement("pre"); } - public CssBuilder withElementCode() { return withElement("code"); } - - // ------------------- - // Components - // ------------------- - public CssBuilder withComponentPage() { return withComponent("page"); } - public CssBuilder withComponentBanner() { return withComponent("banner"); } - public CssBuilder withComponentLogin() { return withComponent("login"); } - public CssBuilder withComponentTitle() { return withComponent("title"); } - public CssBuilder withComponentLabelGroup() { return withComponent("label-group"); } - public CssBuilder withComponentBreadcrumb() { return withComponent("breadcrumb"); } - public CssBuilder withComponentNav() { return withComponent("nav"); } - public CssBuilder withComponentMasthead() { return withComponent("masthead"); } - public CssBuilder withComponentMastheadBrand() { return withComponent("masthead_brand"); } - public CssBuilder withComponentBrand() { return withComponent("brand"); } - public CssBuilder withComponentToolbar() { return withComponent("toolbar"); } - public CssBuilder withComponentPagination() { return withComponent("pagination"); } - public CssBuilder withComponentSwitch() { return withComponent("switch"); } - public CssBuilder withComponentInputGroup() { return withComponent("input-group"); } - public CssBuilder withComponentTextInputGroup() { return withComponent("text-input-group"); } - public CssBuilder withComponentFormControl() { return withComponent("form-control"); } - public CssBuilder withComponentButton() { return withComponent("button"); } - public CssBuilder withComponentCard() { return withComponent("card"); } - public CssBuilder withComponentBackdrop() { return withComponent("backdrop"); } - public CssBuilder withComponentDescriptionList() { return withComponent("description-list"); } - public CssBuilder withComponentModalBox() { return withComponent("modal-box"); } - public CssBuilder withComponentChart() { return withComponent("chart"); } - public CssBuilder withComponentTable() { return withComponent("table"); } - public CssBuilder withComponentLabel() { return withComponent("label"); } - public CssBuilder withComponentList() { return withComponent("list"); } - public CssBuilder withComponentClipboardCopy() { return withComponent("clipboard-copy"); } - public CssBuilder withComponentContent() { return withComponent("content"); } - public CssBuilder withComponentExpandableSection() { return withComponent("expandable-section"); } - public CssBuilder withComponentDataList() { return withComponent("data-list"); } - public CssBuilder withComponentEmptyState() { return withComponent("empty-state"); } - public CssBuilder withComponentScrollOuterWrapper() { return withComponent("scroll-outer-wrapper"); } - public CssBuilder withComponentScrollInnerWrapper() { return withComponent("scroll-inner-wrapper"); } - public CssBuilder withComponentDrawer() { return withComponent("drawer"); } - public CssBuilder withComponentBadge() { return withComponent("badge"); } - public CssBuilder withComponentChipGroup() { return withComponent("chip-group"); } - public CssBuilder withComponentMenu() { return withComponent("menu"); } - public CssBuilder withComponentCheck() { return withComponent("check"); } - public CssBuilder withComponentMenuToggle() { return withComponent("menu-toggle"); } - public CssBuilder withComponentPanel() { return withComponent("panel"); } - public CssBuilder withComponentForm() { return withComponent("form"); } - public CssBuilder withComponentRadio() { return withComponent("radio"); } - public CssBuilder withComponentDatePicker() { return withComponent("date-picker"); } - public CssBuilder withComponentTabs() { return withComponent("tabs"); } - public CssBuilder withComponentCodeBlock() { return withComponent("code-block"); } - - // ---------------- - // Sub-components - // ---------------- - public CssBuilder withSubComponentText() { return withSubComponent("text"); } - public CssBuilder withSubComponentTextInput() { return withSubComponent("text-input"); } - public CssBuilder withSubComponentContent() { return withSubComponent("content"); } - public CssBuilder withSubComponentBody() { return withSubComponent("body"); } - public CssBuilder withSubComponentFooter() { return withSubComponent("footer"); } - public CssBuilder withSubComponentContainer() { return withSubComponent("container"); } - public CssBuilder withSubComponentHead() { return withSubComponent("head"); } - public CssBuilder withSubComponentActions() { return withSubComponent("actions"); } - public CssBuilder withSubComponentMainGroup() { return withSubComponent("main-group"); } - public CssBuilder withSubComponentMainBreadcrumb() { return withSubComponent("main-breadcrumb"); } - public CssBuilder withSubComponentContentSection() { return withSubComponent("content-section"); } - public CssBuilder withSubComponentPageMenu() { return withSubComponent("page-menu"); } - public CssBuilder withSubComponentMainSection() { return withSubComponent("main-section"); } - public CssBuilder withSubComponentItem() { return withSubComponent("item"); } - public CssBuilder withSubComponentItemRow() { return withSubComponent("item-row"); } - public CssBuilder withSubComponentItemControl() { return withSubComponent("item-control"); } - public CssBuilder withSubComponentOpen() { return withSubComponent("open"); } - public CssBuilder withSubComponentItemMain() { return withSubComponent("item-main"); } - public CssBuilder withSubComponentItemText() { return withSubComponent("item-text"); } - public CssBuilder withSubComponentItemCheck() { return withSubComponent("item-check"); } - public CssBuilder withSubComponentInput() { return withSubComponent("input"); } - public CssBuilder withSubComponentControls() { return withSubComponent("controls"); } - public CssBuilder withSubComponentLink() { return withSubComponent("link"); } - public CssBuilder withSubComponentSubnav() { return withSubComponent("subnav"); } - public CssBuilder withSubComponentSidebar() { return withSubComponent("sidebar"); } - public CssBuilder withSubComponentSidebarBody() { return withSubComponent("sidebar-body"); } - public CssBuilder withSubComponentTbody() { return withSubComponent("tbody"); } - public CssBuilder withSubComponentThead() { return withSubComponent("thead"); } - public CssBuilder withSubComponentGroup() { return withSubComponent("group"); } - public CssBuilder withSubComponentNav() { return withSubComponent("nav"); } - public CssBuilder withSubComponentNavControl() { return withSubComponent("nav-control"); } - public CssBuilder withSubComponentClose() { return withSubComponent("close"); } - public CssBuilder withSubComponentPanelMain() { return withSubComponent("panel-main"); } - public CssBuilder withSubComponentPanel() { return withSubComponent("panel"); } - public CssBuilder withSubComponentToggle() { return withSubComponent("toggle"); } - public CssBuilder withSubComponentToggleText() { return withSubComponent("toggle-text"); } - public CssBuilder withSubComponentToggleIcon() { return withSubComponent("toggle-icon"); } - public CssBuilder withSubComponentMain() { return withSubComponent("main"); } - public CssBuilder withSubComponentMainContainer() { return withSubComponent("main-container"); } - public CssBuilder withSubComponentMainBody() { return withSubComponent("main-body"); } - public CssBuilder withSubComponentBrand() { return withSubComponent("brand"); } - public CssBuilder withSubComponentList() { return withSubComponent("list"); } - public CssBuilder withSubComponentListItem() { return withSubComponent("list-item"); } - public CssBuilder withSubComponentHeader() { return withSubComponent("header"); } - public CssBuilder withSubComponentTitle() { return withSubComponent("title"); } - public CssBuilder withSubComponentTitleText() { return withSubComponent("title-text"); } - public CssBuilder withSubComponentHeaderMain() { return withSubComponent("header-main"); } - public CssBuilder withSubComponentExpandableContent() { return withSubComponent("expandable-content"); } - public CssBuilder withSubComponentTr() { return withSubComponent("tr"); } - public CssBuilder withSubComponentTd() { return withSubComponent("td"); } - public CssBuilder withSubComponentTh() { return withSubComponent("th"); } - public CssBuilder withSubComponentButton() { return withSubComponent("button"); } - public CssBuilder withSubComponentSection() { return withSubComponent("section"); } - public CssBuilder withSubComponentSectionTitle() { return withSubComponent("section-title"); } - public CssBuilder withSubComponentGroupControl() { return withSubComponent("group-control"); } - public CssBuilder withSubComponentCheck() { return withSubComponent("check"); } - public CssBuilder withSubComponentSort() { return withSubComponent("sort"); } - public CssBuilder withSubComponentButtonContent() { return withSubComponent("button-content"); } - public CssBuilder withSubComponentLabel() { return withSubComponent("label"); } - public CssBuilder withSubComponentExpandableRowContent() { return withSubComponent("expandable-row-content"); } - public CssBuilder withSubComponentDescription() { return withSubComponent("description"); } - public CssBuilder withComponentTabContent() { return withComponent("tab-content"); } - public CssBuilder withSubComponentMainSubnav() { return withSubComponent("main-subnav"); } - public CssBuilder withSubComponentPre() { return withSubComponent("pre"); } - public CssBuilder withSubComponentCode() { return withSubComponent("code"); } - - // ------------ - // Layouts - // ------------ - public CssBuilder withLayoutSplit() { return withLayout("split"); } - public CssBuilder withLayoutGrid() { return withLayout("grid"); } - public CssBuilder withLayoutBullseye() { return withLayout("bullseye"); } - public CssBuilder withLayoutStack() { return withLayout("stack"); } - public CssBuilder withLayoutFlex() { return withLayout("flex"); } - - public String build() { - return this.cssClass.toString() - // Remove multiple whitespaces with only one - .replaceAll("\\s+", " ") - // Remove whitespace before ":" to avoid `div :nth-child(1)` - .replace(" :", ":") - // Add space after ")" if it's not there to avoid `div:nth-child(1)div` - .replaceAll("\\)(?!\\s)", ") "); - } -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/CssSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/CssSelectors.java deleted file mode 100644 index 826f7fafe..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/CssSelectors.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright Console Authors. - * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ - -package com.github.streamshub.systemtests.locators; - -/** - * Note: When creating new element from scratch using empty CssSelectors constructor, - * please start with `body` element that is right after `html` element, this ensures code continuity. - */ -public class CssSelectors { - - private CssSelectors() {} - - public static final String PAGE_DIV = new CssBuilder() - .withElementDiv().withId("root") - .build(); - - // Keycloak - public static final String LOGIN_KEYCLOAK_PAGE_TITLE = new CssBuilder() - .withElementHeader().withId("kc-header") - .build(); - - public static final String LOGIN_KEYCLOAK_FORM = new CssBuilder() - .withElementForm().withId("kc-form-login") - .build(); - - public static final String LOGIN_KEYCLOAK_USERNAME_INPUT = new CssBuilder(LOGIN_KEYCLOAK_FORM) - .withDesc() - .withElementInput().withId("username") - .build(); - - public static final String LOGIN_KEYCLOAK_PASSWORD_INPUT = new CssBuilder(LOGIN_KEYCLOAK_FORM) - .withDesc() - .withElementInput().withId("password") - .build(); - - public static final String LOGIN_KEYCLOAK_SIGN_IN_BUTTON = new CssBuilder(LOGIN_KEYCLOAK_FORM) - .withDesc() - .withElementButton().withId("kc-login") - .build(); - - // ---------------------------- - // Page contents - // ---------------------------- - public static final String PAGES_MAIN_CONTENT = new CssBuilder(PAGE_DIV) - .withChild() - .withElementDiv().withComponentPage().withChild() - .withElementDiv().withComponentPage().withSubComponentMainContainer().withChild() - .withElementMain().withComponentPage().withSubComponentMain() - .build(); - - public static final String PAGES_CONTENT = new CssBuilder(PAGES_MAIN_CONTENT) - .withChild() - .withElementDiv().withComponentDrawer() - .build(); - - public static final String PAGES_HEADER = new CssBuilder(PAGES_MAIN_CONTENT) - .withChild() - .withElementDiv().withComponentPage().withSubComponentMainGroup() - .build(); - - public static final String PAGES_CONTENT_HEADER_TITLE_CONTENT = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > h1.pf-v6-c-title"; - - public static final String PAGES_POPUP_MODAL = new CssBuilder() - .withElementBody().withComponentBackdrop().withSubComponentOpen().withChild() - .withElementDiv().withComponentBackdrop().withChild() - .withElementDiv().withLayoutBullseye().withChild() - .withElementDiv().withComponentModalBox() - .build(); - - public static final String PAGES_POPUP_MODAL_HEADER = new CssBuilder(PAGES_POPUP_MODAL) - .withChild() - .withElementHeader().withComponentModalBox().withSubComponentHeader() - .build(); - - public static final String PAGES_POPUP_MODAL_BODY = new CssBuilder(PAGES_POPUP_MODAL) - .withChild() - .withElementDiv().withComponentModalBox().withSubComponentBody() - .build(); - - public static final String PAGES_MODAL_CONFIRM_BUTTON = new CssBuilder(PAGES_POPUP_MODAL) - .withChild() - .withElementFooter().withComponentModalBox().withSubComponentFooter().withChild() - .withElementButton().withComponentButton().nth(1) - .build(); - - public static final String PAGES_MODAL_CANCEL_BUTTON = new CssBuilder(PAGES_POPUP_MODAL) - .withChild() - .withElementFooter().withComponentModalBox().withSubComponentFooter().withChild() - .withElementButton().withComponentButton().nth(2) - .build(); - - public static final String PAGES_AD_TABLE_ROW_ITEMS = new CssBuilder() - .withElementTd().withComponentTable().withSubComponentTd() - .build(); - - public static final String PAGES_AD_TABLE_ROW_ITEMS_LINK = new CssBuilder() - .withElementTr().withComponentTable().withSubComponentTr().withChild() - .withElementTd().withComponentTable().withSubComponentTd().withChild() - .withElementA() - .build(); - - // Logged user - public static final String PAGES_CURRENTLY_LOGGED_USER_BUTTON = new CssBuilder(CssSelectors.PAGE_DIV) - .withChild() - .withElementDiv().withComponentPage().withChild() - .withElementHeader().withComponentMasthead().withChild() - .withElementDiv().withComponentMasthead().withSubComponentContent().withChild() - .withElementDiv().withComponentToolbar().withChild() - .withElementDiv().withComponentToolbar().withSubComponentContent().nth(1).withChild() - .withElementDiv().withComponentToolbar().withSubComponentContentSection().withChild() - .withElementDiv().withComponentToolbar().withSubComponentItem().nth(3).withChild() - .withElementButton().withComponentMenuToggle() - .build(); - - // Select kafka - public static final String PAGES_NAV_KAFKA_SELECT_BUTTON = new CssBuilder(CssSelectors.PAGE_DIV) - .withChild() - .withElementDiv().withComponentPage().withChild() - .withElementHeader().withComponentMasthead().withChild() - .withElementDiv().withComponentMasthead().withSubComponentContent().withChild() - .withElementDiv().withComponentToolbar().withChild() - .withElementDiv().withComponentToolbar().withSubComponentContent().nth(1).withChild() - .withElementDiv().withComponentToolbar().withSubComponentContentSection().withChild() - .withElementDiv().withComponentToolbar().withSubComponentItem().nth(1).withChild() - .withElementButton().withComponentMenuToggle() - .build(); - - // Total available kafka clusters count - public static final String PAGES_TOTAL_AVAILABLE_KAFKA_COUNT = new CssBuilder(PAGES_NAV_KAFKA_SELECT_BUTTON) - .withChild() - .withElementSpan().withComponentMenuToggle().withSubComponentText().withChild() - .withElementDiv().withLayoutFlex().withChild() - .withElementDiv().nth(2).withChild() - .withElementSpan().withComponentBadge() - .build(); - - public static final String PAGES_LOGOUT_BUTTON = new CssBuilder() - .withElementBody().withChild() - .withElementDiv().withComponentMenu().withChild() - .withElementDiv().withComponentMenu().withSubComponentContent().withChild() - .withElementUl().withComponentMenu().withSubComponentList().withChild() - .withElementLi().withComponentMenu().withSubComponentListItem().withChild() - .withElementButton().withComponentMenu().withSubComponentItem() - .build(); - - - // Kafka credentials login page - - public static final String PAGES_KAFKA_CREDENTIALS_NAME_INPUT = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body:nth-of-type(2) > form.pf-v6-c-form > div.pf-v6-c-form__group:nth-of-type(1) > div.pf-v6-c-form__group-control:nth-of-type(2) > span.pf-v6-c-form-control > input#username"; - - public static final String PAGES_KAFKA_CREDENTIALS_PASSWORD_INPUT = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body:nth-of-type(2) > form.pf-v6-c-form > div.pf-v6-c-form__group:nth-of-type(2) > div.pf-v6-c-form__group-control:nth-of-type(2) > span.pf-v6-c-form-control > input#password"; - - public static final String PAGES_KAFKA_CREDENTIALS_LOGIN_BUTTON = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > footer.pf-v6-c-modal-box__footer > button.pf-v6-c-button:nth-of-type(1) > span.pf-v6-c-button__text"; - - public static final String PAGES_NAV_KAFKA_CLUSTERS_LIST_ITEMS = "body > div.pf-v6-c-menu:nth-of-type(2) > div.pf-v6-c-menu__content:nth-of-type(2) > ul.pf-v6-c-menu__list > section.pf-v6-c-menu__group > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item"; -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/GroupsPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/GroupsPageSelectors.java deleted file mode 100644 index af805f752..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/GroupsPageSelectors.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class GroupsPageSelectors { - - private GroupsPageSelectors() {} - - public static final String GPS_HEADER_TITLE = CssSelectors.PAGES_CONTENT_HEADER_TITLE_CONTENT; - - public static final String GPS_TABLE = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > table.pf-v6-c-table"; - - public static final String GPS_TABLE_ITEMS = GPS_TABLE + " > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - - public static final String GPS_GROUP_NAME_INPUT = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-c-toolbar:nth-of-type(1) > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(1) > div.pf-v6-c-text-input-group > div.pf-v6-c-text-input-group__main:nth-of-type(1) > span.pf-v6-c-text-input-group__text > input.pf-v6-c-text-input-group__text-input"; - public static final String GPS_RESULT_FIRST_NAME = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td:nth-of-type(1) > a"; - public static String getTableRowItems(int nth) { - return CssBuilder.joinLocators(new CssBuilder(GPS_TABLE_ITEMS).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getTableRowItem(int nthRow, int nthColumn) { - return new CssBuilder(getTableRowItems(nthRow)).nth(nthColumn).build(); - } - public static final String GPS_NO_GROUPS_AVAILABLE = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-c-empty-state:nth-of-type(2) > div.pf-v6-c-empty-state__content > h2.pf-v6-c-title"; - -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/IdConstants.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/IdConstants.java new file mode 100644 index 000000000..38afc9d26 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/IdConstants.java @@ -0,0 +1,27 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators; + +/** CSS {@code #id} selectors used directly by locator classes in this package. */ +public final class IdConstants { + private IdConstants() {} + + public static final String MASTHEAD_TOOLBAR = "#masthead-toolbar"; + public static final String USERNAME_FILTER = "#username-filter"; + public static final String FILTER_TYPE_SELECT = "#filter-type-select"; + public static final String STATUS_FILTER_SELECT = "#status-filter-select"; + public static final String TOPIC_SELECT = "#topic-select"; + public static final String OFFSET_SELECT = "#offset-select"; + public static final String DATETIME_INPUT = "#datetime-input"; + public static final String CLUSTER_WARNINGS_TOGGLE = "#cluster-warnings-toggle"; + + /** PatternFly's own default {@code widgetId} ("options-menu") for the Pagination component, unchanged by this app. */ + public static final String PAGINATION_TOP_TOGGLE = "#options-menu-top-toggle"; + public static final String PAGINATION_BOTTOM_TOGGLE = "#options-menu-bottom-toggle"; + + /** TopicsFilterToolbar.tsx sets {@code id={`${selectedFilterType}-filter-input`}} - an attribute-suffix match, not a full #id selector. */ + public static final String FILTER_INPUT_ID_SUFFIX = "-filter-input"; +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaConnectPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaConnectPageSelectors.java deleted file mode 100644 index 3a4ad238a..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaConnectPageSelectors.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class KafkaConnectPageSelectors { - - private KafkaConnectPageSelectors() {} - - public static final String KCPS_TAB_BUTTON = new CssBuilder(CssSelectors.PAGES_HEADER) - .withChild() - .withElementSection().withComponentPage().withSubComponentMainSubnav().withChild() - .withElementDiv().withComponentPage().withSubComponentMainBody().withChild() - .withElementDiv().withComponentTabs().withChild() - .withElementUl().withComponentTabs().withSubComponentList().withChild() - .withElementLi().withComponentTabs().withSubComponentItem() - .build(); - - public static final String KCPS_TAB_CONNECTORS_BUTTON = new CssBuilder(CssSelectors.PAGES_HEADER) - .nth(1).withChild() - .withElementButton().withComponentTabs().withSubComponentLink() - .build(); - - public static final String KCPS_TAB_CONNECT_CLUSTERS_BUTTON = new CssBuilder(CssSelectors.PAGES_HEADER) - .nth(2).withChild() - .withElementButton().withComponentTabs().withSubComponentLink() - .build(); - - public static final String KCPS_TABLE = new CssBuilder(CssSelectors.PAGES_CONTENT) - .withChild() - .withElementDiv().withComponentPage().withSubComponentMainBody().withChild() - .withElementDiv().withComponentScrollOuterWrapper().withChild() - .withElementDiv().withComponentScrollInnerWrapper().withChild() - .withElementTable().withComponentTable() - .build(); - - public static final String KCPS_TABLE_ITEMS = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - - public static String getTableRowItems(int nth) { - return CssBuilder.joinLocators(new CssBuilder(KCPS_TABLE_ITEMS).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getTableRowItem(int nthRow, int nthColumn) { - return new CssBuilder(getTableRowItems(nthRow)).nth(nthColumn).build(); - } - - public static final String KCPS_PAGE_TITLE_NAME = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title"; - public static final String KCPS_NAME_FILTER_INPUT = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-c-toolbar:nth-of-type(1) > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(1) > div.pf-v6-c-text-input-group > div.pf-v6-c-text-input-group__main:nth-of-type(1) > span.pf-v6-c-text-input-group__text > input.pf-v6-c-text-input-group__text-input"; - -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaDashboardPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaDashboardPageSelectors.java deleted file mode 100644 index cc08afde3..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaDashboardPageSelectors.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class KafkaDashboardPageSelectors { - private KafkaDashboardPageSelectors() {} - - public static final String KDPS_KAFKA_CLUSTER_LIST_ITEMS = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(2) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - - public static final String AD_KDPS_VIEW_BUTTON = "td.pf-v6-c-table__td:nth-of-type(6) > button.pf-v6-c-button"; - - public static String getViewButton(int row) { - return CssBuilder.joinLocators(new CssBuilder(KDPS_KAFKA_CLUSTER_LIST_ITEMS).nth(row).build(), AD_KDPS_VIEW_BUTTON); - } - - public static final String KDPS_CURRENTLY_LOGGED_USER_BUTTON = "body > div#root > div.pf-v6-c-page > header.pf-v6-c-masthead > div.pf-v6-c-masthead__content > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item > button.pf-v6-c-menu-toggle > span.pf-v6-c-menu-toggle__text:nth-of-type(2)"; -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaUsersPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaUsersPageSelectors.java deleted file mode 100644 index fb70efe28..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/KafkaUsersPageSelectors.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class KafkaUsersPageSelectors { - public static final String KUPS_USER_FILTER_INPUT = new CssBuilder(CssSelectors.PAGES_CONTENT) - .withChild() - .withElementDiv().withComponentPage().withSubComponentMainBody().withChild() - .withElementDiv().withComponentScrollOuterWrapper().withChild() - .withElementDiv().withComponentToolbar().withChild() - .withElementDiv().withComponentToolbar().withSubComponentContent().nth(1).withChild() - .withElementDiv().withComponentToolbar().withSubComponentContentSection().withChild() - .withElementDiv().withComponentToolbar().withSubComponentItem().withChild() - .withElementDiv().withComponentInputGroup().withChild() - .withElementDiv().withComponentInputGroup().withChild() - .withElementSpan().withComponentFormControl().withChild() - .withElementInput() - .build(); - - public static final String KUPS_USER_RESULTS_TABLE = new CssBuilder(CssSelectors.PAGES_CONTENT) - .withChild() - .withElementDiv().withComponentPage().withSubComponentMainBody().withChild() - .withElementDiv().withComponentScrollOuterWrapper().withChild() - .withElementDiv().withComponentScrollInnerWrapper().withChild() - .withElementTable().withComponentTable() - .build(); - - - public static final String KUPS_USER_RESULTS_TABLE_ITEMS = new CssBuilder(KUPS_USER_RESULTS_TABLE) - .withChild() - .withElementTbody().withComponentTable().withSubComponentTbody() - .build(); - - - public static String getTableRowItems(int nth) { - return CssBuilder.joinLocators(new CssBuilder(KUPS_USER_RESULTS_TABLE_ITEMS).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getTableRowItem(int nthRow, int nthColumn) { - return new CssBuilder(getTableRowItems(nthRow)).nth(nthColumn).build(); - } - - // Single user page - public static final String KUPS_KAFKA_USER_NAME_HEADER = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title"; - - - // Authorization table - public static final String KUPS_AUTHORIZATION_TABLE_ROWS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-flex > div:nth-of-type(2) > section.pf-v6-c-tab-content > div.pf-v6-c-tab-content__body > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - - public static String getAuthTableRowItems(int nth) { - return CssBuilder.joinLocators(new CssBuilder(KUPS_AUTHORIZATION_TABLE_ROWS).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getAuthTableRowStartingWith(int nth, String type) { - return CssBuilder.joinLocators(new CssBuilder(KUPS_AUTHORIZATION_TABLE_ROWS).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getAuthTableRowItem(int nthRow, int nthColumn) { - return new CssBuilder(getAuthTableRowItems(nthRow)).nth(nthColumn).build(); - } - - public static final String KUPS_DESCRIPTION_NAME = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-flex > div:nth-of-type(1) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(1) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String KUPS_DESCRIPTION_AUTH = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-flex > div:nth-of-type(1) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(3) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String KUPS_DESCRIPTION_USERNAME = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-flex > div:nth-of-type(1) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(2) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String KUPS_DESCRIPTION_CREATION_TIMESTAMP = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-flex > div:nth-of-type(1) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(5) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/Locators.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/Locators.java new file mode 100644 index 000000000..af2b38220 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/Locators.java @@ -0,0 +1,43 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators; + +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +import java.util.regex.Pattern; + +/** Shared helpers for common locator patterns, so page/component classes don't repeat raw Playwright API boilerplate. */ +public final class Locators { + private Locators() {} + + /** Finds an element by its {@code data-ouia-component-id} attribute, the app's stable-identity convention. */ + public static Locator byOuiaId(Page page, String ouiaId) { + return page.locator("[data-ouia-component-id='" + ouiaId + "']"); + } + + public static Locator buttonNamed(Page page, String name) { + return page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName(name)); + } + + public static Locator buttonNamed(Page page, String name, boolean exact) { + return page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName(name).setExact(exact)); + } + + public static Locator buttonNamed(Page page, Pattern namePattern) { + return page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName(namePattern)); + } + + public static Locator buttonNamed(Locator scope, String name) { + return scope.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName(name)); + } + + /** Items of whichever PatternFly menu/dropdown is currently open, portaled to the page root - not scoped to any one page. */ + public static Locator menuItems(Page page) { + return page.getByRole(AriaRole.MENUITEM); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/MessagesPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/MessagesPageSelectors.java deleted file mode 100644 index f6ae30bd5..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/MessagesPageSelectors.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class MessagesPageSelectors { - private MessagesPageSelectors() {} - - public static final String MPS_EMPTY_BODY_CONTENT = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div.pf-v6-c-empty-state:nth-of-type(2) > div.pf-v6-c-empty-state__content > h2.pf-v6-c-title"; - - public static final String MPS_SEARCH_TOOLBAR_QUERY_INPUT = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div.pf-v6-c-toolbar:nth-of-type(1) > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(1) > div > div.pf-v6-c-input-group > div.pf-v6-c-input-group__item:nth-of-type(1) > div.pf-v6-c-text-input-group > div.pf-v6-c-text-input-group__main:nth-of-type(1) > span.pf-v6-c-text-input-group__text > input.pf-v6-c-text-input-group__text-input"; - - public static final String MPS_SEARCH_TOOLBAR_OPEN_POPOVER_FORM_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div.pf-v6-c-toolbar:nth-of-type(1) > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(1) > div > div.pf-v6-c-input-group > div.pf-v6-c-input-group__item:nth-of-type(2) > button.pf-v6-c-button"; - - public static final String MPS_TPF_HAS_WORDS_INPUT = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(1) > div.pf-v6-l-grid:nth-of-type(2) > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control:nth-of-type(2) > span.pf-v6-c-form-control > input"; - public static final String MPS_TPF_WHERE_DROPDOWN_BUTTON = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(1) > div.pf-v6-l-grid:nth-of-type(2) > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - public static final String MPS_TPF_WHERE_DROPDOWN_ITEMS = "body > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item"; - - public static final String MPS_TPF_PARAMETERS_MESSAGES_DROPDOWN_BUTTON = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-l-grid:nth-of-type(2) > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control:nth-of-type(2) > div.pf-v6-l-flex > div > button.pf-v6-c-menu-toggle"; - public static final String MPS_TPF_PARAMETERS_MESSAGES_DROPDOWN_ITEMS = "body > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(2) > button.pf-v6-c-menu__item"; - - public static final String MPS_TPF_PARAMETERS_MESSAGES_OFFSET_INPUT = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-l-grid:nth-of-type(2) > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(2) > span.pf-v6-c-form-control > input"; - - public static final String MPS_TPF_PARAMETERS_MESSAGES_UNIX_TIMESTAMP_INPUT = MPS_TPF_PARAMETERS_MESSAGES_OFFSET_INPUT; - public static final String MPS_TPF_PARAMETERS_MESSAGES_TIMESTAMP_INPUT = "input[aria-label='Specify timestamp']"; - - public static final String MPS_TPF_RETRIEVE_DROPDOWN_BUTTON = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-l-grid:nth-of-type(2) > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control:nth-of-type(2) > div.pf-v6-l-flex > div > button.pf-v6-c-menu-toggle"; - public static final String MPS_TPF_RETRIEVE_LIMIT_DROPDOWN_BUTTON = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-l-grid:nth-of-type(2) > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - public static final String MPS_TPF_PARTITION_DROPDOWN_BUTTON = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-l-grid:nth-of-type(2) > div.pf-v6-l-grid__item:nth-of-type(3) > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - - // Generic locator for whichever dropdown's menu is currently open (PatternFly portals exactly one at a time - // to this same position under body) - filter by exact item text rather than position, since the Messages - // dropdown has a non-selectable divider taking up a sibling slot, and numeric labels (5/50, 1/10) would - // collide under substring matching. - public static final String MPS_TPF_OPEN_MENU_ITEM_BUTTONS = "body > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item > button.pf-v6-c-menu__item"; - - public static final String MPS_TPF_SEARCH_BUTTON = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control > div.pf-v6-c-form__actions > button.pf-v6-c-button:nth-of-type(1)"; - public static final String MPS_TPF_RESET_BUTTON = "body > div.pf-v6-c-panel:nth-of-type(2) > div.pf-v6-c-panel__main > div.pf-v6-c-panel__main-body > form.pf-v6-c-form > div.pf-v6-c-form__group > div.pf-v6-c-form__group-control > div.pf-v6-c-form__actions > button.pf-v6-c-button:nth-of-type(2)"; - public static final String MPS_SEARCH_TOOLBAR_QUERY_ENTER_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div.pf-v6-c-toolbar:nth-of-type(1) > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(1) > div > div.pf-v6-c-input-group > div.pf-v6-c-input-group__item:nth-of-type(3) > button.pf-v6-c-button"; - public static final String MPS_SEARCH_RESULTS_TABLE_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div:nth-of-type(2) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - public static final String MPS_SEARCH_RESULTS_TABLE_NO_DATA = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div.pf-v6-c-empty-state:nth-of-type(2) > div.pf-v6-c-empty-state__content > h2.pf-v6-c-title"; - - - public static String getTableRowItems(int nth) { - return CssBuilder.joinLocators(new CssBuilder(MPS_SEARCH_RESULTS_TABLE_ITEMS).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getTableRowItem(int nthRow, int nthColumn) { - return new CssBuilder(getTableRowItems(nthRow)).nth(nthColumn).build(); - } - - public static final String MPS_MESSAGE_SIDEBAR_VALUE_FORMAT = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__panel:nth-of-type(2) > div.pf-v6-c-drawer__panel-main:nth-of-type(2) > div.pf-v6-c-drawer__body:nth-of-type(2) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(8) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - public static final String MPS_MESSAGE_SIDEBAR_SCHEMA_NAME = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__panel:nth-of-type(2) > div.pf-v6-c-drawer__panel-main:nth-of-type(2) > div.pf-v6-c-drawer__body:nth-of-type(2) > div > section.pf-v6-c-tab-content:nth-of-type(1) > div > div:nth-of-type(2) > p"; - public static final String MPS_MESSAGE_SIDEBAR_SCHEMA_CODE = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__panel:nth-of-type(2) > div.pf-v6-c-drawer__panel-main:nth-of-type(2) > div.pf-v6-c-drawer__body:nth-of-type(2) > div > section.pf-v6-c-tab-content:nth-of-type(1) > div > div.pf-v6-c-code-block:nth-of-type(1) > div.pf-v6-c-code-block__content > pre.pf-v6-c-code-block__pre > code.pf-v6-c-code-block__code"; - - -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/NodesPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/NodesPageSelectors.java deleted file mode 100644 index ce50be65a..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/NodesPageSelectors.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class NodesPageSelectors { - - private NodesPageSelectors() {} - - // ---------------------------- - // Nodes page - // ---------------------------- - - public static final String NPS_HEADER_TITLE_BADGE_TOTAL_COUNT = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > h1.pf-v6-c-title > div.pf-v6-l-split > div.pf-v6-l-split__item > span.pf-v6-c-label > span.pf-v6-c-label__content > span.pf-v6-c-label__text"; - public static final String NPS_HEADER_TITLE_BADGE_WORKING_NODES_COUNT = "body > div#root:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title > div.pf-v6-l-split > div.pf-v6-l-split__item:nth-of-type(3) > div > span.pf-v6-c-label > span.pf-v6-c-label__content"; - public static final String NPS_HEADER_TITLE_BADGE_WARNING_NODES_COUNT = "body > div#root:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title > div.pf-v6-l-split > div.pf-v6-l-split__item:nth-of-type(4) > div > span.pf-v6-c-label > span.pf-v6-c-label__content > span.pf-v6-c-label__text:nth-of-type(2)"; - - public static final String NPS_OVERVIEW_NODE_ITEMS = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-c-card > div.pf-v6-c-card__body > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group"; - - public static final String NPS_TABLE_BODY = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - - public static String getNodeTableRowItems(int nth) { - return CssBuilder.joinLocators(new CssBuilder(NPS_TABLE_BODY).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getNodeTableRowItem(int nthRow, int nthColumn) { - return new CssBuilder(getNodeTableRowItems(nthRow)).nth(nthColumn).build(); - } - - // Rebalance - public static final String NPS_REBALANCE_TABLE_ITEMS = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - - - public static final String NPS_REBALANCE_PROPOSAL_DROPDOWN_BUTTON = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td:nth-of-type(1) > button.pf-v6-c-button"; - - public static final String NPS_REBALANCE_PROPOSAL_STATUS = "body > div#root:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td:nth-of-type(3) > div > span"; - - public static final String NPS_REBALANCE_PROPOSAL_NAME = "body > div:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td:nth-of-type(2) > button"; - - public static final String NPS_REBALANCE_PROPOSAL_DROPDOWN_MODE = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr:nth-of-type(2) > td.pf-v6-c-table__td > div.pf-v6-c-table__expandable-row-content > dl.pf-v6-c-description-list > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-description-list__group > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String NPS_REBALANCE_PROPOSAL_DROPDOWN_AUTO_APPROVAL_ENABLED = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr:nth-of-type(2) > td.pf-v6-c-table__td > div.pf-v6-c-table__expandable-row-content > dl.pf-v6-c-description-list > div.pf-v6-l-flex > div:nth-of-type(1) > div.pf-v6-c-description-list__group > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String NPS_PROPOSAL_ACTION_DROPDOWN_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td:nth-of-type(5) > button.pf-v6-c-menu-toggle"; - - public static final String NPS_PROPOSAL_ACTION_APPROVE_BUTTON = "body > div.pf-v6-c-menu:nth-of-type(2) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(1) > button.pf-v6-c-menu__item"; - - public static final String NPS_PROPOSAL_MODAL_DATA_TO_MOVE = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div:nth-of-type(2) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(1) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String NPS_PROPOSAL_MODAL_DATA_MONITORED_PARTITIONS_PERCENTAGE = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div:nth-of-type(2) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(6) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - public static final String NPS_PROPOSAL_MODAL_DATA_NUMBER_REPLICA_MOVEMENTS = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div:nth-of-type(2) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(9) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String NPS_PROPOSAL_MODAL_DATA_BALANCEDNESS_AFTER = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div:nth-of-type(2) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(10) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String NPS_PROPOSAL_MODAL_DATA_BALANCEDNESS_BEFORE = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div:nth-of-type(2) > dl.pf-v6-c-description-list > div.pf-v6-c-description-list__group:nth-of-type(11) > dd.pf-v6-c-description-list__description > div.pf-v6-c-description-list__text"; - - public static final String NPS_PROPOSAL_MODAL_CLOSE_BUTTON = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div:nth-of-type(3) > button.pf-v6-c-button > span.pf-v6-c-button__text"; - - public static final String NPS_PROPOSAL_MODAL_CONFIRM_BUTTON = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > footer.pf-v6-c-modal-box__footer > button.pf-v6-c-button:nth-of-type(1)"; - - public static final String NPS_FILTER_TYPE_ROLE_DROPDOWN_BUTTON = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-c-toolbar:nth-of-type(1) > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__group:nth-of-type(1) > div.pf-v6-c-toolbar__item:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - - public static final String NPS_FILTER_BY_NODEPOOL_ITEMS = "body > div.pf-v6-c-menu:nth-of-type(2) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item"; - - public static final String NPS_FILTER_CLEAR_ALL_FILTERS_BUTTON = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(3) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-c-toolbar:nth-of-type(1) > div.pf-v6-c-toolbar__content:nth-of-type(2) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__group > div.pf-v6-c-toolbar__item:nth-of-type(2) > button.pf-v6-c-button > span.pf-v6-c-button__text"; - - public static final String PAGES_NOT_AUTHORIZED_CONTENT = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > div.pf-v6-c-empty-state > div.pf-v6-c-empty-state__content > div.pf-v6-c-empty-state__body:nth-of-type(2)"; -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/OuiaIdConstants.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/OuiaIdConstants.java new file mode 100644 index 000000000..15bdda211 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/OuiaIdConstants.java @@ -0,0 +1,73 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators; + +/** {@code data-ouia-component-id} values set by webui components, referenced by the locator classes in this package. */ +public final class OuiaIdConstants { + private OuiaIdConstants() {} + + // Masthead + public static final String TOTAL_AVAILABLE_KAFKA_COUNT = "total-available-kafka-count"; + + // Kafka Users page + public static final String KAFKA_USERS_LISTING = "kafka-users-listing"; + public static final String USER_DETAIL_NAME = "user-detail-name"; + public static final String USER_DETAIL_USERNAME = "user-detail-username"; + public static final String USER_DETAIL_AUTHENTICATION = "user-detail-authentication"; + public static final String USER_DETAIL_CREATION_TIME = "user-detail-creation-time"; + + // Nodes page + public static final String NODES_LISTING = "nodes-listing"; + public static final String NODES_TOTAL_BADGE = "nodes-total-badge"; + public static final String NODES_HEALTHY_BADGE = "nodes-healthy-badge"; + public static final String NODES_UNHEALTHY_BADGE = "nodes-unhealthy-badge"; + public static final String OVERVIEW_TOTAL_NODES = "overview-total-nodes"; + public static final String OVERVIEW_CONTROLLER_ROLE = "overview-controller-role"; + public static final String OVERVIEW_BROKER_ROLE = "overview-broker-role"; + public static final String OVERVIEW_LEAD_CONTROLLER = "overview-lead-controller"; + public static final String REBALANCE_AUTO_APPROVAL_VALUE = "rebalance-auto-approval-value"; + public static final String REBALANCE_MODE_VALUE = "rebalance-mode-value"; + public static final String REBALANCE_DATA_TO_MOVE_MB = "rebalance-data-to-move-mb"; + public static final String REBALANCE_MONITORED_PARTITIONS_PERCENTAGE = "rebalance-monitored-partitions-percentage"; + public static final String REBALANCE_NUM_REPLICA_MOVEMENTS = "rebalance-num-replica-movements"; + public static final String REBALANCE_BALANCEDNESS_BEFORE = "rebalance-balancedness-before"; + public static final String REBALANCE_BALANCEDNESS_AFTER = "rebalance-balancedness-after"; + + // Groups page + public static final String GROUPS_LISTING = "groups-listing"; + + // Messages page + public static final String HAS_WORDS_INPUT = "has-words-input"; + public static final String WHERE_DROPDOWN_BUTTON = "where-dropdown-button"; + public static final String MESSAGES_FROM_DROPDOWN_BUTTON = "messages-from-dropdown-button"; + public static final String RETRIEVE_TYPE_DROPDOWN_BUTTON = "retrieve-type-dropdown-button"; + public static final String RETRIEVE_LIMIT_DROPDOWN_BUTTON = "retrieve-limit-dropdown-button"; + public static final String PARTITION_DROPDOWN_BUTTON = "partition-dropdown-button"; + + // Kafka Dashboard page + public static final String KAFKA_CLUSTERS_TABLE = "kafka-clusters-table"; + + // Single Group page + public static final String BREADCRUMB_GROUP_NAME = "breadcrumb-group-name"; + public static final String DRY_RUN_COMMAND_TEXT = "dry-run-command-text"; + + // Topics page + public static final String TOPICS_TABLE = "topics-table"; + public static final String TOPICS_TOTAL_BADGE = "topics-total-badge"; + public static final String TOPICS_FULLY_REPLICATED_BADGE = "topics-fully-replicated-badge"; + public static final String TOPICS_UNDER_REPLICATED_BADGE = "topics-under-replicated-badge"; + public static final String TOPICS_OFFLINE_BADGE = "topics-offline-badge"; + public static final String TOPIC_GROUPS_LISTING = "topic-groups-listing"; + + // Cluster Overview page + public static final String RECONCILIATION_PAUSED_BANNER = "reconciliation-paused-banner"; + public static final String CLUSTER_KAFKA_VERSION = "cluster-kafka-version"; + public static final String CLUSTER_WARNINGS_LIST = "cluster-warnings-list"; + public static final String RECENT_TOPICS_LIST = "recent-topics-list"; + public static final String FULLY_REPLICATED_STATUS_LINK = "fully-replicated-status-link"; + public static final String UNDER_REPLICATED_STATUS_LINK = "under-replicated-status-link"; + public static final String OFFLINE_STATUS_LINK = "offline-status-link"; +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/SingleGroupPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/SingleGroupPageSelectors.java deleted file mode 100644 index 111d63c71..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/SingleGroupPageSelectors.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class SingleGroupPageSelectors { - private SingleGroupPageSelectors() {} - - public static final String SGPS_PAGE_HEADER_NAME = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-flex > div:nth-of-type(1) > h1.pf-v6-c-title"; - - public static final String SGPS_HEADER_BREADCRUMB_GROUP_NAME = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-breadcrumb:nth-of-type(1) > div.pf-v6-c-page__main-body > div.pf-v6-c-page__main-body > nav.pf-v6-c-breadcrumb > ol.pf-v6-c-breadcrumb__list > li.pf-v6-c-breadcrumb__item:nth-of-type(4)"; - - public static final String SGPS_RESET_CONSUMER_OFFSET_BUTTON = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-flex > div:nth-of-type(2) > button.pf-v6-c-button"; - - public static final String SGPS_RESET_PAGE_TOPIC_NAME_DROPDOWN_RESULT = "body.pf-v6-c-backdrop__open > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content:nth-of-type(2) > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(2) > button.pf-v6-c-menu__item"; - - public static final String SGPS_RESET_PAGE_OFFSET_DROPDOWN_BUTTON = "body.pf-v6-c-backdrop__open > div:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body:nth-of-type(2) > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-c-form__group:nth-of-type(2) > div.pf-v6-c-form__group-control:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - - - public static final String SGPS_RESET_PAGE_OFFSET_EARLIEST_OFFSET = "body.pf-v6-c-backdrop__open > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(1) > button.pf-v6-c-menu__item"; - - public static final String SGPS_RESET_PAGE_OFFSET_LATEST_OFFSET = "body.pf-v6-c-backdrop__open > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(2) > button.pf-v6-c-menu__item"; - - public static final String SGPS_RESET_PAGE_OFFSET_ALL_PARTITIONS_SPECIFIC_DATETIME_OFFSET_ISO = "body.pf-v6-c-backdrop__open > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(3) > button.pf-v6-c-menu__item"; - - public static final String SGPS_RESET_PAGE_OFFSET_ALL_PARTITIONS_SPECIFIC_DATETIME_OFFSET_UNIX = "body.pf-v6-c-backdrop__open > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(4) > button.pf-v6-c-menu__item"; - - public static final String SGPS_RESET_PAGE_OFFSET_ALL_PARTITIONS_DELETE_COMMITED_OFFSETS = "body.pf-v6-c-backdrop__open > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li.pf-v6-c-menu__list-item:nth-of-type(5) > button.pf-v6-c-menu__item"; - - public static final String SGPS_RESET_PAGE_OFFSET_SPECIFIC_DATETIME_INPUT_UNIX = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body:nth-of-type(2) > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-c-form__group:nth-of-type(3) > div.pf-v6-c-form__group-control:nth-of-type(2) > div:nth-of-type(1) > div > span.pf-v6-c-form-control > input"; - - public static final String SGPS_RESET_PAGE_OFFSET_SPECIFIC_DATETIME_INPUT_ISO = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body:nth-of-type(2) > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(2) > div.pf-v6-c-form__group:nth-of-type(3) > div.pf-v6-c-form__group-control:nth-of-type(2) > div:nth-of-type(1) > div:nth-of-type(2) > span.pf-v6-c-form-control > input"; - - public static final String SGPS_RESET_PAGE_OFFSET_RESET_BUTTON = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > footer.pf-v6-c-modal-box__footer > button.pf-v6-c-button:nth-of-type(1)"; - - public static final String SGPS_RESET_PAGE_OFFSET_DRY_RUN_BUTTON = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > footer.pf-v6-c-modal-box__footer > button.pf-v6-c-button:nth-of-type(2)"; - - public static final String SGPS_DRY_RUN_COMMAND = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body > div.pf-v6-c-expandable-section > div.pf-v6-c-expandable-section__content > div.pf-v6-c-code-block > div.pf-v6-c-code-block__content > pre.pf-v6-c-code-block__pre > code.pf-v6-c-code-block__code"; - public static final String SGPS_DRY_RUN_COMMAND_DROPDOWN = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body > div.pf-v6-c-expandable-section > div.pf-v6-c-expandable-section__toggle > button.pf-v6-c-button"; - public static final String SGPS_CANCEL_DRY_RUN_OFFSET_BUTTON = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(3) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > footer.pf-v6-c-modal-box__footer > button.pf-v6-c-button"; - - public static final String SGPS_SELECTED_TOPIC_DROPDOWN_BUTTON = "body.pf-v6-c-backdrop__open > div.pf-v6-c-backdrop:nth-of-type(2) > div.pf-v6-l-bullseye > div.pf-v6-c-modal-box > div.pf-v6-c-modal-box__body:nth-of-type(2) > form.pf-v6-c-form > section.pf-v6-c-form__section:nth-of-type(1) > div.pf-v6-c-form__group:nth-of-type(2) > div.pf-v6-c-form__group-control:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - - public static final String SGPS_SELECTED_TOPIC_DROPDOWN_SEARCH_INPUT = "body.pf-v6-c-backdrop__open > div.pf-v6-c-menu:nth-of-type(3) > div.pf-v6-c-menu__search:nth-of-type(1) > div.pf-v6-c-menu__search-input > div.pf-v6-c-text-input-group > div.pf-v6-c-text-input-group__main:nth-of-type(1) > span.pf-v6-c-text-input-group__text > input.pf-v6-c-text-input-group__text-input"; - -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/TextConstants.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/TextConstants.java new file mode 100644 index 000000000..878e5c7e9 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/TextConstants.java @@ -0,0 +1,79 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators; + +/** Accessible names, visible text, and placeholder strings used by locator classes in this package to find elements by role/label/text. */ +public final class TextConstants { + private TextConstants() {} + + // Shared across multiple pages + public static final String SEARCH = "Search"; + public static final String SEARCH_INPUT = "Search input"; + public static final String CANCEL = "Cancel"; + public static final String CONFIRM = "Confirm"; + public static final String CLEAR_ALL_FILTERS = "Clear all filters"; + public static final String RESET_OFFSETS = "Reset offsets"; + public static final String RESUME = "Resume"; + public static final String NOT_AUTHORIZED = "Not Authorized"; + public static final String NAME = "Name"; + public static final String STORAGE = "Storage"; + public static final String GO_TO_PREVIOUS_PAGE = "Go to previous page"; + public static final String GO_TO_NEXT_PAGE = "Go to next page"; + public static final String DETAILS = "Details"; + public static final String ACTIONS = "Actions"; + public static final String VIEW = "View"; + + // Masthead + public static final String KAFKA_CLUSTERS = "Kafka Clusters"; + public static final String LOGOUT = "Logout"; + + // Groups page + public static final String GROUPS = "Groups"; + public static final String FILTER_BY_NAME = "Filter by name"; + public static final String NO_GROUPS_AVAILABLE = "No groups available"; + + // Nodes page + public static final String FORBIDDEN_STATUS_CODE = "403"; + public static final String NODE_POOL = "Node Pool"; + public static final String ROLES = "Roles"; + public static final String STATUS = "Status"; + public static final String REBALANCES_TABLE = "Rebalances"; + + // Kafka Users page + public static final String AUTHORIZATION = "Authorization"; + + // Topics page + public static final String NO_RESULTS_FOUND = "No results found"; + + // Kafka Connect page + public static final String CONNECTORS = "Connectors"; + public static final String CLUSTERS = "Clusters"; + public static final String KAFKA_CONNECT = "Kafka Connect"; + public static final String CONNECTORS_TABLE = "Connectors table"; + public static final String CONNECT_CLUSTERS_TABLE = "Connect clusters table"; + + // Single Group / reset-offset page + public static final String EARLIEST_OFFSET = "Earliest offset"; + public static final String LATEST_OFFSET = "Latest offset"; + public static final String SPECIFIC_DATETIME_ISO = "Specific date/time (ISO 8601)"; + public static final String SPECIFIC_DATETIME_UNIX = "Specific date/time (Unix epoch timestamp)"; + public static final String DELETE_COMMITTED_OFFSETS = "Delete committed offsets"; + public static final String DRY_RUN = "Dry run"; + public static final String CLI_COMMAND = "CLI command"; + + // Messages page + public static final String MESSAGES_TABLE = "Messages table"; + public static final String NO_MESSAGES_DATA = "No messages data"; + public static final String OPEN_ADVANCED_SEARCH = "Open advanced search"; + public static final String SPECIFY_OFFSET = "Specify offset"; + public static final String SPECIFY_TIMESTAMP = "Specify timestamp"; + public static final String SPECIFY_UNIX_TIMESTAMP = "Specify Unix timestamp"; + public static final String RESET = "Reset"; + public static final String VALUE_FORMAT = "Value format"; + + // Cluster Overview page + public static final String PAUSE_OR_RESUME_RECONCILIATION_PATTERN = "Pause Reconciliation|Resume Reconciliation"; +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/TopicsPageSelectors.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/TopicsPageSelectors.java deleted file mode 100644 index 8a35c4c2d..000000000 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/TopicsPageSelectors.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.github.streamshub.systemtests.locators; - -public class TopicsPageSelectors { - private TopicsPageSelectors() {} - // ---------------------------- - // Topics page - // ---------------------------- - public static final String TPS_HEADER_TOTAL_TOPICS_BADGE = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title > div.pf-v6-l-split > div.pf-v6-l-split__item:nth-of-type(2) > span.pf-v6-c-label > span.pf-v6-c-label__content > span.pf-v6-c-label__text"; - - public static final String TPS_HEADER_BADGE_STATUS_SUCCESS = "body > div#root:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title > div.pf-v6-l-split > div.pf-v6-l-split__item:nth-of-type(3) > div > span.pf-v6-c-label > span.pf-v6-c-label__content"; - - public static final String TPS_HEADER_BADGE_STATUS_WARNING = "body > div#root:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title > div.pf-v6-l-split > div.pf-v6-l-split__item:nth-of-type(4) > div > span.pf-v6-c-label > span.pf-v6-c-label__content > span.pf-v6-c-label__text:nth-of-type(2)"; - - public static final String TPS_HEADER_BADGE_STATUS_ERROR = "body > div#root:nth-of-type(1) > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > h1.pf-v6-c-title > div.pf-v6-l-split > div.pf-v6-l-split__item:nth-of-type(5) > div > span.pf-v6-c-label > span.pf-v6-c-label__content > span.pf-v6-c-label__text:nth-of-type(2)"; - - public static final String TPS_TABLE_HEADER_SORT_BY_NAME = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(2) > table.pf-v6-c-table > thead.pf-v6-c-table__thead > tr.pf-v6-c-table__tr > th.pf-v6-c-table__th:nth-of-type(1)"; - public static final String TPS_TABLE_HEADER_SORT_BY_NAME_BUTTON = TPS_TABLE_HEADER_SORT_BY_NAME + " > button"; - - public static final String TPS_TABLE_HEADER_SORT_BY_STORAGE = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(2) > table.pf-v6-c-table > thead.pf-v6-c-table__thead > tr.pf-v6-c-table__tr > th.pf-v6-c-table__th:nth-of-type(5)"; - public static final String TPS_TABLE_HEADER_SORT_BY_STORAGE_BUTTON = TPS_TABLE_HEADER_SORT_BY_STORAGE + " > button"; - - public static final String TPS_NO_RESULTS_FOUND = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(2) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td > div.pf-v6-c-empty-state > div.pf-v6-c-empty-state__content > div.pf-v6-c-empty-state__header:nth-of-type(1) > div.pf-v6-c-empty-state__title:nth-of-type(2) > h4.pf-v6-c-empty-state__title-text"; - - public static final String TPS_TABLE_ROWS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(2) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr"; - - public static String getTopicsTableRowItems(int nth) { - return CssBuilder.joinLocators(new CssBuilder(TPS_TABLE_ROWS).nth(nth).build(), CssSelectors.PAGES_AD_TABLE_ROW_ITEMS); - } - - public static String getTopicsTableRowItem(int nthRow, int nthColumn) { - return new CssBuilder(getTopicsTableRowItems(nthRow)).nth(nthColumn).build(); - } - - - public static final String TPS_TOP_TOOLBAR_SEARCH_CLEAR_ALL_FILTERS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(2) > div.pf-v6-c-toolbar__group:nth-of-type(2) > div.pf-v6-c-toolbar__item > button.pf-v6-c-button"; - - public static final String TPS_TOP_TOOLBAR_FILTER_TYPE_DROPDOWN = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item > div.pf-v6-c-toolbar__group:nth-of-type(1) > div.pf-v6-c-toolbar__group:nth-of-type(2) > div:nth-of-type(1) > button.pf-v6-c-menu-toggle"; - public static final String TPS_TOP_TOOLBAR_FILTER_TYPE_DROPDOWN_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item > div.pf-v6-c-toolbar__group:nth-of-type(1) > div.pf-v6-c-toolbar__group:nth-of-type(2) > div:nth-of-type(1) > div.pf-v6-c-menu > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li"; - - public static final String TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(1) > div.pf-v6-c-toolbar__group:nth-of-type(1) > div.pf-v6-c-toolbar__group:nth-of-type(2) > div.pf-v6-c-toolbar__item:nth-of-type(2) > div.pf-v6-c-input-group > div.pf-v6-c-input-group__item:nth-of-type(1) > div.pf-v6-c-text-input-group > div.pf-v6-c-text-input-group__main > span.pf-v6-c-text-input-group__text > input.pf-v6-c-text-input-group__text-input"; - - public static final String TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(1) > div.pf-v6-c-toolbar__group:nth-of-type(1) > div.pf-v6-c-toolbar__group:nth-of-type(2) > div.pf-v6-c-toolbar__item:nth-of-type(2) > div.pf-v6-c-input-group > div.pf-v6-c-input-group__item:nth-of-type(2) > button.pf-v6-c-button"; - public static final String TPS_TOP_TOOLBAR_FILTER_BY_STATUS_DROPDOWN = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item > div.pf-v6-c-toolbar__group:nth-of-type(1) > div.pf-v6-c-toolbar__group:nth-of-type(2) > div.pf-v6-c-toolbar__item:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - - public static final String TPS_TOP_TOOLBAR_FILTER_BY_STATUS_DROPDOWN_ITEMS = "body > div.pf-v6-c-menu:nth-of-type(2) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li"; - - - public static final String TPS_TOP_PAGINATION_DROPDOWN_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(2) > div > div.pf-v6-c-pagination > div.pf-v6-c-pagination__page-menu:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - - public static final String TPS_TOP_PAGINATION_DROPDOWN_BUTTON_TEXT = TPS_TOP_PAGINATION_DROPDOWN_BUTTON + " > span.pf-v6-c-menu-toggle__text"; - - public static final String TPS_PAGINATION_DROPDOWN_ITEMS = "body > div.pf-v6-c-menu:nth-of-type(2) > div.pf-v6-c-menu__content > ul.pf-v6-c-menu__list > li"; - - public static final String TPS_TOP_PAGINATION_NAV_PREV_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(2) > div > div.pf-v6-c-pagination > nav.pf-v6-c-pagination__nav > div.pf-v6-c-pagination__nav-control:nth-of-type(1) > button"; - - public static final String TPS_TOP_PAGINATION_NAV_NEXT_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(1) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item:nth-of-type(2) > div > div.pf-v6-c-pagination > nav.pf-v6-c-pagination__nav > div.pf-v6-c-pagination__nav-control:nth-of-type(2) > button"; - - public static final String TPS_BOTTOM_PAGINATION_DROPDOWN_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(3) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item > div > div.pf-v6-c-pagination > div.pf-v6-c-pagination__page-menu:nth-of-type(2) > button.pf-v6-c-menu-toggle"; - - public static final String TPS_BOTTOM_PAGINATION_DROPDOWN_BUTTON_TEXT = TPS_BOTTOM_PAGINATION_DROPDOWN_BUTTON + "> span.pf-v6-c-menu-toggle__text"; - - public static final String TPS_BOTTOM_PAGINATION_NAV_PREV_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(3) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item > div > div.pf-v6-c-pagination > nav.pf-v6-c-pagination__nav > div.pf-v6-c-pagination__nav-control:nth-of-type(1) button"; - - public static final String TPS_BOTTOM_PAGINATION_NAV_NEXT_BUTTON = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(3) > div.pf-v6-c-toolbar > div.pf-v6-c-toolbar__content:nth-of-type(1) > div.pf-v6-c-toolbar__content-section > div.pf-v6-c-toolbar__item > div > div.pf-v6-c-pagination > nav.pf-v6-c-pagination__nav > div.pf-v6-c-pagination__nav-control:nth-of-type(2) button"; - - public static final String TPS_GROUPS_TABLE_FIRST_GROUP = "body > div#root > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(4) > div.pf-v6-c-page__main-body > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td:nth-of-type(1) > a"; - - public static final String PAGES_NOT_AUTHORIZED_CONTENT = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > section.pf-v6-c-page__main-section:nth-of-type(3) > div.pf-v6-c-page__main-body > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(2) > table.pf-v6-c-table > tbody.pf-v6-c-table__tbody > tr.pf-v6-c-table__tr > td.pf-v6-c-table__td > div.pf-v6-c-empty-state > div.pf-v6-c-empty-state__content > div.pf-v6-c-empty-state__header:nth-of-type(1) > div.pf-v6-c-empty-state__title:nth-of-type(2) > h4.pf-v6-c-empty-state__title-text"; - -} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/Masthead.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/Masthead.java new file mode 100644 index 000000000..8b6da98eb --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/Masthead.java @@ -0,0 +1,51 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.components; + +import com.github.streamshub.systemtests.locators.IdConstants; +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +/** App-chrome locators shared by every page (masthead toolbar, cluster switcher, user menu). */ +public final class Masthead { + private Masthead() {} + + private static Locator toolbar(Page page) { + return page.locator(IdConstants.MASTHEAD_TOOLBAR); + } + + /** The "Kafka Clusters" toggle button (ClusterSwitcher.tsx) that opens the cluster-switch menu. */ + public static Locator clusterSwitcherButton(Page page) { + return Locators.buttonNamed(toolbar(page), TextConstants.KAFKA_CLUSTERS); + } + + /** The badge showing the total available Kafka cluster count, nested inside the switcher button. */ + public static Locator totalAvailableKafkaCount(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.TOTAL_AVAILABLE_KAFKA_COUNT); + } + + /** Cluster entries in the open cluster-switcher menu (portaled to the page root). */ + public static Locator clusterListItems(Page page) { + return Locators.menuItems(page); + } + + /** + * The logged-in user's dropdown toggle. Identified by the avatar's {@code alt} + * attribute (UserDropdown.tsx sets {@code alt={username}}), which is real test + * data (the username under assertion), not UI chrome text. + */ + public static Locator userDropdownButton(Page page, String username) { + return toolbar(page).locator("button").filter(new Locator.FilterOptions().setHas(page.getByAltText(username))); + } + + public static Locator logoutMenuItem(Page page) { + return page.getByRole(AriaRole.MENUITEM, new Page.GetByRoleOptions().setName(TextConstants.LOGOUT)); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/Modal.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/Modal.java new file mode 100644 index 000000000..fc616110b --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/Modal.java @@ -0,0 +1,33 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.components; + +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +/** Scopes locators to the currently-open PatternFly modal dialog (role=dialog). */ +public final class Modal { + private Modal() {} + + public static Locator root(Page page) { + return page.getByRole(AriaRole.DIALOG); + } + + public static Locator heading(Page page) { + return root(page).getByRole(AriaRole.HEADING); + } + + public static Locator confirmButton(Page page) { + return Locators.buttonNamed(root(page), TextConstants.CONFIRM); + } + + public static Locator cancelButton(Page page) { + return Locators.buttonNamed(root(page), TextConstants.CANCEL); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/ResourceTable.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/ResourceTable.java new file mode 100644 index 000000000..173647e20 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/components/ResourceTable.java @@ -0,0 +1,55 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.components; + +import com.github.streamshub.systemtests.locators.Locators; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +/** + * Wraps a semantically-anchored PatternFly table (found via OUIA id or accessible + * name, never an absolute DOM path) and exposes row/cell lookups scoped to that + * table, so a specific row is found by matching its own data rather than by + * position in the page. + */ +public class ResourceTable { + private final Locator root; + + private ResourceTable(Locator root) { + this.root = root; + } + + public static ResourceTable byOuiaId(Page page, String ouiaId) { + return new ResourceTable(Locators.byOuiaId(page, ouiaId)); + } + + public static ResourceTable byAccessibleName(Page page, String accessibleName) { + return new ResourceTable(page.getByRole(AriaRole.TABLE, new Page.GetByRoleOptions().setName(accessibleName))); + } + + public Locator root() { + return root; + } + + public Locator rows() { + return root.locator("tbody tr"); + } + + public int rowCount() { + return rows().count(); + } + + /** The row whose visible text contains {@code text} (e.g. a topic/user/group name the test itself created). */ + public Locator row(String text) { + return rows().filter(new Locator.FilterOptions().setHasText(text)); + } + + /** The cell in {@code row} for the column labelled {@code dataLabel} (PatternFly's {@code Td dataLabel} prop renders a {@code data-label} attribute). */ + public Locator cell(Locator row, String dataLabel) { + return row.locator("td[data-label='" + dataLabel + "']"); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/ClusterOverviewPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/ClusterOverviewPage.java new file mode 100644 index 000000000..f648b8794 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/ClusterOverviewPage.java @@ -0,0 +1,87 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.IdConstants; +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +import java.util.regex.Pattern; + +/** Locators for the Kafka cluster Overview page. */ +public final class ClusterOverviewPage { + private ClusterOverviewPage() {} + + public static Locator pauseReconciliationButton(Page page) { + return Locators.buttonNamed(page, Pattern.compile(TextConstants.PAUSE_OR_RESUME_RECONCILIATION_PATTERN)); + } + + public static Locator reconciliationPausedBanner(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.RECONCILIATION_PAUSED_BANNER); + } + + public static Locator reconciliationResumeButton(Page page) { + return Locators.buttonNamed(reconciliationPausedBanner(page), TextConstants.RESUME); + } + + public static Locator clusterName(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setLevel(2)); + } + + public static Locator brokerCount(Page page) { + return page.getByRole(AriaRole.LINK).filter(new Locator.FilterOptions().setHasText(Pattern.compile("^\\d+/\\d+$"))); + } + + public static Locator kafkaVersion(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.CLUSTER_KAFKA_VERSION); + } + + public static Locator warningsToggle(Page page) { + return page.locator(IdConstants.CLUSTER_WARNINGS_TOGGLE); + } + + public static Locator warningsList(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.CLUSTER_WARNINGS_LIST); + } + + /** + * PatternFly's {@code DataListItem} consumes its {@code aria-labelledby} prop internally (wiring + * cells together) rather than rendering it as an HTML attribute - confirmed against the live DOM, + * where each item's {@code <li>} has no {@code aria-labelledby} at all. Scope under the already + * OUIA-anchored warnings list instead and find items by their native {@code <li>} role. + */ + public static Locator warningMessages(Page page) { + return warningsList(page).getByRole(AriaRole.LISTITEM); + } + + public static Locator recentTopicsListItems(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.RECENT_TOPICS_LIST).getByRole(AriaRole.LISTITEM); + } + + public static Locator totalTopics(Page page) { + return page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName(Pattern.compile("topics$"))); + } + + public static Locator totalPartitions(Page page) { + return page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName(Pattern.compile("partitions$"))); + } + + public static Locator fullyReplicated(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.FULLY_REPLICATED_STATUS_LINK); + } + + public static Locator underReplicated(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.UNDER_REPLICATED_STATUS_LINK); + } + + public static Locator unavailable(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.OFFLINE_STATUS_LINK); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/GroupsPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/GroupsPage.java new file mode 100644 index 000000000..e47c0067b --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/GroupsPage.java @@ -0,0 +1,34 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.github.streamshub.systemtests.locators.components.ResourceTable; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public final class GroupsPage { + private GroupsPage() {} + + public static Locator heading(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setLevel(1).setName(TextConstants.GROUPS)); + } + + /** No id/aria-label on GroupsPage.tsx's SearchInput; only the placeholder text is stable. */ + public static Locator searchInput(Page page) { + return page.getByPlaceholder(TextConstants.FILTER_BY_NAME); + } + + public static ResourceTable table(Page page) { + return ResourceTable.byOuiaId(page, OuiaIdConstants.GROUPS_LISTING); + } + + public static Locator noGroupsAvailable(Page page) { + return page.getByText(TextConstants.NO_GROUPS_AVAILABLE); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaConnectPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaConnectPage.java new file mode 100644 index 000000000..846e15bc1 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaConnectPage.java @@ -0,0 +1,37 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.TextConstants; +import com.github.streamshub.systemtests.locators.components.ResourceTable; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public final class KafkaConnectPage { + private KafkaConnectPage() {} + + public static Locator connectorsTab(Page page) { + return page.getByRole(AriaRole.TAB, new Page.GetByRoleOptions().setName(TextConstants.CONNECTORS)); + } + + public static Locator connectClustersTab(Page page) { + return page.getByRole(AriaRole.TAB, new Page.GetByRoleOptions().setName(TextConstants.CLUSTERS)); + } + + public static Locator pageTitle(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setLevel(1).setName(TextConstants.KAFKA_CONNECT)); + } + + /** Both the Connectors and Connect Clusters tabs render the same SearchInput shape with only the placeholder differing, and share the PatternFly default aria-label ("Search input"). */ + public static Locator nameFilterInput(Page page) { + return page.getByLabel(TextConstants.SEARCH_INPUT); + } + + public static ResourceTable table(Page page, boolean connectors) { + return ResourceTable.byAccessibleName(page, connectors ? TextConstants.CONNECTORS_TABLE : TextConstants.CONNECT_CLUSTERS_TABLE); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaDashboardPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaDashboardPage.java new file mode 100644 index 000000000..56aeae7bc --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaDashboardPage.java @@ -0,0 +1,26 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.github.streamshub.systemtests.locators.components.ResourceTable; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; + +public final class KafkaDashboardPage { + private KafkaDashboardPage() {} + + public static ResourceTable table(Page page) { + return ResourceTable.byOuiaId(page, OuiaIdConstants.KAFKA_CLUSTERS_TABLE); + } + + /** The "View" button in the row for the named cluster, found by matching the cluster name itself rather than a row index. */ + public static Locator viewButton(Page page, String clusterName) { + return Locators.buttonNamed(table(page).row(clusterName), TextConstants.VIEW); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaUsersPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaUsersPage.java new file mode 100644 index 000000000..69cf0b6db --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/KafkaUsersPage.java @@ -0,0 +1,51 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.IdConstants; +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.github.streamshub.systemtests.locators.components.ResourceTable; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public final class KafkaUsersPage { + private KafkaUsersPage() {} + + public static ResourceTable table(Page page) { + return ResourceTable.byOuiaId(page, OuiaIdConstants.KAFKA_USERS_LISTING); + } + + public static Locator usernameFilterInput(Page page) { + return page.locator(IdConstants.USERNAME_FILTER); + } + + public static Locator userNameHeading(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setLevel(1)); + } + + public static Locator nameField(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.USER_DETAIL_NAME); + } + + public static Locator usernameField(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.USER_DETAIL_USERNAME); + } + + public static Locator authenticationField(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.USER_DETAIL_AUTHENTICATION); + } + + public static Locator creationTimeField(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.USER_DETAIL_CREATION_TIME); + } + + public static ResourceTable authorizationTable(Page page) { + return ResourceTable.byAccessibleName(page, TextConstants.AUTHORIZATION); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/MessagesPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/MessagesPage.java new file mode 100644 index 000000000..11e1d4caf --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/MessagesPage.java @@ -0,0 +1,114 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.github.streamshub.systemtests.locators.components.ResourceTable; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public final class MessagesPage { + private MessagesPage() {} + + public static ResourceTable table(Page page) { + return ResourceTable.byAccessibleName(page, TextConstants.MESSAGES_TABLE); + } + + /** The topic detail page's h1 title, showing the current topic name. */ + public static Locator pageTitle(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setLevel(1)); + } + + /** + * The message table's visible columns are user-configurable (ColumnsModal), so unlike other + * tables a stable data-label can't be assigned to a fixed column index - this stays positional, + * matching the table's own dynamic-column behavior rather than introducing a false stable identity. + * nthRow/nthColumn are 1-based, matching the rest of this codebase's row/column addressing. + */ + public static Locator cellAt(Page page, int nthRow, int nthColumn) { + return table(page).rows().nth(nthRow - 1).locator("td").nth(nthColumn - 1); + } + + public static Locator emptyBodyContent(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName(TextConstants.NO_MESSAGES_DATA)); + } + + public static Locator searchInput(Page page) { + return page.getByLabel(TextConstants.SEARCH_INPUT); + } + + public static Locator advancedSearchToggle(Page page) { + return page.getByLabel(TextConstants.OPEN_ADVANCED_SEARCH); + } + + public static Locator searchSubmitButton(Page page) { + return page.getByLabel(TextConstants.SEARCH, new Page.GetByLabelOptions().setExact(true)); + } + + public static Locator hasWordsInput(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.HAS_WORDS_INPUT); + } + + public static Locator whereDropdownButton(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.WHERE_DROPDOWN_BUTTON); + } + + public static Locator messagesFromDropdownButton(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.MESSAGES_FROM_DROPDOWN_BUTTON); + } + + /** Exact-match menu item lookup - PatternFly portals every open Select menu to the same DOM position, and numeric option labels (e.g. "5" vs "50") collide under substring matching, so this must stay an exact match. */ + public static Locator openMenuItem(Page page, String exactLabel) { + return page.getByRole(AriaRole.MENUITEM, new Page.GetByRoleOptions().setName(exactLabel).setExact(true)); + } + + public static Locator offsetInput(Page page) { + return page.getByLabel(TextConstants.SPECIFY_OFFSET); + } + + public static Locator timestampInput(Page page) { + return page.getByLabel(TextConstants.SPECIFY_TIMESTAMP); + } + + public static Locator unixTimestampInput(Page page) { + return page.getByLabel(TextConstants.SPECIFY_UNIX_TIMESTAMP); + } + + public static Locator retrieveTypeDropdownButton(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.RETRIEVE_TYPE_DROPDOWN_BUTTON); + } + + public static Locator retrieveLimitDropdownButton(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.RETRIEVE_LIMIT_DROPDOWN_BUTTON); + } + + public static Locator partitionDropdownButton(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.PARTITION_DROPDOWN_BUTTON); + } + + public static Locator formSearchButton(Page page) { + return Locators.buttonNamed(page, TextConstants.SEARCH, true); + } + + public static Locator formResetButton(Page page) { + return Locators.buttonNamed(page, TextConstants.RESET, true); + } + + public static Locator sidebarValueFormat(Page page) { + return page.locator(".pf-v6-c-description-list__group").filter(new Locator.FilterOptions().setHas(page.getByText(TextConstants.VALUE_FORMAT, new Page.GetByTextOptions().setExact(true)))).locator(".pf-v6-c-description-list__description"); + } + + public static Locator sidebarSchemaName(Page page) { + return page.locator("[role='tabpanel']").getByRole(AriaRole.PARAGRAPH); + } + + public static Locator sidebarSchemaCode(Page page) { + return page.locator("[role='tabpanel'] .pf-v6-c-code-block__code"); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/NodesPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/NodesPage.java new file mode 100644 index 000000000..d0d116e32 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/NodesPage.java @@ -0,0 +1,118 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.github.streamshub.systemtests.locators.components.ResourceTable; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public final class NodesPage { + private NodesPage() {} + + public static ResourceTable table(Page page) { + return ResourceTable.byOuiaId(page, OuiaIdConstants.NODES_LISTING); + } + + public static Locator totalCountBadge(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.NODES_TOTAL_BADGE); + } + + public static Locator workingNodesBadge(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.NODES_HEALTHY_BADGE); + } + + public static Locator warningNodesBadge(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.NODES_UNHEALTHY_BADGE); + } + + public static Locator totalNodesCount(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.OVERVIEW_TOTAL_NODES); + } + + public static Locator controllerRoleCount(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.OVERVIEW_CONTROLLER_ROLE); + } + + public static Locator brokerRoleCount(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.OVERVIEW_BROKER_ROLE); + } + + public static Locator leadControllerCount(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.OVERVIEW_LEAD_CONTROLLER); + } + + public static Locator nodePoolFilterButton(Page page) { + return Locators.buttonNamed(page, TextConstants.NODE_POOL); + } + + public static Locator roleFilterButton(Page page) { + return Locators.buttonNamed(page, TextConstants.ROLES); + } + + public static Locator statusFilterButton(Page page) { + return Locators.buttonNamed(page, TextConstants.STATUS); + } + + public static Locator filterMenuItems(Page page) { + return Locators.menuItems(page); + } + + public static Locator clearAllFiltersButton(Page page) { + return Locators.buttonNamed(page, TextConstants.CLEAR_ALL_FILTERS); + } + + public static Locator notAuthorizedContent(Page page) { + return page.getByText(TextConstants.FORBIDDEN_STATUS_CODE); + } + + public static ResourceTable rebalanceTable(Page page) { + return ResourceTable.byAccessibleName(page, TextConstants.REBALANCES_TABLE); + } + + public static Locator rebalanceExpandToggle(Locator row) { + return Locators.buttonNamed(row, TextConstants.DETAILS); + } + + public static Locator rebalanceActionsMenuButton(Locator row) { + return Locators.buttonNamed(row, TextConstants.ACTIONS); + } + + public static Locator rebalanceMenuItem(Page page, String name) { + return page.getByRole(AriaRole.MENUITEM, new Page.GetByRoleOptions().setName(name)); + } + + public static Locator rebalanceAutoApprovalValue(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.REBALANCE_AUTO_APPROVAL_VALUE); + } + + public static Locator rebalanceModeValue(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.REBALANCE_MODE_VALUE); + } + + public static Locator rebalanceDataToMoveMb(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.REBALANCE_DATA_TO_MOVE_MB); + } + + public static Locator rebalanceMonitoredPartitionsPercentage(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.REBALANCE_MONITORED_PARTITIONS_PERCENTAGE); + } + + public static Locator rebalanceNumReplicaMovements(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.REBALANCE_NUM_REPLICA_MOVEMENTS); + } + + public static Locator rebalanceBalancednessBefore(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.REBALANCE_BALANCEDNESS_BEFORE); + } + + public static Locator rebalanceBalancednessAfter(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.REBALANCE_BALANCEDNESS_AFTER); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/SingleGroupPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/SingleGroupPage.java new file mode 100644 index 000000000..beafc78d7 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/SingleGroupPage.java @@ -0,0 +1,91 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.IdConstants; +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public final class SingleGroupPage { + private SingleGroupPage() {} + + public static Locator heading(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setLevel(1)); + } + + public static Locator breadcrumbGroupName(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.BREADCRUMB_GROUP_NAME); + } + + public static Locator resetOffsetButton(Page page) { + return Locators.buttonNamed(page, TextConstants.RESET_OFFSETS); + } + + public static Locator topicDropdownButton(Page page) { + return page.locator(IdConstants.TOPIC_SELECT); + } + + public static Locator topicDropdownSearchInput(Page page) { + return page.getByPlaceholder(TextConstants.SEARCH); + } + + public static Locator topicDropdownResult(Page page, String topicName) { + return page.getByRole(AriaRole.OPTION, new Page.GetByRoleOptions().setName(topicName)); + } + + public static Locator offsetTypeDropdownButton(Page page) { + return page.locator(IdConstants.OFFSET_SELECT); + } + + public static Locator earliestOffsetOption(Page page) { + return page.getByRole(AriaRole.OPTION, new Page.GetByRoleOptions().setName(TextConstants.EARLIEST_OFFSET)); + } + + public static Locator latestOffsetOption(Page page) { + return page.getByRole(AriaRole.OPTION, new Page.GetByRoleOptions().setName(TextConstants.LATEST_OFFSET)); + } + + public static Locator specificDateTimeIsoOption(Page page) { + return page.getByRole(AriaRole.OPTION, new Page.GetByRoleOptions().setName(TextConstants.SPECIFIC_DATETIME_ISO)); + } + + public static Locator specificDateTimeUnixOption(Page page) { + return page.getByRole(AriaRole.OPTION, new Page.GetByRoleOptions().setName(TextConstants.SPECIFIC_DATETIME_UNIX)); + } + + public static Locator deleteCommittedOffsetsOption(Page page) { + return page.getByRole(AriaRole.OPTION, new Page.GetByRoleOptions().setName(TextConstants.DELETE_COMMITTED_OFFSETS)); + } + + public static Locator datetimeInput(Page page) { + return page.locator(IdConstants.DATETIME_INPUT); + } + + public static Locator resetButton(Page page) { + return Locators.buttonNamed(page, TextConstants.RESET_OFFSETS, true); + } + + public static Locator dryRunButton(Page page) { + return Locators.buttonNamed(page, TextConstants.DRY_RUN); + } + + public static Locator dryRunCommandToggle(Page page) { + return page.getByText(TextConstants.CLI_COMMAND); + } + + public static Locator dryRunCommandText(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.DRY_RUN_COMMAND_TEXT); + } + + /** ResetOffsetModal and DryRunResults are two stacked, simultaneously-open dialogs; .last() targets the topmost one. */ + public static Locator dryRunCancelButton(Page page) { + return Locators.buttonNamed(page.getByRole(AriaRole.DIALOG).last(), TextConstants.CANCEL); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/TopicsPage.java b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/TopicsPage.java new file mode 100644 index 000000000..c3658fb22 --- /dev/null +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/locators/pages/TopicsPage.java @@ -0,0 +1,105 @@ +/* + * Copyright Console Authors. + * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ + +package com.github.streamshub.systemtests.locators.pages; + +import com.github.streamshub.systemtests.locators.IdConstants; +import com.github.streamshub.systemtests.locators.Locators; +import com.github.streamshub.systemtests.locators.OuiaIdConstants; +import com.github.streamshub.systemtests.locators.TextConstants; +import com.github.streamshub.systemtests.locators.components.ResourceTable; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.options.AriaRole; + +public final class TopicsPage { + private TopicsPage() {} + + public static ResourceTable table(Page page) { + return ResourceTable.byOuiaId(page, OuiaIdConstants.TOPICS_TABLE); + } + + public static Locator totalTopicsBadge(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.TOPICS_TOTAL_BADGE); + } + + public static Locator fullyReplicatedBadge(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.TOPICS_FULLY_REPLICATED_BADGE); + } + + public static Locator underReplicatedBadge(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.TOPICS_UNDER_REPLICATED_BADGE); + } + + public static Locator offlineBadge(Page page) { + return Locators.byOuiaId(page, OuiaIdConstants.TOPICS_OFFLINE_BADGE); + } + + public static Locator searchInput(Page page) { + // TopicsFilterToolbar.tsx sets id={`${selectedFilterType}-filter-input`}; "name" is the default filter type. + return page.locator("input[id$='" + IdConstants.FILTER_INPUT_ID_SUFFIX + "']"); + } + + public static Locator searchButton(Page page) { + return Locators.buttonNamed(page, TextConstants.SEARCH); + } + + public static Locator clearAllFiltersButton(Page page) { + return Locators.buttonNamed(page, TextConstants.CLEAR_ALL_FILTERS); + } + + public static Locator filterTypeDropdownButton(Page page) { + return page.locator(IdConstants.FILTER_TYPE_SELECT); + } + + public static Locator filterTypeDropdownItems(Page page) { + return Locators.menuItems(page); + } + + public static Locator statusFilterDropdownButton(Page page) { + return page.locator(IdConstants.STATUS_FILTER_SELECT); + } + + public static Locator statusFilterDropdownItems(Page page) { + return Locators.menuItems(page); + } + + public static Locator noResultsFound(Page page) { + return page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName(TextConstants.NO_RESULTS_FOUND)); + } + + public static Locator notAuthorizedContent(Page page) { + return page.getByText(TextConstants.NOT_AUTHORIZED); + } + + public static Locator sortByNameHeader(Page page) { + return page.getByRole(AriaRole.COLUMNHEADER, new Page.GetByRoleOptions().setName(TextConstants.NAME)); + } + + public static Locator sortByStorageHeader(Page page) { + return page.getByRole(AriaRole.COLUMNHEADER, new Page.GetByRoleOptions().setName(TextConstants.STORAGE)); + } + + public static Locator paginationDropdownButton(Page page, boolean top) { + return page.locator(top ? IdConstants.PAGINATION_TOP_TOGGLE : IdConstants.PAGINATION_BOTTOM_TOGGLE); + } + + public static Locator paginationDropdownItems(Page page) { + return Locators.menuItems(page); + } + + public static Locator paginationPrevButton(Page page, boolean top) { + return Locators.buttonNamed(page, TextConstants.GO_TO_PREVIOUS_PAGE).nth(top ? 0 : 1); + } + + public static Locator paginationNextButton(Page page, boolean top) { + return Locators.buttonNamed(page, TextConstants.GO_TO_NEXT_PAGE).nth(top ? 0 : 1); + } + + /** Link to the first group in the "Groups reading from this topic" table on the Topic Groups tab. */ + public static Locator groupsTabFirstGroupLink(Page page) { + return ResourceTable.byOuiaId(page, OuiaIdConstants.TOPIC_GROUPS_LISTING).rows().first().locator("a"); + } +} diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/playwright/PwUtils.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/playwright/PwUtils.java index 88a26d8a5..aa61d49e6 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/playwright/PwUtils.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/playwright/PwUtils.java @@ -6,8 +6,7 @@ import com.github.streamshub.systemtests.constants.TimeConstants; import com.github.streamshub.systemtests.enums.BrowserTypes; import com.github.streamshub.systemtests.exceptions.SetupException; -import com.github.streamshub.systemtests.locators.CssSelectors; -import com.github.streamshub.systemtests.locators.KafkaDashboardPageSelectors; +import com.github.streamshub.systemtests.locators.components.Masthead; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.Utils; import com.github.streamshub.systemtests.utils.WaitUtils; @@ -122,10 +121,6 @@ public static String getTrimmedText(String text) { // -------------------------- // Wait for locator visible // -------------------------- - public static void waitForLocatorVisible(TestCaseConfig tcc, String selector) { - waitForLocatorVisible(tcc.page().locator(selector)); - } - public static void waitForLocatorVisible(Locator locator) { waitForLocatorVisible(locator, TimeConstants.ELEMENT_VISIBILITY_TIMEOUT); } @@ -139,10 +134,6 @@ public static void waitForLocatorVisible(Locator locator, long timeout) { // -------------------------- // Click actions // -------------------------- - public static void waitForLocatorAndClick(TestCaseConfig tcc, String selector) { - waitForLocatorAndClick(tcc.page().locator(selector)); - } - public static void waitForLocatorAndClick(Locator locator) { waitForLocatorVisible(locator); clickWithRetry(locator); @@ -170,10 +161,6 @@ public static boolean click(Locator locator) { // -------------------------- // Fill actions // -------------------------- - public static void waitForLocatorAndFill(TestCaseConfig tcc, String selector, String fillText) { - waitForLocatorAndFill(tcc.page().locator(selector), fillText); - } - public static void waitForLocatorAndFill(Locator locator, String fillText) { waitForLocatorVisible(locator); fillWithRetry(locator, fillText); @@ -183,9 +170,6 @@ public static void fillWithRetry(Locator locator, String text) { Utils.retryAction("fill locator", () -> fill(locator, text), Constants.DEFAULT_ACTION_RETRIES); } - public static boolean fill(TestCaseConfig tcc, String selector, String text) { - return fill(tcc.page().locator(selector), text); - } public static boolean fill(Locator locator, String text) { LOGGER.debug("Filling locator [{}] with text [{}]", locator, text); try { @@ -206,12 +190,8 @@ public static void waitForContainsText(TestCaseConfig tcc, String selector, Stri waitForContainsText(tcc, selector, text, reload, true, TimeConstants.COMPONENT_LOAD_TIMEOUT_SHORT, Constants.DEFAULT_ACTION_RETRIES); } - public static void waitForContainsText(TestCaseConfig tcc, String selector, String text, boolean reload, boolean exactCase) { - waitForContainsText(tcc, selector, text, reload, exactCase, TimeConstants.COMPONENT_LOAD_TIMEOUT_SHORT, Constants.DEFAULT_ACTION_RETRIES); - } - - public static void waitForContainsText(TestCaseConfig tcc, String selector, String text, long waitTime) { - waitForContainsText(tcc, selector, text, true, true, waitTime, Constants.DEFAULT_ACTION_RETRIES); + public static void waitForContainsText(Locator locator, String text, long waitTime) { + waitForContainsText(locator, text, true, true, waitTime, Constants.DEFAULT_ACTION_RETRIES); } /** @@ -251,6 +231,35 @@ public static void waitForContainsText(TestCaseConfig tcc, String selector, Stri ); } + public static void waitForContainsText(Locator locator, String text, boolean reload) { + waitForContainsText(locator, text, reload, true, TimeConstants.COMPONENT_LOAD_TIMEOUT_SHORT, Constants.DEFAULT_ACTION_RETRIES); + } + + public static void waitForContainsText(Locator locator, String text, boolean reload, boolean exactCase) { + waitForContainsText(locator, text, reload, exactCase, TimeConstants.COMPONENT_LOAD_TIMEOUT_SHORT, Constants.DEFAULT_ACTION_RETRIES); + } + + public static void waitForContainsText(Locator locator, String text, boolean reload, boolean exactCase, long waitTime, int retries) { + LOGGER.debug("Waiting for locator [{}] to contain text [{}]", locator, text); + Utils.retryAction("waitForContainsText: " + text, + () -> { + if (locatorContainsText(locator, text, exactCase)) { + screenshot(locator.page(), "wait-text-success"); + return true; + } + + LOGGER.warn("Locator did not contain text [{}]", text); + screenshot(locator.page(), "wait-text-retry"); + + if (reload) { + locator.page().reload(getDefaultReloadOpts()); + } + + return false; + }, retries, waitTime + ); + } + /** * Checks whether the given Playwright {@link Locator} contains the expected text. * @@ -311,8 +320,7 @@ public static void waitForLocatorContainsText(Locator locator, String text, long * found, the check is retried until the timeout is reached. Optionally, the * page can be reloaded between retries.</p> * - * @param tcc {@link TestCaseConfig} instance representing the current browser page - * @param selector selector used to resolve the {@link Locator} whose attribute will be inspected + * @param locator the {@link Locator} whose attribute will be inspected * @param text expected text to be contained in the attribute value * @param attribute name of the attribute to inspect (e.g. {@code value}, {@code aria-label}, {@code href}) * @param reload if {@code true}, the page will be reloaded between retry attempts @@ -320,23 +328,22 @@ public static void waitForLocatorContainsText(Locator locator, String text, long * * @throws AssertionError if the attribute value does not contain the expected text within the timeout */ - public static void waitForAttributeContainsText(TestCaseConfig tcc, String selector, String text, String attribute, boolean reload, boolean exactCase) { - LOGGER.debug("Waiting for locator [{}] to contain value [{}]", selector, text); + public static void waitForAttributeContainsText(Locator locator, String text, String attribute, boolean reload, boolean exactCase) { + LOGGER.debug("Waiting for locator [{}] to contain value [{}]", locator, text); Utils.retryAction("waitForAttributeContainsText: " + text, () -> { - // For reliability let's wait for the selector before getting it's value - waitForLocatorVisible(tcc, selector); + waitForLocatorVisible(locator); - if (attributeContainsText(tcc.page().locator(selector), attribute, text, exactCase)) { - screenshot(tcc, tcc.kafkaName(), "wait-attribute-success"); + if (attributeContainsText(locator, attribute, text, exactCase)) { + screenshot(locator.page(), "wait-attribute-success"); return true; } LOGGER.warn("Locator attribute did not contain text [{}]", text); - screenshot(tcc, tcc.kafkaName(), "wait-attribute-retry"); + screenshot(locator.page(), "wait-attribute-retry"); if (reload) { - reload(tcc); + locator.page().reload(getDefaultReloadOpts()); } return false; @@ -344,10 +351,6 @@ public static void waitForAttributeContainsText(TestCaseConfig tcc, String selec ); } - public static boolean attributeContainsText(TestCaseConfig tcc, String selector, String attribute, String expectedText, boolean exactCase) { - return attributeContainsText(tcc.page().locator(selector), attribute, expectedText, exactCase); - } - /** * Checks whether a specified attribute of a {@link Locator} contains the expected text. @@ -391,27 +394,26 @@ public static boolean attributeContainsText(Locator locator, String attribute, S * If the count does not match the expected value, the check is retried until the * timeout is reached. Optionally, the page can be reloaded between retries.</p> * - * @param tcc {@link TestCaseConfig} containing the page instance used for locating elements - * @param count expected number of elements matching the selector - * @param selector selector used to resolve the {@link Locator} + * @param count expected number of elements matching the locator + * @param locator the {@link Locator} to count matches for * @param reload if {@code true}, the page is reloaded between retry attempts when the count is incorrect */ - public static void waitForLocatorCount(TestCaseConfig tcc, int count, String selector, boolean reload) { - LOGGER.debug("Waiting for locator [{}] to contain item count [{}] reload={}", selector, count, reload); + public static void waitForLocatorCount(int count, Locator locator, boolean reload) { + LOGGER.debug("Waiting for locator [{}] to contain item count [{}] reload={}", locator, count, reload); Utils.retryAction("waitForLocatorCount: " + count, () -> { - int locatorCount = tcc.page().locator(selector).all().size(); + int locatorCount = locator.all().size(); if (locatorCount == count) { LOGGER.debug("Locator has correct item count {}", count); - screenshot(tcc, tcc.kafkaName(), "wait-count-success"); + screenshot(locator.page(), "wait-count-success"); return true; } LOGGER.warn("Locator has incorrect item count {}, need {}", locatorCount, count); - screenshot(tcc, tcc.kafkaName(), "wait-count-retry"); + screenshot(locator.page(), "wait-count-retry"); if (reload) { - tcc.page().reload(getDefaultReloadOpts()); + locator.page().reload(getDefaultReloadOpts()); } return false; @@ -429,29 +431,26 @@ public static void waitForLocatorCount(TestCaseConfig tcc, int count, String sel * If the expected state is not reached, the check is retried until the timeout * is reached. Optionally, the page can be reloaded between retries.</p> * - * @param tcc {@link TestCaseConfig} containing the Playwright page instance - * @param selector selector used to resolve the {@link Locator} + * @param locator the {@link Locator} to check * @param shouldBeEnabled {@code true} if the element is expected to be enabled, * {@code false} if it is expected to be disabled * @param reload if {@code true}, the page is reloaded between retry attempts */ - public static void waitForElementEnabledState(TestCaseConfig tcc, String selector, boolean shouldBeEnabled, boolean reload) { - LOGGER.debug("Waiting for locator [{}] to reach enabled state [{}]", selector, shouldBeEnabled); + public static void waitForElementEnabledState(Locator locator, boolean shouldBeEnabled, boolean reload) { + LOGGER.debug("Waiting for locator [{}] to reach enabled state [{}]", locator, shouldBeEnabled); Utils.retryAction("waitForElementEnabledState enabled=" + shouldBeEnabled, () -> { - Locator locator = tcc.page().locator(selector); - if (locator.isEnabled() == shouldBeEnabled) { LOGGER.debug("Locator has correct state enabled={}", locator.isEnabled()); - screenshot(tcc, tcc.kafkaName(), "wait-enabled-success"); + screenshot(locator.page(), "wait-enabled-success"); return true; } LOGGER.warn("Locator has incorrect state enabled={}, need enabled={}", locator.isEnabled(), shouldBeEnabled); - screenshot(tcc, tcc.kafkaName(), "wait-enabled-retry"); + screenshot(locator.page(), "wait-enabled-retry"); if (reload) { - tcc.page().reload(getDefaultReloadOpts()); + locator.page().reload(getDefaultReloadOpts()); } return false; @@ -475,7 +474,7 @@ public static void waitForConsoleUiWithKeycloakToBecomeReady(TestCaseConfig tcc) } // Check if Keycloak login page is displayed - if (tcc.page().locator(CssSelectors.LOGIN_KEYCLOAK_PAGE_TITLE).isVisible()) { + if (tcc.page().locator("#kc-header").isVisible()) { LOGGER.info("Console website is ready"); screenshot(tcc, tcc.kafkaName(), "keycloak-ready"); return true; @@ -630,9 +629,10 @@ public static void loginWithOidcUser(TestCaseConfig tcc, String username, String waitForConsoleUiWithKeycloakToBecomeReady(tcc); navigate(tcc, baseUrl); // Login with user - waitForLocatorAndFill(tcc, CssSelectors.LOGIN_KEYCLOAK_USERNAME_INPUT, username); - waitForLocatorAndFill(tcc, CssSelectors.LOGIN_KEYCLOAK_PASSWORD_INPUT, password); - waitForLocatorAndClick(tcc, CssSelectors.LOGIN_KEYCLOAK_SIGN_IN_BUTTON); + Locator loginForm = tcc.page().locator("#kc-form-login"); + waitForLocatorAndFill(loginForm.locator("input#username"), username); + waitForLocatorAndFill(loginForm.locator("input#password"), password); + waitForLocatorAndClick(loginForm.locator("button#kc-login")); // Wait for redirect to overview page waitForUrl(tcc, baseUrl, true); LOGGER.info("Successfully logged into Console"); @@ -655,18 +655,11 @@ public static void logoutUser(TestCaseConfig tcc, String userName, boolean https Utils.retryAction("Log-out user " + userName, () -> { String dashboardUrl = ConsoleUtils.getConsoleUiUrl(tcc.consoleInstanceName(), https) + "/"; - // There is a xpath difference between logout button in dashboard and in navbar on other pages - if (tcc.page().url().equals(dashboardUrl)) { - waitForLocatorAndClick(tcc, KafkaDashboardPageSelectors.KDPS_CURRENTLY_LOGGED_USER_BUTTON); - } else { - waitForLocatorAndClick(tcc, CssSelectors.PAGES_CURRENTLY_LOGGED_USER_BUTTON); - } - - waitForLocatorAndClick(tcc, CssSelectors.PAGES_LOGOUT_BUTTON); + waitForLocatorAndClick(Masthead.userDropdownButton(tcc.page(), userName)); + waitForLocatorAndClick(Masthead.logoutMenuItem(tcc.page())); Utils.sleepWait(TimeConstants.UI_COMPONENT_REACTION_INTERVAL_SHORT); - if (tcc.page().url().equals(dashboardUrl) || - tcc.page().locator(KafkaDashboardPageSelectors.KDPS_CURRENTLY_LOGGED_USER_BUTTON).allInnerTexts().contains(userName)) { + if (tcc.page().url().equals(dashboardUrl) || Masthead.userDropdownButton(tcc.page(), userName).isVisible()) { LOGGER.warn("User '{}' has not been logged out", userName); screenshot(tcc, tcc.kafkaName(), "logout-retry"); return false; diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KafkaNodePoolChecks.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KafkaNodePoolChecks.java index 3efcedcb0..4455cb163 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KafkaNodePoolChecks.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KafkaNodePoolChecks.java @@ -2,11 +2,12 @@ import com.github.streamshub.systemtests.TestCaseConfig; import com.github.streamshub.systemtests.constants.TimeConstants; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; -import com.github.streamshub.systemtests.locators.NodesPageSelectors; +import com.github.streamshub.systemtests.locators.pages.ClusterOverviewPage; +import com.github.streamshub.systemtests.locators.pages.NodesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.playwright.PwPageUrls; import com.github.streamshub.systemtests.utils.playwright.PwUtils; +import com.microsoft.playwright.Locator; import io.strimzi.api.kafka.model.nodepool.ProcessRoles; import org.apache.logging.log4j.Logger; @@ -48,14 +49,14 @@ public static void checkDefaultNodeState(TestCaseConfig tcc, List<Integer> broke public static void checkOverviewPageKafkaBrokerNodes(TestCaseConfig tcc, int brokerCount) { LOGGER.info("Checking overview page shows broker node count {}/{}", brokerCount, brokerCount); PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_BROKER_COUNT, + PwUtils.waitForContainsText(ClusterOverviewPage.brokerCount(tcc.page()), brokerCount + "/" + brokerCount, TimeConstants.ACTION_WAIT_SHORT); } public static void checkNodesPageKafkaNodes(TestCaseConfig tcc, int totalNodeCount) { LOGGER.info("Checking nodes page shows total node count {}", totalNodeCount); PwUtils.navigate(tcc, PwPageUrls.getNodesPage(tcc, tcc.kafkaName())); - PwUtils.waitForLocatorCount(tcc, totalNodeCount, NodesPageSelectors.NPS_TABLE_BODY, true); + PwUtils.waitForLocatorCount(totalNodeCount, NodesPage.table(tcc.page()).rows(), true); } /** @@ -69,7 +70,7 @@ public static void checkNodesPageKafkaNodes(TestCaseConfig tcc, int totalNodeCou */ public static void checkFilterTypeResults(TestCaseConfig tcc, List<Integer> nodeIds, String expectedRole) { LOGGER.info("Verifying kafka node table filtered results for nodeIds {} with expected role [{}]", nodeIds, expectedRole); - PwUtils.waitForLocatorCount(tcc, nodeIds.size(), NodesPageSelectors.NPS_TABLE_BODY, true); + PwUtils.waitForLocatorCount(nodeIds.size(), NodesPage.table(tcc.page()).rows(), true); for (int row = 1; row < nodeIds.size(); row++) { checkKnpTableRow(tcc, row, nodeIds.get(row - 1), expectedRole); } @@ -86,7 +87,8 @@ public static void checkFilterTypeResults(TestCaseConfig tcc, List<Integer> node */ public static void checkKnpTableRow(TestCaseConfig tcc, int nthRow, int expectedNodeId, String expectedRole) { LOGGER.debug("Checking kafka node table row {} contains nodeId {} with role [{}]", nthRow, expectedNodeId, expectedRole); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.getNodeTableRowItem(nthRow, 2), String.valueOf(expectedNodeId), false, false); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.getNodeTableRowItem(nthRow, 3), expectedRole, false, false); + Locator row = NodesPage.table(tcc.page()).rows().nth(nthRow - 1); + PwUtils.waitForContainsText(NodesPage.table(tcc.page()).cell(row, "Node ID"), String.valueOf(expectedNodeId), false, false); + PwUtils.waitForContainsText(NodesPage.table(tcc.page()).cell(row, "Roles"), expectedRole, false, false); } } diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KroxyChecks.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KroxyChecks.java index 158ab7cf6..841310369 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KroxyChecks.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/KroxyChecks.java @@ -1,7 +1,7 @@ package com.github.streamshub.systemtests.utils.testchecks; import com.github.streamshub.systemtests.TestCaseConfig; -import com.github.streamshub.systemtests.locators.CssSelectors; +import com.github.streamshub.systemtests.locators.components.Masthead; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.playwright.PwUtils; import org.apache.logging.log4j.Logger; @@ -26,7 +26,7 @@ private KroxyChecks() {} public static void checkKafkaClusterDropdownContains(TestCaseConfig tcc, List<String> expectedKafkaNames) { LOGGER.info("Checking Kafka cluster dropdown contains expected clusters: {}", expectedKafkaNames); - List<String> visibleKafkaClusters = tcc.page().locator(CssSelectors.PAGES_NAV_KAFKA_CLUSTERS_LIST_ITEMS) + List<String> visibleKafkaClusters = Masthead.clusterListItems(tcc.page()) .all() .stream() .map(locator -> PwUtils.getTrimmedText(locator.allInnerTexts().toString())) diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/MessagesChecks.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/MessagesChecks.java index e99e0375e..fab9c8818 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/MessagesChecks.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/MessagesChecks.java @@ -3,10 +3,11 @@ import com.github.streamshub.systemtests.TestCaseConfig; import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.enums.MessagesParameterType; -import com.github.streamshub.systemtests.locators.MessagesPageSelectors; +import com.github.streamshub.systemtests.locators.pages.MessagesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.playwright.PwUtils; import com.github.streamshub.systemtests.utils.testutils.MessagesTestUtils; +import com.microsoft.playwright.Locator; import org.apache.logging.log4j.Logger; import java.util.Map; @@ -40,10 +41,10 @@ public static void checkPopoverUnixFilter(TestCaseConfig tcc, String unixTimesta MessagesTestUtils.openFilterForm(tcc); MessagesTestUtils.selectMessagesParameter(tcc, MessagesParameterType.FROM_UNIX_TIMESTAMP, unixTimestamp); MessagesTestUtils.search(tcc); - PwUtils.waitForLocatorCount(tcc, expectedCount, MessagesPageSelectors.MPS_SEARCH_RESULTS_TABLE_ITEMS, true); + PwUtils.waitForLocatorCount(expectedCount, MessagesPage.table(tcc.page()).rows(), true); if (expectedContent != null) { LOGGER.debug("Verifying first search result row contains [{}]", expectedContent); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.getTableRowItem(1, 5), expectedContent, true); + PwUtils.waitForContainsText(MessagesPage.cellAt(tcc.page(), 1, 5), expectedContent, true); } } @@ -74,10 +75,10 @@ public static void checkPopoverIsoFilter(TestCaseConfig tcc, String isoDateTime, MessagesTestUtils.openFilterForm(tcc); MessagesTestUtils.selectMessagesParameter(tcc, MessagesParameterType.FROM_TIMESTAMP, isoDateTime); MessagesTestUtils.search(tcc); - PwUtils.waitForLocatorCount(tcc, expectedCount, MessagesPageSelectors.MPS_SEARCH_RESULTS_TABLE_ITEMS, true); + PwUtils.waitForLocatorCount(expectedCount, MessagesPage.table(tcc.page()).rows(), true); if (expectedContent != null) { LOGGER.debug("Verifying first search result row contains [{}]", expectedContent); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.getTableRowItem(1, 5), expectedContent, true); + PwUtils.waitForContainsText(MessagesPage.cellAt(tcc.page(), 1, 5), expectedContent, true); } } @@ -106,36 +107,36 @@ public static void checkPopoverIsoFilter(TestCaseConfig tcc, String isoDateTime, */ public static void checkQueryBarFilter(TestCaseConfig tcc, String filterPrefix, String timestampValue, int expectedCount, String expectedContent) { LOGGER.info("Checking query bar filter [{}{}] returns {} message(s)", filterPrefix, timestampValue, expectedCount); - PwUtils.waitForLocatorVisible(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT); - PwUtils.waitForLocatorAndFill(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT, filterPrefix + timestampValue); - PwUtils.waitForLocatorAndClick(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_ENTER_BUTTON); - PwUtils.waitForAttributeContainsText(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT, timestampValue, Constants.VALUE_ATTRIBUTE, true, true); - PwUtils.waitForLocatorCount(tcc, expectedCount, MessagesPageSelectors.MPS_SEARCH_RESULTS_TABLE_ITEMS, true); + PwUtils.waitForLocatorVisible(MessagesPage.searchInput(tcc.page())); + PwUtils.waitForLocatorAndFill(MessagesPage.searchInput(tcc.page()), filterPrefix + timestampValue); + PwUtils.waitForLocatorAndClick(MessagesPage.searchSubmitButton(tcc.page())); + PwUtils.waitForAttributeContainsText(MessagesPage.searchInput(tcc.page()), timestampValue, Constants.VALUE_ATTRIBUTE, true, true); + PwUtils.waitForLocatorCount(expectedCount, MessagesPage.table(tcc.page()).rows(), true); if (expectedContent != null) { LOGGER.debug("Verifying first search result row contains [{}]", expectedContent); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.getTableRowItem(1, 5), expectedContent, true); + PwUtils.waitForContainsText(MessagesPage.cellAt(tcc.page(), 1, 5), expectedContent, true); } } /** * Verifies the outcome of a popover filter-form submission: that the resulting query-bar value matches - * what's expected, that the expected number of result rows are shown, and that each given selector + * what's expected, that the expected number of result rows are shown, and that each given locator * contains its expected text. * * @param tcc Test case configuration containing Playwright context * @param expectedQueryValue The expected value of the search query-bar input after submitting the form * (e.g. {@code "messages=offset:95 retrieve=50 orderID where=key"}) * @param expectedCount Expected number of messages returned after filtering - * @param rowChecks Map of selector to expected text, each verified against the result table + * @param rowChecks Map of locator to expected text, each verified against the result table * (e.g. a specific row/column cell) */ - public static void checkFilterResults(TestCaseConfig tcc, String expectedQueryValue, int expectedCount, Map<String, String> rowChecks) { + public static void checkFilterResults(TestCaseConfig tcc, String expectedQueryValue, int expectedCount, Map<Locator, String> rowChecks) { LOGGER.info("Checking filter results: query=[{}], expecting {} row(s)", expectedQueryValue, expectedCount); - PwUtils.waitForAttributeContainsText(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT, expectedQueryValue, Constants.VALUE_ATTRIBUTE, true, true); - PwUtils.waitForLocatorCount(tcc, expectedCount, MessagesPageSelectors.MPS_SEARCH_RESULTS_TABLE_ITEMS, true); - rowChecks.forEach((selector, expectedText) -> { - LOGGER.debug("Checking selector [{}] contains expected value [{}]", selector, expectedText); - PwUtils.waitForContainsText(tcc, selector, expectedText, true); + PwUtils.waitForAttributeContainsText(MessagesPage.searchInput(tcc.page()), expectedQueryValue, Constants.VALUE_ATTRIBUTE, true, true); + PwUtils.waitForLocatorCount(expectedCount, MessagesPage.table(tcc.page()).rows(), true); + rowChecks.forEach((locator, expectedText) -> { + LOGGER.debug("Checking locator [{}] contains expected value [{}]", locator, expectedText); + PwUtils.waitForContainsText(locator, expectedText, true); }); } } diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/TopicChecks.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/TopicChecks.java index 6e64013ef..faf8b52c8 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/TopicChecks.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testchecks/TopicChecks.java @@ -6,14 +6,14 @@ import com.github.streamshub.systemtests.enums.FilterType; import com.github.streamshub.systemtests.enums.TopicStatus; import com.github.streamshub.systemtests.enums.TopicsPerPage; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; -import com.github.streamshub.systemtests.locators.CssBuilder; -import com.github.streamshub.systemtests.locators.TopicsPageSelectors; +import com.github.streamshub.systemtests.locators.pages.ClusterOverviewPage; +import com.github.streamshub.systemtests.locators.pages.TopicsPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.WaitUtils; import com.github.streamshub.systemtests.utils.playwright.PwPageUrls; import com.github.streamshub.systemtests.utils.playwright.PwUtils; import com.github.streamshub.systemtests.utils.testutils.TopicsTestUtils; +import com.microsoft.playwright.Locator; import org.apache.logging.log4j.Logger; @@ -41,12 +41,14 @@ public static void checkOverviewPageTopicState(TestCaseConfig tcc, String kafkaN PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, kafkaName)); // Status - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_TOTAL_TOPICS, total + " topics", false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_TOTAL_PARTITIONS, partitions + " partitions", false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); - - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_FULLY_REPLICATED, fullyReplicated + " Fully replicated", false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_UNDER_REPLICATED, underReplicated + " Under-replicated", false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_UNAVAILABLE, unavailable + " Unavailable", false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); + // useTopics() (backing all counts below) fetches once with no refetchInterval, so a stale count + // needs a page reload to re-fetch - a plain re-check of the same DOM would retry forever on stale data. + PwUtils.waitForContainsText(ClusterOverviewPage.totalTopics(tcc.page()), total + " topics", true, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); + PwUtils.waitForContainsText(ClusterOverviewPage.totalPartitions(tcc.page()), partitions + " partitions", true, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); + + PwUtils.waitForContainsText(ClusterOverviewPage.fullyReplicated(tcc.page()), fullyReplicated + " Fully replicated", true, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); + PwUtils.waitForContainsText(ClusterOverviewPage.underReplicated(tcc.page()), underReplicated + " Under-replicated", true, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); + PwUtils.waitForContainsText(ClusterOverviewPage.unavailable(tcc.page()), unavailable + " Unavailable", true, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); } /** @@ -66,10 +68,11 @@ public static void checkTopicsPageTopicState(TestCaseConfig tcc, String kafkaNam // Total topic count PwUtils.navigate(tcc, PwPageUrls.getTopicsPage(tcc, kafkaName)); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_HEADER_TOTAL_TOPICS_BADGE, total + " total", false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_HEADER_BADGE_STATUS_SUCCESS, Integer.toString(fullyReplicated), false, true, TimeConstants.ACTION_WAIT_SHORT, Constants.SELECTOR_RETRIES); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_HEADER_BADGE_STATUS_WARNING, Integer.toString(underReplicated), false, true, TimeConstants.ACTION_WAIT_SHORT, Constants.SELECTOR_RETRIES); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_HEADER_BADGE_STATUS_ERROR, Integer.toString(unavailable), false, true, TimeConstants.ACTION_WAIT_SHORT, Constants.SELECTOR_RETRIES); + // Same non-polling useTopics() data source as the overview page - a reload is needed to see an updated count. + PwUtils.waitForContainsText(TopicsPage.totalTopicsBadge(tcc.page()), total + " total", true, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); + PwUtils.waitForContainsText(TopicsPage.fullyReplicatedBadge(tcc.page()), Integer.toString(fullyReplicated), true, true, TimeConstants.ACTION_WAIT_SHORT, Constants.SELECTOR_RETRIES); + PwUtils.waitForContainsText(TopicsPage.underReplicatedBadge(tcc.page()), Integer.toString(underReplicated), true, true, TimeConstants.ACTION_WAIT_SHORT, Constants.SELECTOR_RETRIES); + PwUtils.waitForContainsText(TopicsPage.offlineBadge(tcc.page()), Integer.toString(unavailable), true, true, TimeConstants.ACTION_WAIT_SHORT, Constants.SELECTOR_RETRIES); } /** @@ -82,17 +85,17 @@ public static void checkTopicsPageTopicState(TestCaseConfig tcc, String kafkaNam * - Iterates forward through all pages, verifying content and pagination info * - Iterates backward through all pages, verifying content and pagination info * - * @param tcc the test case configuration with page context - * @param topicsCount the total number of topics present - * @param topicsPerPageList list of topics per page options to test - * @param dropdownButtonSelector CSS selector for the dropdown button controlling topics per page - * @param dropdownItemsSelector CSS selector for the dropdown items in the topics per page selector - * @param paginationTextSelector CSS selector for the pagination summary text (e.g., "1-10 of 57") - * @param previousButtonSelector CSS selector for the pagination "previous page" button - * @param nextButtonSelector CSS selector for the pagination "next page" button + * @param tcc the test case configuration with page context + * @param topicsCount the total number of topics present + * @param topicsPerPageList list of topics per page options to test + * @param dropdownButton the dropdown button controlling topics per page + * @param dropdownItems the dropdown items in the topics per page selector + * @param paginationText the pagination summary text (e.g., "1-10 of 57") + * @param previousButton the pagination "previous page" button + * @param nextButton the pagination "next page" button */ public static void checkPaginationPage(TestCaseConfig tcc, int topicsCount, List<TopicsPerPage> topicsPerPageList, - String dropdownButtonSelector, String dropdownItemsSelector, String paginationTextSelector, String previousButtonSelector, String nextButtonSelector) { + Locator dropdownButton, Locator dropdownItems, Locator paginationText, Locator previousButton, Locator nextButton) { for (TopicsPerPage topicsPerPageOption : topicsPerPageList) { int lowBoundary; int highBoundary; @@ -105,10 +108,10 @@ public static void checkPaginationPage(TestCaseConfig tcc, int topicsCount, List PwUtils.navigate(tcc, PwPageUrls.getTopicsPage(tcc, tcc.kafkaName())); LOGGER.debug("Opening topics-per-page dropdown selector"); - PwUtils.waitForLocatorAndClick(tcc, dropdownButtonSelector); + PwUtils.waitForLocatorAndClick(dropdownButton); LOGGER.debug("Selecting topics-per-page dropdown item [{}] for value {}", topicsPerPageOption.getDropdownPosition(), topicsPerPage); - PwUtils.waitForLocatorAndClick(tcc, new CssBuilder(dropdownItemsSelector).nth(topicsPerPageOption.getDropdownPosition()).build()); + PwUtils.waitForLocatorAndClick(dropdownItems.nth(topicsPerPageOption.getDropdownPosition() - 1)); // Check pages int pageOverflow = topicsCount % topicsPerPage; @@ -120,14 +123,14 @@ public static void checkPaginationPage(TestCaseConfig tcc, int topicsCount, List lowBoundary = (topicsPerPage * (pageNum - 1)) + 1; highBoundary = Integer.min(topicsPerPage * pageNum, topicsCount); topicsOnPage = pageNum == numOfPages ? finalPageSize : topicsPerPage; - checkPaginationContent(tcc, pageNum, numOfPages, topicsOnPage, lowBoundary, highBoundary, topicsCount, paginationTextSelector, nextButtonSelector); + checkPaginationContent(tcc, pageNum, numOfPages, topicsOnPage, lowBoundary, highBoundary, topicsCount, paginationText, nextButton); } // Backwards movement for (int pageNum = numOfPages; pageNum >= 1; pageNum--) { lowBoundary = (topicsPerPage * (pageNum - 1)) + 1; highBoundary = Integer.min(topicsPerPage * pageNum, topicsCount); topicsOnPage = pageNum == numOfPages ? finalPageSize : topicsPerPage; - checkPaginationContent(tcc, pageNum, 1, topicsOnPage, lowBoundary, highBoundary, topicsCount, paginationTextSelector, previousButtonSelector); + checkPaginationContent(tcc, pageNum, 1, topicsOnPage, lowBoundary, highBoundary, topicsCount, paginationText, previousButton); } } } @@ -143,18 +146,18 @@ public static void checkPaginationPage(TestCaseConfig tcc, int topicsCount, List * @param topicsOnPage the expected number of topics displayed on the current page * @param lowBoundary the lowest topic index shown on the current page (1-based) * @param highBoundary the highest topic index shown on the current page - * @param topicsCount the total number of topics in all pages - * @param paginationTextSelector CSS selector for the pagination summary text element - * @param moveButtonSelector CSS selector for the button used to navigate to the next/previous page + * @param topicsCount the total number of topics in all pages + * @param paginationText the pagination summary text element + * @param moveButton the button used to navigate to the next/previous page */ - private static void checkPaginationContent(TestCaseConfig tcc, int pageNum, int numOfFinalPage, int topicsOnPage, int lowBoundary, int highBoundary, int topicsCount, String paginationTextSelector, String moveButtonSelector) { + private static void checkPaginationContent(TestCaseConfig tcc, int pageNum, int numOfFinalPage, int topicsOnPage, int lowBoundary, int highBoundary, int topicsCount, Locator paginationText, Locator moveButton) { LOGGER.debug("Checking pagination page {}, expecting {} topics displayed", pageNum, topicsOnPage); // Check that correct number of topics is displayed - PwUtils.waitForLocatorCount(tcc, topicsOnPage, TopicsPageSelectors.TPS_TABLE_ROWS, false); + PwUtils.waitForLocatorCount(topicsOnPage, TopicsPage.table(tcc.page()).rows(), false); // Check pagination details String paginationOf = String.format("%s - %s of %s", lowBoundary, highBoundary, topicsCount); LOGGER.debug("Verifying pagination summary text shows [{}]", paginationOf); - PwUtils.waitForContainsText(tcc, paginationTextSelector, paginationOf, false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); + PwUtils.waitForContainsText(paginationText, paginationOf, false, true, TimeConstants.ACTION_WAIT_MEDIUM, Constants.SELECTOR_RETRIES); // Click to move to next page if (pageNum == numOfFinalPage) { @@ -162,8 +165,8 @@ private static void checkPaginationContent(TestCaseConfig tcc, int pageNum, int return; } - LOGGER.debug("Navigating to next pagination page via selector [{}]", moveButtonSelector); - PwUtils.waitForLocatorAndClick(tcc, moveButtonSelector); + LOGGER.debug("Navigating to next pagination page via locator [{}]", moveButton); + PwUtils.waitForLocatorAndClick(moveButton); } /** @@ -180,11 +183,11 @@ public static void checkTopicsFilterByName(TestCaseConfig tcc, List<String> topi TopicsTestUtils.selectFilter(tcc, FilterType.NAME); for (String topicName : topicNames) { LOGGER.debug("Verifying filtered result shows topic name [{}]", topicName); - PwUtils.waitForLocatorAndFill(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT, topicName); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.getTopicsTableRowItems(1), topicName, false); + PwUtils.waitForLocatorAndFill(TopicsPage.searchInput(tcc.page()), topicName); + PwUtils.waitForLocatorAndClick(TopicsPage.searchButton(tcc.page())); + PwUtils.waitForContainsText(TopicsPage.table(tcc.page()).row(topicName), topicName, false); } - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_SEARCH_CLEAR_ALL_FILTERS); + PwUtils.waitForLocatorAndClick(TopicsPage.clearAllFiltersButton(tcc.page())); } /** @@ -202,11 +205,11 @@ public static void checkTopicsFilterById(TestCaseConfig tcc, List<String> topicN for (String topicName : topicNames) { String topicId = WaitUtils.waitForKafkaTopicToHaveIdAndReturn(tcc.namespace(), topicName); LOGGER.debug("Verifying filtered result shows topic [{}] for id [{}]", topicName, topicId); - PwUtils.waitForLocatorAndFill(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT, topicId); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.getTopicsTableRowItems(1), topicName, false); + PwUtils.waitForLocatorAndFill(TopicsPage.searchInput(tcc.page()), topicId); + PwUtils.waitForLocatorAndClick(TopicsPage.searchButton(tcc.page())); + PwUtils.waitForContainsText(TopicsPage.table(tcc.page()).rows().first(), topicName, false); } - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_SEARCH_CLEAR_ALL_FILTERS); + PwUtils.waitForLocatorAndClick(TopicsPage.clearAllFiltersButton(tcc.page())); } /** @@ -224,13 +227,13 @@ public static void checkTopicsFilterByStatus(TestCaseConfig tcc, List<String> to TopicsTestUtils.selectFilter(tcc, FilterType.STATUS); TopicsTestUtils.selectTopicStatus(tcc, status); // Use default max results per page or actual topic count if it's less than the maximum per page - PwUtils.waitForLocatorCount(tcc, Math.min(topicNames.size(), Constants.DEFAULT_TOPICS_PER_PAGE), TopicsPageSelectors.TPS_TABLE_ROWS, false); + PwUtils.waitForLocatorCount(Math.min(topicNames.size(), Constants.DEFAULT_TOPICS_PER_PAGE), TopicsPage.table(tcc.page()).rows(), false); for (String topicName : topicNames) { LOGGER.debug("Verifying topic [{}] is present in results filtered by status [{}]", topicName, status.getName()); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_TABLE_ROWS, topicName, true); + PwUtils.waitForContainsText(TopicsPage.table(tcc.page()).rows(), topicName, true); } - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_SEARCH_CLEAR_ALL_FILTERS); + PwUtils.waitForLocatorAndClick(TopicsPage.clearAllFiltersButton(tcc.page())); } } diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/GroupsTestUtils.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/GroupsTestUtils.java index 67af88506..1752885c4 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/GroupsTestUtils.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/GroupsTestUtils.java @@ -3,15 +3,11 @@ import com.github.streamshub.systemtests.TestCaseConfig; import com.github.streamshub.systemtests.enums.ResetOffsetDateTimeType; import com.github.streamshub.systemtests.enums.ResetOffsetType; -import com.github.streamshub.systemtests.locators.GroupsPageSelectors; -import com.github.streamshub.systemtests.locators.SingleGroupPageSelectors; +import com.github.streamshub.systemtests.locators.pages.SingleGroupPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.playwright.PwUtils; -import com.microsoft.playwright.Locator; import org.apache.logging.log4j.Logger; -import java.util.List; - public class GroupsTestUtils { private static final Logger LOGGER = LogWrapper.getLogger(GroupsTestUtils.class); @@ -31,25 +27,25 @@ private GroupsTestUtils() {} */ public static void selectResetOffsetType(TestCaseConfig tcc, ResetOffsetType offsetType, ResetOffsetDateTimeType dateTimeType, String value) { LOGGER.info("Selecting reset offset type {}", offsetType); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_DROPDOWN_BUTTON); + PwUtils.waitForLocatorAndClick(SingleGroupPage.offsetTypeDropdownButton(tcc.page())); switch (offsetType) { case EARLIEST: - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_EARLIEST_OFFSET); + PwUtils.waitForLocatorAndClick(SingleGroupPage.earliestOffsetOption(tcc.page())); break; case LATEST: - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_LATEST_OFFSET); + PwUtils.waitForLocatorAndClick(SingleGroupPage.latestOffsetOption(tcc.page())); break; case DATE_TIME_ISO: - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_ALL_PARTITIONS_SPECIFIC_DATETIME_OFFSET_ISO); + PwUtils.waitForLocatorAndClick(SingleGroupPage.specificDateTimeIsoOption(tcc.page())); fillResetOffsetDatetime(tcc, dateTimeType, value); break; case DATE_TIME_UNIX: - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_ALL_PARTITIONS_SPECIFIC_DATETIME_OFFSET_UNIX); + PwUtils.waitForLocatorAndClick(SingleGroupPage.specificDateTimeUnixOption(tcc.page())); fillResetOffsetDatetime(tcc, dateTimeType, value); break; case DELETE_COMMITED_OFFSETS: - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_ALL_PARTITIONS_DELETE_COMMITED_OFFSETS); + PwUtils.waitForLocatorAndClick(SingleGroupPage.deleteCommittedOffsetsOption(tcc.page())); break; } } @@ -68,14 +64,8 @@ public static void selectResetOffsetType(TestCaseConfig tcc, ResetOffsetType off */ public static void fillResetOffsetDatetime(TestCaseConfig tcc, ResetOffsetDateTimeType dateTimeType, String value) { LOGGER.debug("Selecting reset offset datetime type {} and filling value {}", dateTimeType, value); - switch (dateTimeType) { - case UNIX_EPOCH: - PwUtils.waitForLocatorAndFill(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_SPECIFIC_DATETIME_INPUT_UNIX, value); - break; - case ISO_8601: - PwUtils.waitForLocatorAndFill(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_SPECIFIC_DATETIME_INPUT_ISO, value); - break; - } + // The ISO and Unix inputs share the same #datetime-input element - they're mutually exclusive by offsetValue state. + PwUtils.waitForLocatorAndFill(SingleGroupPage.datetimeInput(tcc.page()), value); } /** @@ -97,10 +87,10 @@ public static void execDryRun(TestCaseConfig tcc, ResetOffsetType offsetType, Re LOGGER.info("DryRun reset offset - OffsetType {} DateTime {} reset value {}", offsetType, dateTimeType, value); selectResetOffsetType(tcc, offsetType, dateTimeType, value); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_DRY_RUN_BUTTON); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_DRY_RUN_COMMAND_DROPDOWN); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_DRY_RUN_COMMAND, offsetType.getCommand(), false, false); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_CANCEL_DRY_RUN_OFFSET_BUTTON); + PwUtils.waitForLocatorAndClick(SingleGroupPage.dryRunButton(tcc.page())); + PwUtils.waitForLocatorAndClick(SingleGroupPage.dryRunCommandToggle(tcc.page())); + PwUtils.waitForContainsText(SingleGroupPage.dryRunCommandText(tcc.page()), offsetType.getCommand(), false, false); + PwUtils.waitForLocatorAndClick(SingleGroupPage.dryRunCancelButton(tcc.page())); } /** @@ -120,43 +110,6 @@ public static void execDryRun(TestCaseConfig tcc, ResetOffsetType offsetType, Re public static void execResetOffset(TestCaseConfig tcc, ResetOffsetType offsetType, ResetOffsetDateTimeType dateTimeType, String value) { LOGGER.info("Reset offset - OffsetType {} DateTime {} reset value {}", offsetType, dateTimeType, value); selectResetOffsetType(tcc, offsetType, dateTimeType, value); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_OFFSET_RESET_BUTTON); - } - - /** - * Verifies that a consumer group with the given name is present in the Groups table. - * - * @param tcc test case configuration containing the Playwright page - * @param consumerGroupName name of the consumer group expected to be visible - * - * @throws AssertionError if the consumer group is not found in the table - */ - public static void waitForGroupInTable(TestCaseConfig tcc, String consumerGroupName) { - LOGGER.info("Verifying consumer group {} is present in Groups table", consumerGroupName); - List<String> visibleGroups = tcc.page().locator(GroupsPageSelectors.GPS_TABLE_ITEMS) - .all() - .stream() - .map(locator -> PwUtils.getTrimmedText(locator.allInnerTexts().toString())) - .toList(); - - if (visibleGroups.stream().noneMatch(group -> group.contains(consumerGroupName))) { - LOGGER.error("Consumer group {} not found in Groups table, visible groups: {}", consumerGroupName, visibleGroups); - throw new AssertionError("Consumer group not found in UI, expected: " + consumerGroupName + - " - Visible groups: " + visibleGroups); - } - } - - /** - * Clicks the consumer group with the given name in the Groups table. - * - * @param tcc test case configuration containing the Playwright page - * @param consumerGroupName name of the consumer group to open - */ - public static void clickGroupInTable(TestCaseConfig tcc, String consumerGroupName) { - LOGGER.info("Opening consumer group {} from Groups table", consumerGroupName); - PwUtils.waitForLocatorAndClick(tcc.page().locator(GroupsPageSelectors.GPS_TABLE_ITEMS) - .filter(new Locator.FilterOptions().setHasText(consumerGroupName)) - .locator("a") - .first()); + PwUtils.waitForLocatorAndClick(SingleGroupPage.resetButton(tcc.page())); } } diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/KafkaTestUtils.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/KafkaTestUtils.java index 67015f302..5e78b3d2e 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/KafkaTestUtils.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/KafkaTestUtils.java @@ -1,7 +1,7 @@ package com.github.streamshub.systemtests.utils.testutils; import com.github.streamshub.systemtests.TestCaseConfig; -import com.github.streamshub.systemtests.locators.NodesPageSelectors; +import com.github.streamshub.systemtests.locators.pages.NodesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.playwright.PwUtils; import com.microsoft.playwright.Locator; @@ -24,8 +24,8 @@ private KafkaTestUtils() {} */ public static void filterKnpByRole(TestCaseConfig tcc, String roleName) { LOGGER.info("Filtering Kafka Node Pool table by role {}", roleName); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_FILTER_TYPE_ROLE_DROPDOWN_BUTTON); - List<Locator> knpItems = tcc.page().locator(NodesPageSelectors.NPS_FILTER_BY_NODEPOOL_ITEMS).all(); + PwUtils.waitForLocatorAndClick(NodesPage.roleFilterButton(tcc.page())); + List<Locator> knpItems = NodesPage.filterMenuItems(tcc.page()).all(); for (Locator knpItem : knpItems) { if (PwUtils.locatorContainsText(knpItem, roleName, false)) { PwUtils.waitForLocatorAndClick(knpItem); @@ -44,8 +44,8 @@ public static void filterKnpByRole(TestCaseConfig tcc, String roleName) { */ public static void resetKnpFilters(TestCaseConfig tcc, int defaultNodeCount) { LOGGER.info("Clearing all Kafka Node Pool filters, expecting {} nodes to be listed", defaultNodeCount); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_FILTER_CLEAR_ALL_FILTERS_BUTTON); - PwUtils.waitForLocatorCount(tcc, defaultNodeCount, NodesPageSelectors.NPS_TABLE_BODY, true); + PwUtils.waitForLocatorAndClick(NodesPage.clearAllFiltersButton(tcc.page())); + PwUtils.waitForLocatorCount(defaultNodeCount, NodesPage.table(tcc.page()).rows(), true); PwUtils.reload(tcc); } } diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/MessagesTestUtils.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/MessagesTestUtils.java index 8a360aba9..32e204dd3 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/MessagesTestUtils.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/MessagesTestUtils.java @@ -5,14 +5,13 @@ import com.github.streamshub.systemtests.enums.MessagesRetrieveType; import com.github.streamshub.systemtests.enums.MessagesRetrieveLimit; import com.github.streamshub.systemtests.enums.MessagesWhereFilter; -import com.github.streamshub.systemtests.locators.MessagesPageSelectors; +import com.github.streamshub.systemtests.locators.pages.MessagesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.playwright.PwUtils; import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; import org.apache.logging.log4j.Logger; -import java.util.regex.Pattern; - public class MessagesTestUtils { private static final Logger LOGGER = LogWrapper.getLogger(MessagesTestUtils.class); private static final String ALL_PARTITIONS_LABEL = "All partitions"; @@ -26,8 +25,8 @@ private MessagesTestUtils() {} */ public static void openFilterForm(TestCaseConfig tcc) { LOGGER.info("Opening Messages popover filter form"); - PwUtils.waitForLocatorAndClick(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_OPEN_POPOVER_FORM_BUTTON); - PwUtils.waitForLocatorVisible(tcc, MessagesPageSelectors.MPS_TPF_HAS_WORDS_INPUT); + PwUtils.waitForLocatorAndClick(MessagesPage.advancedSearchToggle(tcc.page())); + PwUtils.waitForLocatorVisible(MessagesPage.hasWordsInput(tcc.page())); } /** @@ -38,7 +37,7 @@ public static void openFilterForm(TestCaseConfig tcc) { */ public static void fillHasWords(TestCaseConfig tcc, String text) { LOGGER.debug("Filling 'Has the words' input with [{}]", text); - fillAndVerify(tcc, MessagesPageSelectors.MPS_TPF_HAS_WORDS_INPUT, text); + fillAndVerify(MessagesPage.hasWordsInput(tcc.page()), text); } /** @@ -49,7 +48,7 @@ public static void fillHasWords(TestCaseConfig tcc, String text) { */ public static void selectWhere(TestCaseConfig tcc, MessagesWhereFilter where) { LOGGER.info("Selecting 'Where' filter [{}]", where.getLabel()); - selectMenuItem(tcc, MessagesPageSelectors.MPS_TPF_WHERE_DROPDOWN_BUTTON, where.getLabel()); + selectMenuItem(tcc.page(), MessagesPage.whereDropdownButton(tcc.page()), where.getLabel()); } /** @@ -64,13 +63,13 @@ public static void selectWhere(TestCaseConfig tcc, MessagesWhereFilter where) { */ public static void selectMessagesParameter(TestCaseConfig tcc, MessagesParameterType type, String value) { LOGGER.info("Selecting Messages parameter [{}] with value [{}]", type.getLabel(), value); - selectMenuItem(tcc, MessagesPageSelectors.MPS_TPF_PARAMETERS_MESSAGES_DROPDOWN_BUTTON, type.getLabel()); + selectMenuItem(tcc.page(), MessagesPage.messagesFromDropdownButton(tcc.page()), type.getLabel()); switch (type) { case FROM_OFFSET, FROM_UNIX_TIMESTAMP -> - fillAndVerify(tcc, MessagesPageSelectors.MPS_TPF_PARAMETERS_MESSAGES_OFFSET_INPUT, value); + fillAndVerify(MessagesPage.offsetInput(tcc.page()), value); case FROM_TIMESTAMP -> - fillAndVerify(tcc, MessagesPageSelectors.MPS_TPF_PARAMETERS_MESSAGES_TIMESTAMP_INPUT, value); + fillAndVerify(MessagesPage.timestampInput(tcc.page()), value); case LATEST -> LOGGER.debug("Messages parameter [Latest messages] has no secondary input to fill"); } } @@ -86,10 +85,10 @@ public static void selectMessagesParameter(TestCaseConfig tcc, MessagesParameter */ public static void selectRetrieveType(TestCaseConfig tcc, MessagesRetrieveType type, MessagesRetrieveLimit limit) { LOGGER.info("Selecting Retrieve type [{}]{}", type.getLabel(), limit != null ? " with limit [" + limit.getLabel() + "]" : ""); - selectMenuItem(tcc, MessagesPageSelectors.MPS_TPF_RETRIEVE_DROPDOWN_BUTTON, type.getLabel()); + selectMenuItem(tcc.page(), MessagesPage.retrieveTypeDropdownButton(tcc.page()), type.getLabel()); if (type == MessagesRetrieveType.NUMBER_OF_MESSAGES && limit != null) { - selectMenuItem(tcc, MessagesPageSelectors.MPS_TPF_RETRIEVE_LIMIT_DROPDOWN_BUTTON, limit.getLabel()); + selectMenuItem(tcc.page(), MessagesPage.retrieveLimitDropdownButton(tcc.page()), limit.getLabel()); } } @@ -102,7 +101,7 @@ public static void selectRetrieveType(TestCaseConfig tcc, MessagesRetrieveType t public static void selectPartition(TestCaseConfig tcc, Integer partitionIndex) { String label = partitionIndex == null ? ALL_PARTITIONS_LABEL : String.valueOf(partitionIndex); LOGGER.info("Selecting 'In partition' filter [{}]", label); - selectMenuItem(tcc, MessagesPageSelectors.MPS_TPF_PARTITION_DROPDOWN_BUTTON, label); + selectMenuItem(tcc.page(), MessagesPage.partitionDropdownButton(tcc.page()), label); } /** @@ -112,7 +111,7 @@ public static void selectPartition(TestCaseConfig tcc, Integer partitionIndex) { */ public static void search(TestCaseConfig tcc) { LOGGER.debug("Submitting Messages filter form"); - PwUtils.waitForLocatorAndClick(tcc, MessagesPageSelectors.MPS_TPF_SEARCH_BUTTON); + PwUtils.waitForLocatorAndClick(MessagesPage.formSearchButton(tcc.page())); } /** @@ -122,65 +121,45 @@ public static void search(TestCaseConfig tcc) { */ public static void resetFilters(TestCaseConfig tcc) { LOGGER.debug("Resetting Messages filter form"); - PwUtils.waitForLocatorAndClick(tcc, MessagesPageSelectors.MPS_TPF_RESET_BUTTON); + PwUtils.waitForLocatorAndClick(MessagesPage.formResetButton(tcc.page())); } /** * Opens a dropdown toggle, clicks the item matching the given label exactly (avoiding substring collisions * such as "5"/"50" or "1"/"10"), and verifies the toggle now displays that label. * - * @param tcc test case configuration containing the Playwright page - * @param toggleSelector selector for the dropdown's toggle button - * @param exactLabel the exact, case-sensitive label of the item to select + * @param page the Playwright page used to resolve the shared open-menu-item locator + * @param toggle the dropdown's toggle button + * @param exactLabel the exact, case-sensitive label of the item to select * * @throws AssertionError if the toggle does not display the expected label after selection */ - private static void selectMenuItem(TestCaseConfig tcc, String toggleSelector, String exactLabel) { - PwUtils.waitForLocatorAndClick(tcc, toggleSelector); - - // Playwright serializes this Pattern's source to a JavaScript RegExp on the browser side, so it must be - // escaped in a JS-regex-safe way - Java's Pattern.quote() wraps in \Q...\E, which JS regex doesn't - // understand, silently producing a pattern that can never match anything. - Pattern exactMatch = Pattern.compile("^" + escapeForJsRegex(exactLabel) + "$"); - Locator item = tcc.page().locator(MessagesPageSelectors.MPS_TPF_OPEN_MENU_ITEM_BUTTONS) - .filter(new Locator.FilterOptions().setHasText(exactMatch)); - PwUtils.waitForLocatorAndClick(item); - - String toggleText = PwUtils.getTrimmedText(tcc.page().locator(toggleSelector).innerText()); + private static void selectMenuItem(Page page, Locator toggle, String exactLabel) { + PwUtils.waitForLocatorAndClick(toggle); + PwUtils.waitForLocatorAndClick(MessagesPage.openMenuItem(page, exactLabel)); + + String toggleText = PwUtils.getTrimmedText(toggle.innerText()); if (!toggleText.contains(exactLabel)) { - LOGGER.error("Dropdown [{}] shows [{}], expected [{}]", toggleSelector, toggleText, exactLabel); - throw new AssertionError("Dropdown [" + toggleSelector + "] expected to show [" + exactLabel + "] but showed [" + toggleText + "]"); + LOGGER.error("Dropdown shows [{}], expected [{}]", toggleText, exactLabel); + throw new AssertionError("Dropdown expected to show [" + exactLabel + "] but showed [" + toggleText + "]"); } } - /** - * Escapes regex metacharacters using backslash-escaping valid in both Java and JavaScript regex syntax - * (unlike {@link Pattern#quote(String)}, which uses Java-only {@code \Q...\E} quoting that JavaScript's - * RegExp does not understand - Playwright sends the pattern's source to the browser as a JS RegExp). - * - * @param text the literal text to escape - * @return the escaped text, safe to embed in a regex matched by Playwright's browser-side engine - */ - private static String escapeForJsRegex(String text) { - return text.replaceAll("[.*+?^${}()|\\[\\]\\\\]", "\\\\$0"); - } - /** * Fills an input and verifies the resulting value matches exactly. * - * @param tcc test case configuration containing the Playwright page - * @param selector selector for the input to fill - * @param value the value to fill and verify + * @param input the input to fill + * @param value the value to fill and verify * * @throws AssertionError if the input's value doesn't match after filling */ - private static void fillAndVerify(TestCaseConfig tcc, String selector, String value) { - PwUtils.waitForLocatorAndFill(tcc, selector, value); + private static void fillAndVerify(Locator input, String value) { + PwUtils.waitForLocatorAndFill(input, value); - String actual = tcc.page().locator(selector).inputValue(); + String actual = input.inputValue(); if (!value.equals(actual)) { - LOGGER.error("Input [{}] has value [{}], expected [{}]", selector, actual, value); - throw new AssertionError("Input [" + selector + "] expected value [" + value + "] but was [" + actual + "]"); + LOGGER.error("Input has value [{}], expected [{}]", actual, value); + throw new AssertionError("Input expected value [" + value + "] but was [" + actual + "]"); } } } diff --git a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/TopicsTestUtils.java b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/TopicsTestUtils.java index 1f09a4312..6a6022fe7 100644 --- a/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/TopicsTestUtils.java +++ b/systemtests/src/main/java/com/github/streamshub/systemtests/utils/testutils/TopicsTestUtils.java @@ -4,12 +4,12 @@ import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.enums.FilterType; import com.github.streamshub.systemtests.enums.TopicStatus; -import com.github.streamshub.systemtests.locators.CssBuilder; -import com.github.streamshub.systemtests.locators.TopicsPageSelectors; +import com.github.streamshub.systemtests.locators.pages.TopicsPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.utils.Utils; import com.github.streamshub.systemtests.utils.playwright.PwUtils; import com.microsoft.playwright.Locator; +import com.microsoft.playwright.options.AriaRole; import org.apache.logging.log4j.Logger; public class TopicsTestUtils { @@ -18,30 +18,27 @@ private TopicsTestUtils() {} /** * Attempts to select a sorting option on the page by repeatedly checking and clicking until the - * element identified by {@code selectorWithAttribute} has the specified {@code aria-sort} attribute value. + * given column header has the specified {@code aria-sort} attribute value. * * The method tries up to {@link Constants#SELECTOR_RETRIES} times, waiting a short interval between attempts. - * It clicks the element identified by {@code selectorSortButton} to change the sort order if the - * current attribute does not match the desired {@code attributeVal}. + * It clicks the sort button within {@code header} to change the sort order if the + * current attribute does not match the desired {@code expectedAttr}. * - * @param tcc the test case configuration containing the page context - * @param selectorWithAttribute CSS selector to locate the element whose 'aria-sort' attribute is checked - * @param selectorSortButton CSS selector to locate the button that triggers sorting when clicked - * @param expectedAttr the desired value of the 'aria-sort' attribute to confirm selection + * @param tcc the test case configuration containing the page context + * @param header the column header {@link Locator} whose {@code aria-sort} attribute is checked + * @param expectedAttr the desired value of the 'aria-sort' attribute to confirm selection */ - public static void selectSortBy(TestCaseConfig tcc, String selectorWithAttribute, String selectorSortButton, String expectedAttr) { - LOGGER.info("Sorting topics table using button [{}] until aria-sort={}", selectorSortButton, expectedAttr); + public static void selectSortBy(TestCaseConfig tcc, Locator header, String expectedAttr) { + LOGGER.info("Sorting topics table using header [{}] until aria-sort={}", header, expectedAttr); Utils.retryAction("Ensure topics table is sorted correctly", () -> { - Locator locator = tcc.page().locator(selectorWithAttribute); - - if (locator == null || locator.isHidden()) { + if (header == null || header.isHidden()) { PwUtils.screenshot(tcc, tcc.kafkaName(), "topicStatusFilterInvisible"); throw new IllegalStateException("Locator was not visible"); } - String currentAttr = locator.getAttribute("aria-sort"); + String currentAttr = header.getAttribute("aria-sort"); if (!expectedAttr.equals(currentAttr)) { - PwUtils.waitForLocatorAndClick(tcc, selectorSortButton); + PwUtils.waitForLocatorAndClick(header.getByRole(AriaRole.BUTTON)); LOGGER.warn("Locator had incorrect aria-sort={}, expected={}, clicking sort button again", currentAttr, expectedAttr); return false; } @@ -56,25 +53,18 @@ public static void selectSortBy(TestCaseConfig tcc, String selectorWithAttribute /** * Selects a topic filter type from the filter dropdown in the UI. * - * This method repeatedly attempts (up to {@link Constants#SELECTOR_RETRIES} times) to select the filter type - * specified by {@code filterType}. It first checks if the desired filter is already selected by inspecting - * the current dropdown text. If not selected, it opens the dropdown, waits for the desired filter option to be visible, - * and clicks it. + * This method first checks if the desired filter is already selected by inspecting + * the current dropdown text. If not selected, it opens the dropdown, waits for the desired filter option + * to be visible, and clicks it. * * @param tcc the test case configuration containing the page and context * @param filterType the {@link FilterType} to select from the filter dropdown */ public static void selectFilter(TestCaseConfig tcc, FilterType filterType) { LOGGER.info("Selecting topic filter type [{}]", filterType.getName()); - PwUtils.waitForLocatorVisible(tcc, TopicsPageSelectors.TPS_TABLE_HEADER_SORT_BY_NAME); - String filterTypeSelector = new CssBuilder(TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_TYPE_DROPDOWN).build(); - // if status is selected and both are present - if (tcc.page().locator(filterTypeSelector).count() > 1) { - LOGGER.debug("Topic filter type status is already present = two identical css buttons, select the first button"); - filterTypeSelector = new CssBuilder(TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_TYPE_DROPDOWN).nth(1).build(); - } + PwUtils.waitForLocatorVisible(TopicsPage.sortByNameHeader(tcc.page())); - String currentText = tcc.page().locator(filterTypeSelector).innerText(); + String currentText = TopicsPage.filterTypeDropdownButton(tcc.page()).innerText(); LOGGER.debug("Filter type contains [{}]", currentText); // Already correct @@ -83,33 +73,21 @@ public static void selectFilter(TestCaseConfig tcc, FilterType filterType) { return; } - PwUtils.waitForLocatorAndClick(tcc, filterTypeSelector); - String dropdownItem = new CssBuilder(TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_TYPE_DROPDOWN_ITEMS) - .nth(filterType.getPosition()) - .build(); - PwUtils.waitForLocatorAndClick(tcc, dropdownItem); + PwUtils.waitForLocatorAndClick(TopicsPage.filterTypeDropdownButton(tcc.page())); + PwUtils.waitForLocatorAndClick(TopicsPage.filterTypeDropdownItems(tcc.page()).nth(filterType.getPosition() - 1)); } /** * Selects a topic status filter from the status dropdown in the UI. * - * This method tries up to {@link Constants#SELECTOR_RETRIES} times to ensure the given - * {@code topicStatus} is selected. It first checks the currently applied status filters, - * and if the desired status is not selected, it opens the status dropdown, - * waits for the corresponding item to be visible, and clicks it. - * * @param tcc the test case configuration with page and context information * @param topicStatus the {@link TopicStatus} enum value representing the desired status to filter by */ public static void selectTopicStatus(TestCaseConfig tcc, TopicStatus topicStatus) { LOGGER.info("Selecting topic status filter [{}]", topicStatus.getName()); - // Try selecting it - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_BY_STATUS_DROPDOWN); - String dropdownItemInput = new CssBuilder(TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_BY_STATUS_DROPDOWN_ITEMS) - .nth(topicStatus.getPosition()).build(); - PwUtils.waitForLocatorAndClick(tcc, dropdownItemInput); - + PwUtils.waitForLocatorAndClick(TopicsPage.statusFilterDropdownButton(tcc.page())); + PwUtils.waitForLocatorAndClick(TopicsPage.statusFilterDropdownItems(tcc.page()).nth(topicStatus.getPosition() - 1)); } } diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/auth/AuthST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/auth/AuthST.java index 096d68e43..4c37d8505 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/auth/AuthST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/auth/AuthST.java @@ -8,12 +8,12 @@ import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.constants.TestTags; import com.github.streamshub.systemtests.enums.FilterType; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; -import com.github.streamshub.systemtests.locators.CssSelectors; -import com.github.streamshub.systemtests.locators.GroupsPageSelectors; -import com.github.streamshub.systemtests.locators.KafkaDashboardPageSelectors; -import com.github.streamshub.systemtests.locators.NodesPageSelectors; -import com.github.streamshub.systemtests.locators.TopicsPageSelectors; +import com.github.streamshub.systemtests.locators.components.Masthead; +import com.github.streamshub.systemtests.locators.pages.ClusterOverviewPage; +import com.github.streamshub.systemtests.locators.pages.GroupsPage; +import com.github.streamshub.systemtests.locators.pages.KafkaDashboardPage; +import com.github.streamshub.systemtests.locators.pages.NodesPage; +import com.github.streamshub.systemtests.locators.pages.TopicsPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.keycloak.KeycloakInstanceSetup; @@ -84,19 +84,19 @@ void testAccessDevUser() { PwUtils.navigate(tcc, ConsoleUtils.getConsoleUiUrl(tcc.consoleInstanceName(), true)); LOGGER.info("Verify navbar displays logged-in user '{}'", AuthTestConstants.USER_DEV_BOB); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_DEV_BOB, true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_DEV_BOB), AuthTestConstants.USER_DEV_BOB, true); LOGGER.info("Verify dashboard lists a single Kafka cluster '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, AuthTestConstants.TEAM_DEV_KAFKA_NAME, true); - PwUtils.waitForLocatorCount(tcc, 1, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, true); + PwUtils.waitForContainsText(KafkaDashboardPage.table(tcc.page()).rows(), AuthTestConstants.TEAM_DEV_KAFKA_NAME, true); + PwUtils.waitForLocatorCount(1, KafkaDashboardPage.table(tcc.page()).rows(), true); LOGGER.info("Open Kafka cluster detail view from the dashboard list"); - PwUtils.waitForLocatorAndClick(tcc, KafkaDashboardPageSelectors.getViewButton(1)); + PwUtils.waitForLocatorAndClick(KafkaDashboardPage.viewButton(tcc.page(), AuthTestConstants.TEAM_DEV_KAFKA_NAME)); LOGGER.info("Verify navbar still shows user '{}' after opening the Kafka cluster view", AuthTestConstants.USER_DEV_BOB); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_DEV_BOB, true); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "1", true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_DEV_BOB), AuthTestConstants.USER_DEV_BOB, true); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "1", true); LOGGER.info("Verify topic replication state on overview and topics pages for Kafka cluster '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME); TopicChecks.checkOverviewPageTopicState(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, 0, 0); @@ -107,10 +107,10 @@ void testAccessDevUser() { TopicsTestUtils.selectFilter(tcc, FilterType.NAME); LOGGER.debug("Verify topic name containing {} cannot be retrieved", AuthTestConstants.TEAM_ADMIN_KAFKA_NAME); - PwUtils.waitForLocatorAndFill(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT, AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON); - PwUtils.waitForLocatorCount(tcc, 1, TopicsPageSelectors.TPS_TABLE_ROWS, false); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_NO_RESULTS_FOUND, "No results found", false); + PwUtils.waitForLocatorAndFill(TopicsPage.searchInput(tcc.page()), AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); + PwUtils.waitForLocatorAndClick(TopicsPage.searchButton(tcc.page())); + PwUtils.waitForLocatorCount(1, TopicsPage.table(tcc.page()).rows(), false); + PwUtils.waitForContainsText(TopicsPage.noResultsFound(tcc.page()), "No results found", false); // TODO: enable once fixed // // Logout and check user is no longer logged in @@ -150,18 +150,18 @@ void testAccessAdminUser() { PwUtils.navigate(tcc, ConsoleUtils.getConsoleUiUrl(tcc.consoleInstanceName(), true)); LOGGER.info("Verify navbar displays logged-in user '{}'", AuthTestConstants.USER_ADMIN_ALICE); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_ADMIN_ALICE, true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_ADMIN_ALICE), AuthTestConstants.USER_ADMIN_ALICE, true); LOGGER.info("Verify dashboard lists both Kafka clusters '{}' and '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME, AuthTestConstants.TEAM_ADMIN_KAFKA_NAME); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, AuthTestConstants.TEAM_ADMIN_KAFKA_NAME, true); - PwUtils.waitForLocatorCount(tcc, 2, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, true); + PwUtils.waitForContainsText(KafkaDashboardPage.table(tcc.page()).rows(), AuthTestConstants.TEAM_ADMIN_KAFKA_NAME, true); + PwUtils.waitForLocatorCount(2, KafkaDashboardPage.table(tcc.page()).rows(), true); LOGGER.info("Navigate to Dev Kafka cluster '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME); PwUtils.navigate(tcc, PwPageUrls.getKafkaBaseUrl(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); LOGGER.info("Verify navbar shows user '{}' and total Kafka count on Dev cluster page", AuthTestConstants.USER_ADMIN_ALICE); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_ADMIN_ALICE, true); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "2", true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_ADMIN_ALICE), AuthTestConstants.USER_ADMIN_ALICE, true); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "2", true); LOGGER.info("Verify topic replication state on overview and topics pages for Dev Kafka cluster '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME); TopicChecks.checkOverviewPageTopicState(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, 0, 0); @@ -172,17 +172,17 @@ void testAccessAdminUser() { TopicsTestUtils.selectFilter(tcc, FilterType.NAME); LOGGER.debug("Verify topic name containing {} cannot be retrieved", AuthTestConstants.TEAM_ADMIN_KAFKA_NAME); - PwUtils.waitForLocatorAndFill(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT, AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON); - PwUtils.waitForLocatorCount(tcc, 1, TopicsPageSelectors.TPS_TABLE_ROWS, false); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_NO_RESULTS_FOUND, "No results found", false); + PwUtils.waitForLocatorAndFill(TopicsPage.searchInput(tcc.page()), AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); + PwUtils.waitForLocatorAndClick(TopicsPage.searchButton(tcc.page())); + PwUtils.waitForLocatorCount(1, TopicsPage.table(tcc.page()).rows(), false); + PwUtils.waitForContainsText(TopicsPage.noResultsFound(tcc.page()), "No results found", false); LOGGER.info("Navigate to Admin Kafka cluster '{}'", AuthTestConstants.TEAM_ADMIN_KAFKA_NAME); PwUtils.navigate(tcc, PwPageUrls.getKafkaBaseUrl(tcc, AuthTestConstants.TEAM_ADMIN_KAFKA_NAME)); LOGGER.info("Verify navbar shows user '{}' and total Kafka count on Admin cluster page", AuthTestConstants.USER_ADMIN_ALICE); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_ADMIN_ALICE, true); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "2", true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_ADMIN_ALICE), AuthTestConstants.USER_ADMIN_ALICE, true); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "2", true); LOGGER.info("Verify topic replication state on overview and topics pages for Admin Kafka cluster '{}'", AuthTestConstants.TEAM_ADMIN_KAFKA_NAME); TopicChecks.checkOverviewPageTopicState(tcc, AuthTestConstants.TEAM_ADMIN_KAFKA_NAME, AuthTestConstants.ADMIN_REPLICATED_TOPICS_COUNT, AuthTestConstants.ADMIN_REPLICATED_TOPICS_COUNT, AuthTestConstants.ADMIN_REPLICATED_TOPICS_COUNT, 0, 0); @@ -193,16 +193,16 @@ void testAccessAdminUser() { TopicsTestUtils.selectFilter(tcc, FilterType.NAME); LOGGER.debug("Verify topic name containing {} cannot be retrieved from Admin Kafka", AuthTestConstants.TEAM_DEV_TOPIC_PREFIX); - PwUtils.waitForLocatorAndFill(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT, AuthTestConstants.TEAM_DEV_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON); - PwUtils.waitForLocatorCount(tcc, 1, TopicsPageSelectors.TPS_TABLE_ROWS, false); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.TPS_NO_RESULTS_FOUND, "No results found", false); + PwUtils.waitForLocatorAndFill(TopicsPage.searchInput(tcc.page()), AuthTestConstants.TEAM_DEV_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); + PwUtils.waitForLocatorAndClick(TopicsPage.searchButton(tcc.page())); + PwUtils.waitForLocatorCount(1, TopicsPage.table(tcc.page()).rows(), false); + PwUtils.waitForContainsText(TopicsPage.noResultsFound(tcc.page()), "No results found", false); LOGGER.info("Verify Admin Kafka cluster '{}' topics matching prefix '{}' can be found via search", AuthTestConstants.TEAM_ADMIN_KAFKA_NAME, AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX); LOGGER.debug("Verify topic name containing {} can be retrieved", AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX); - PwUtils.waitForLocatorAndFill(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT, AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON); - PwUtils.waitForLocatorCount(tcc, AuthTestConstants.ADMIN_REPLICATED_TOPICS_COUNT, TopicsPageSelectors.TPS_TABLE_ROWS, false); + PwUtils.waitForLocatorAndFill(TopicsPage.searchInput(tcc.page()), AuthTestConstants.TEAM_ADMIN_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); + PwUtils.waitForLocatorAndClick(TopicsPage.searchButton(tcc.page())); + PwUtils.waitForLocatorCount(AuthTestConstants.ADMIN_REPLICATED_TOPICS_COUNT, TopicsPage.table(tcc.page()).rows(), false); // TODO: enable once fixed // // Logout and check user is no longer logged in @@ -246,18 +246,18 @@ void testAccessTopicsViewUser() { PwUtils.navigate(tcc, PwPageUrls.getConsoleUrl(tcc)); LOGGER.info("Verify navbar displays logged-in user '{}'", AuthTestConstants.USER_TOPICONLY_FRANK); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_TOPICONLY_FRANK, true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_TOPICONLY_FRANK), AuthTestConstants.USER_TOPICONLY_FRANK, true); LOGGER.info("Verify dashboard lists only the authorized Kafka cluster '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, AuthTestConstants.TEAM_DEV_KAFKA_NAME, true); - PwUtils.waitForLocatorCount(tcc, 1, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, true); + PwUtils.waitForContainsText(KafkaDashboardPage.table(tcc.page()).rows(), AuthTestConstants.TEAM_DEV_KAFKA_NAME, true); + PwUtils.waitForLocatorCount(1, KafkaDashboardPage.table(tcc.page()).rows(), true); LOGGER.info("Verify Dev Kafka cluster '{}' is accessible", AuthTestConstants.TEAM_DEV_KAFKA_NAME); PwUtils.navigate(tcc, PwPageUrls.getKafkaBaseUrl(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); LOGGER.info("Verify navbar shows user '{}' and total Kafka count on Dev cluster page", AuthTestConstants.USER_TOPICONLY_FRANK); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_TOPICONLY_FRANK, true); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "1", true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_TOPICONLY_FRANK), AuthTestConstants.USER_TOPICONLY_FRANK, true); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "1", true); LOGGER.info("Verify topic replication state on overview and topics pages for Kafka cluster '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME); TopicChecks.checkOverviewPageTopicState(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, 0, 0); @@ -266,18 +266,18 @@ void testAccessTopicsViewUser() { LOGGER.info("Verify topics page filtering by name prefix '{}' returns {} replicated topics", AuthTestConstants.TEAM_DEV_TOPIC_PREFIX, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT); PwUtils.navigate(tcc, PwPageUrls.getTopicsPage(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); TopicsTestUtils.selectFilter(tcc, FilterType.NAME); - PwUtils.waitForLocatorAndFill(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_INPUT, AuthTestConstants.TEAM_DEV_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_FILTER_SEARCH_BUTTON); - PwUtils.waitForLocatorCount(tcc, AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, TopicsPageSelectors.TPS_TABLE_ROWS, false); + PwUtils.waitForLocatorAndFill(TopicsPage.searchInput(tcc.page()), AuthTestConstants.TEAM_DEV_TOPIC_PREFIX + Constants.REPLICATED_TOPICS_PREFIX); + PwUtils.waitForLocatorAndClick(TopicsPage.searchButton(tcc.page())); + PwUtils.waitForLocatorCount(AuthTestConstants.DEV_REPLICATED_TOPICS_COUNT, TopicsPage.table(tcc.page()).rows(), false); LOGGER.info("Verify Nodes page returns 403 for topics-only user '{}'", AuthTestConstants.USER_TOPICONLY_FRANK); PwUtils.navigate(tcc, PwPageUrls.getNodesPage(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.PAGES_NOT_AUTHORIZED_CONTENT, "403", true); + PwUtils.waitForContainsText(NodesPage.notAuthorizedContent(tcc.page()), "403", true); - // TODO: enable once fixed + // TODO: enable once fixed - also needs a "not authorized" locator added to GroupsPage // LOGGER.info("Verify consumer groups page is unavailable"); // PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME, "")); - // PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_NOT_AUTHORIZED_CONTENT, "403 Forbidden", true); + // PwUtils.waitForContainsText(tcc, "403 Forbidden", true); // // Logout and check user is no longer logged in //PwUtils.logoutUser(tcc, AuthTestConstants.USER_TOPICONLY_FRANK, true); @@ -318,37 +318,37 @@ void testAccessGroupsViewUser() { PwUtils.navigate(tcc, ConsoleUtils.getConsoleUiUrl(tcc.consoleInstanceName(), true)); LOGGER.info("Verify navbar displays logged-in user '{}'", AuthTestConstants.USER_CONSUMERONLY_GRACE); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_CURRENTLY_LOGGED_USER_BUTTON, AuthTestConstants.USER_CONSUMERONLY_GRACE, true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_CONSUMERONLY_GRACE), AuthTestConstants.USER_CONSUMERONLY_GRACE, true); LOGGER.info("Verify dashboard lists only the authorized Kafka cluster '{}'", AuthTestConstants.TEAM_DEV_KAFKA_NAME); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, AuthTestConstants.TEAM_DEV_KAFKA_NAME, true); - PwUtils.waitForLocatorCount(tcc, 1, KafkaDashboardPageSelectors.KDPS_KAFKA_CLUSTER_LIST_ITEMS, true); + PwUtils.waitForContainsText(KafkaDashboardPage.table(tcc.page()).rows(), AuthTestConstants.TEAM_DEV_KAFKA_NAME, true); + PwUtils.waitForLocatorCount(1, KafkaDashboardPage.table(tcc.page()).rows(), true); LOGGER.info("Verify Dev Kafka cluster '{}' is accessible", AuthTestConstants.TEAM_DEV_KAFKA_NAME); PwUtils.navigate(tcc, PwPageUrls.getKafkaBaseUrl(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); LOGGER.info("Verify navbar shows user '{}' and total Kafka count on Dev cluster page", AuthTestConstants.USER_CONSUMERONLY_GRACE); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "1", true); - assertTrue(tcc.page().locator(CssSelectors.PAGES_CURRENTLY_LOGGED_USER_BUTTON).allInnerTexts().toString().contains(AuthTestConstants.USER_CONSUMERONLY_GRACE)); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "1", true); + assertTrue(Masthead.userDropdownButton(tcc.page(), AuthTestConstants.USER_CONSUMERONLY_GRACE).allInnerTexts().toString().contains(AuthTestConstants.USER_CONSUMERONLY_GRACE)); LOGGER.info("Verify overview page topic metrics show 0 since topic details are not authorized"); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_FULLY_REPLICATED, "0", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_UNDER_REPLICATED, "0", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_UNAVAILABLE, "0", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_TOTAL_TOPICS, "0", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPICS_CARD_TOTAL_PARTITIONS, "0", true); + PwUtils.waitForContainsText(ClusterOverviewPage.fullyReplicated(tcc.page()), "0", true); + PwUtils.waitForContainsText(ClusterOverviewPage.underReplicated(tcc.page()), "0", true); + PwUtils.waitForContainsText(ClusterOverviewPage.unavailable(tcc.page()), "0", true); + PwUtils.waitForContainsText(ClusterOverviewPage.totalTopics(tcc.page()), "0", true); + PwUtils.waitForContainsText(ClusterOverviewPage.totalPartitions(tcc.page()), "0", true); LOGGER.info("Verify Topics page shows 'Not Authorized' for user '{}'", AuthTestConstants.USER_CONSUMERONLY_GRACE); PwUtils.navigate(tcc, PwPageUrls.getTopicsPage(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.PAGES_NOT_AUTHORIZED_CONTENT, "Not Authorized", true); + PwUtils.waitForContainsText(TopicsPage.notAuthorizedContent(tcc.page()), "Not Authorized", true); LOGGER.info("Verify Nodes page returns 403 Forbidden for user '{}'", AuthTestConstants.USER_CONSUMERONLY_GRACE); PwUtils.navigate(tcc, PwPageUrls.getNodesPage(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.PAGES_NOT_AUTHORIZED_CONTENT, "403", true); + PwUtils.waitForContainsText(NodesPage.notAuthorizedContent(tcc.page()), "403", true); LOGGER.info("Verify Groups page is accessible and initially shows no groups for user '{}'", AuthTestConstants.USER_CONSUMERONLY_GRACE); PwUtils.navigate(tcc, PwPageUrls.getGroupsPage(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); - PwUtils.waitForContainsText(tcc, GroupsPageSelectors.GPS_NO_GROUPS_AVAILABLE, "No groups available", true); + PwUtils.waitForContainsText(GroupsPage.noGroupsAvailable(tcc.page()), "No groups available", true); String newTopicName = AuthTestConstants.TEAM_DEV_TOPIC_PREFIX + "continuous-msg"; LOGGER.info("Create topic '{}' with producer and consumer clients to generate a new consumer group", newTopicName); @@ -369,7 +369,7 @@ void testAccessGroupsViewUser() { LOGGER.info("Verify consumer group '{}' appears on the Groups page", KafkaNamingUtils.consumerGroupName(newTopicName)); PwUtils.navigate(tcc, PwPageUrls.getGroupsPage(tcc, AuthTestConstants.TEAM_DEV_KAFKA_NAME)); - PwUtils.waitForContainsText(tcc, GroupsPageSelectors.GPS_TABLE, KafkaNamingUtils.consumerGroupName(newTopicName), true); + PwUtils.waitForContainsText(GroupsPage.table(tcc.page()).rows(), KafkaNamingUtils.consumerGroupName(newTopicName), true); LOGGER.info("Wait for producer and consumer clients on topic '{}' to complete successfully", newTopicName); WaitUtils.waitForClientsSuccess(clients); diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/auth/KafkaCredentialsST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/auth/KafkaCredentialsST.java index 1118d763b..66e90a5ff 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/auth/KafkaCredentialsST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/auth/KafkaCredentialsST.java @@ -10,11 +10,10 @@ import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.constants.TestTags; import com.github.streamshub.systemtests.constants.TimeConstants; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; -import com.github.streamshub.systemtests.locators.CssBuilder; -import com.github.streamshub.systemtests.locators.CssSelectors; -import com.github.streamshub.systemtests.locators.KafkaDashboardPageSelectors; -import com.github.streamshub.systemtests.locators.NodesPageSelectors; +import com.github.streamshub.systemtests.locators.components.Masthead; +import com.github.streamshub.systemtests.locators.components.Modal; +import com.github.streamshub.systemtests.locators.pages.ClusterOverviewPage; +import com.github.streamshub.systemtests.locators.pages.NodesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaSetup; @@ -102,11 +101,11 @@ void testUserLoginKafkaCredentials() { // Verify basic UI elements LOGGER.info("Verifying navbar shows {} available Kafka cluster(s)", 1); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "1", true); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "1", true); LOGGER.info("Verifying broker replica count card shows {}/{}", Constants.REGULAR_BROKER_REPLICAS, Constants.REGULAR_BROKER_REPLICAS); - PwUtils.waitForContainsText(tcc, - ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_BROKER_COUNT, + PwUtils.waitForContainsText( + ClusterOverviewPage.brokerCount(tcc.page()), Constants.REGULAR_BROKER_REPLICAS + "/" + Constants.REGULAR_BROKER_REPLICAS, TimeConstants.ACTION_WAIT_LONG); LOGGER.info("Navigating to Nodes page to verify node counts and role distribution"); @@ -114,25 +113,25 @@ void testUserLoginKafkaCredentials() { // Header LOGGER.debug("Checking Nodes page header badges - total: {}, working: {}, warning: 0", Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS, Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_HEADER_TITLE_BADGE_TOTAL_COUNT, Integer.toString(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS), true); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_HEADER_TITLE_BADGE_WORKING_NODES_COUNT, Integer.toString(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS), true); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_HEADER_TITLE_BADGE_WARNING_NODES_COUNT, "0", true); + PwUtils.waitForContainsText(NodesPage.totalCountBadge(tcc.page()), Integer.toString(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS), true); + PwUtils.waitForContainsText(NodesPage.workingNodesBadge(tcc.page()), Integer.toString(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS), true); + PwUtils.waitForContainsText(NodesPage.warningNodesBadge(tcc.page()), "0", true); // Page infobox LOGGER.debug("Checking Nodes page table row count matches total node count {}", Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS); - PwUtils.waitForLocatorCount(tcc, Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS, NodesPageSelectors.NPS_TABLE_BODY, true); + PwUtils.waitForLocatorCount(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS, NodesPage.table(tcc.page()).rows(), true); // total nodes LOGGER.debug("Checking node role distribution - {} controllers, {} brokers", Constants.REGULAR_CONTROLLER_REPLICAS, Constants.REGULAR_BROKER_REPLICAS); - PwUtils.waitForContainsText(tcc, new CssBuilder(NodesPageSelectors.NPS_OVERVIEW_NODE_ITEMS).nth(1).build(), Integer.toString(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS), true); + PwUtils.waitForContainsText(NodesPage.totalNodesCount(tcc.page()), Integer.toString(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS), true); // with controller role - PwUtils.waitForContainsText(tcc, new CssBuilder(NodesPageSelectors.NPS_OVERVIEW_NODE_ITEMS).nth(2).build(), Integer.toString(Constants.REGULAR_CONTROLLER_REPLICAS), true); + PwUtils.waitForContainsText(NodesPage.controllerRoleCount(tcc.page()), Integer.toString(Constants.REGULAR_CONTROLLER_REPLICAS), true); // with broker role - PwUtils.waitForContainsText(tcc, new CssBuilder(NodesPageSelectors.NPS_OVERVIEW_NODE_ITEMS).nth(3).build(), Integer.toString(Constants.REGULAR_BROKER_REPLICAS), true); + PwUtils.waitForContainsText(NodesPage.brokerRoleCount(tcc.page()), Integer.toString(Constants.REGULAR_BROKER_REPLICAS), true); // Node table LOGGER.debug("Asserting Nodes page table contains {} rows", Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS); - assertEquals(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS, tcc.page().locator(NodesPageSelectors.NPS_TABLE_BODY).all().size()); + assertEquals(Constants.REGULAR_BROKER_REPLICAS + Constants.REGULAR_CONTROLLER_REPLICAS, NodesPage.table(tcc.page()).rowCount()); LOGGER.info("Verifying navbar displays logged-in Kafka username '{}'", tcc.kafkaUserName()); - PwUtils.waitForContainsText(tcc, KafkaDashboardPageSelectors.KDPS_CURRENTLY_LOGGED_USER_BUTTON, tcc.kafkaUserName(), true); + PwUtils.waitForContainsText(Masthead.userDropdownButton(tcc.page(), tcc.kafkaUserName()), tcc.kafkaUserName(), true); LOGGER.info("Verifying topic counts on Overview and Topics pages - total: {}, replicated: {}, under-replicated: {}, unavailable: {}", TOTAL_TOPICS_COUNT, TOTAL_REPLICATED_TOPICS_COUNT, UNDER_REPLICATED_TOPICS_COUNT, UNAVAILABLE_TOPICS_COUNT); TopicChecks.checkOverviewPageTopicState(tcc, tcc.kafkaName(), TOTAL_TOPICS_COUNT, TOTAL_TOPICS_COUNT, TOTAL_REPLICATED_TOPICS_COUNT, UNDER_REPLICATED_TOPICS_COUNT, UNAVAILABLE_TOPICS_COUNT); @@ -143,41 +142,41 @@ void testUserLoginKafkaCredentials() { PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); LOGGER.debug("Clicking pause reconciliation button to open confirmation modal"); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON, "Pause Reconciliation", false); - PwUtils.waitForLocatorAndClick(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON); + PwUtils.waitForContainsText(ClusterOverviewPage.pauseReconciliationButton(tcc.page()), "Pause Reconciliation", false); + PwUtils.waitForLocatorAndClick(ClusterOverviewPage.pauseReconciliationButton(tcc.page())); LOGGER.debug("Verifying pause reconciliation confirmation modal content"); - PwUtils.waitForLocatorVisible(tcc, CssSelectors.PAGES_POPUP_MODAL); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_POPUP_MODAL_HEADER, "Pause cluster reconciliation?", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_POPUP_MODAL_BODY, "While paused, updates to the cluster are ignored until reconciliation is resumed.", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CANCEL_BUTTON, "Cancel", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON, "Confirm", false); + PwUtils.waitForLocatorVisible(Modal.root(tcc.page())); + PwUtils.waitForContainsText(Modal.heading(tcc.page()), "Pause cluster reconciliation?", false); + PwUtils.waitForContainsText(Modal.root(tcc.page()), "While paused, updates to the cluster are ignored until reconciliation is resumed.", false); + PwUtils.waitForContainsText(Modal.cancelButton(tcc.page()), "Cancel", false); + PwUtils.waitForContainsText(Modal.confirmButton(tcc.page()), "Confirm", false); LOGGER.debug("Clicking Confirm button to pause reconciliation"); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON); + PwUtils.waitForLocatorAndClick(Modal.confirmButton(tcc.page())); // Check aftermath LOGGER.info("Verifying reconciliation paused notification is displayed"); - PwUtils.waitForLocatorVisible(tcc, ClusterOverviewPageSelectors.COPS_RECONCILIATION_PAUSED_NOTIFICATION); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_RECONCILIATION_PAUSED_NOTIFICATION, "Cluster reconciliation paused. Changes to the Kafka resource will not be applied.", false); + PwUtils.waitForLocatorVisible(ClusterOverviewPage.reconciliationPausedBanner(tcc.page())); + PwUtils.waitForContainsText(ClusterOverviewPage.reconciliationPausedBanner(tcc.page()), "Cluster reconciliation paused. Changes to the Kafka resource will not be applied.", false); LOGGER.info("Verifying Kafka resource '{}' annotation '{}' flips to '{}'", tcc.kafkaName(), ResourceAnnotations.ANNO_STRIMZI_IO_PAUSE_RECONCILIATION, "true"); WaitUtils.waitForKafkaHasAnnotationWithValue(tcc.namespace(), tcc.kafkaName(), ResourceAnnotations.ANNO_STRIMZI_IO_PAUSE_RECONCILIATION, "true"); LOGGER.info("Resuming Kafka reconciliation for cluster '{}' via UI", tcc.kafkaName()); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON, "Resume Reconciliation", true); - PwUtils.waitForLocatorAndClick(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON); + PwUtils.waitForContainsText(ClusterOverviewPage.pauseReconciliationButton(tcc.page()), "Resume Reconciliation", true); + PwUtils.waitForLocatorAndClick(ClusterOverviewPage.pauseReconciliationButton(tcc.page())); LOGGER.debug("Verifying resume reconciliation confirmation modal content"); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CANCEL_BUTTON, "Cancel", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON, "Confirm", false); + PwUtils.waitForContainsText(Modal.cancelButton(tcc.page()), "Cancel", false); + PwUtils.waitForContainsText(Modal.confirmButton(tcc.page()), "Confirm", false); LOGGER.debug("Clicking Confirm button to resume reconciliation"); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON); + PwUtils.waitForLocatorAndClick(Modal.confirmButton(tcc.page())); // Reconciliation is resumed and button should display Pause LOGGER.info("Verifying reconciliation resumed and button displays 'Pause Reconciliation' again"); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON, "Pause Reconciliation", true); + PwUtils.waitForContainsText(ClusterOverviewPage.pauseReconciliationButton(tcc.page()), "Pause Reconciliation", true); // Check annotation LOGGER.info("Verifying Kafka resource '{}' annotation '{}' flips back to '{}'", tcc.kafkaName(), ResourceAnnotations.ANNO_STRIMZI_IO_PAUSE_RECONCILIATION, "false"); diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/consumers/GroupsST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/consumers/GroupsST.java index 3b8538d24..2a516bd50 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/consumers/GroupsST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/consumers/GroupsST.java @@ -13,9 +13,9 @@ import com.github.streamshub.systemtests.constants.TimeConstants; import com.github.streamshub.systemtests.enums.ResetOffsetDateTimeType; import com.github.streamshub.systemtests.enums.ResetOffsetType; -import com.github.streamshub.systemtests.locators.GroupsPageSelectors; -import com.github.streamshub.systemtests.locators.SingleGroupPageSelectors; -import com.github.streamshub.systemtests.locators.TopicsPageSelectors; +import com.github.streamshub.systemtests.locators.pages.GroupsPage; +import com.github.streamshub.systemtests.locators.pages.SingleGroupPage; +import com.github.streamshub.systemtests.locators.pages.TopicsPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaSetup; @@ -168,27 +168,27 @@ void testVariableConsumerGroupNames() { // Verify row on groups page LOGGER.info("Verify group '{}' ('{}') is present in groups table", displayName, consumerGroupName); PwUtils.navigate(tcc, PwPageUrls.getGroupsPage(tcc, tcc.kafkaName())); - PwUtils.waitForContainsText(tcc, GroupsPageSelectors.GPS_HEADER_TITLE, "Groups", true); - PwUtils.fill(tcc, GroupsPageSelectors.GPS_GROUP_NAME_INPUT, consumerGroupName); - PwUtils.waitForContainsText(tcc, GroupsPageSelectors.GPS_RESULT_FIRST_NAME, consumerGroupName, false); + PwUtils.waitForContainsText(GroupsPage.heading(tcc.page()), "Groups", true); + PwUtils.fill(GroupsPage.searchInput(tcc.page()), consumerGroupName); + PwUtils.waitForContainsText(GroupsPage.table(tcc.page()).rows().first(), consumerGroupName, false); // Verify single group page LOGGER.info("Navigate to single consumer group page for '{}' ('{}')", displayName, consumerGroupName); PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), consumerGroupEncodedName)); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, consumerGroupName, true); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_HEADER_BREADCRUMB_GROUP_NAME, consumerGroupName, true); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), consumerGroupName, true); + PwUtils.waitForContainsText(SingleGroupPage.breadcrumbGroupName(tcc.page()), consumerGroupName, true); // Click through from groups page LOGGER.info("Navigate back to groups page and test click-through for '{}' ('{}')", displayName, consumerGroupName); PwUtils.navigate(tcc, PwPageUrls.getGroupsPage(tcc, tcc.kafkaName())); - PwUtils.waitForContainsText(tcc, GroupsPageSelectors.GPS_HEADER_TITLE, "Groups", true); - PwUtils.fill(tcc, GroupsPageSelectors.GPS_GROUP_NAME_INPUT, consumerGroupName); - PwUtils.waitForContainsText(tcc, GroupsPageSelectors.GPS_RESULT_FIRST_NAME, consumerGroupName, false); - tcc.page().click(GroupsPageSelectors.GPS_RESULT_FIRST_NAME); + PwUtils.waitForContainsText(GroupsPage.heading(tcc.page()), "Groups", true); + PwUtils.fill(GroupsPage.searchInput(tcc.page()), consumerGroupName); + PwUtils.waitForContainsText(GroupsPage.table(tcc.page()).rows().first(), consumerGroupName, false); + GroupsPage.table(tcc.page()).rows().first().locator("a").click(); PwUtils.waitForUrl(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), consumerGroupEncodedName), true); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, consumerGroupName, true); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_HEADER_BREADCRUMB_GROUP_NAME, consumerGroupName, true); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), consumerGroupName, true); + PwUtils.waitForContainsText(SingleGroupPage.breadcrumbGroupName(tcc.page()), consumerGroupName, true); // Verify group on topic page LOGGER.info("Check topic page if consumer group '{}' ('{}') is present", displayName, consumerGroupName); @@ -198,11 +198,11 @@ void testVariableConsumerGroupNames() { PwUtils.navigate(tcc, PwPageUrls.getSingleTopicGroupsPage(tcc, tcc.kafkaName(), topicId), true, true); // Topic page is focused on one topic so filter by text is safer than row index - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_GROUPS_TABLE_FIRST_GROUP); + PwUtils.waitForLocatorAndClick(TopicsPage.groupsTabFirstGroupLink(tcc.page())); PwUtils.waitForUrl(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), consumerGroupEncodedName), true); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, consumerGroupName, true); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_HEADER_BREADCRUMB_GROUP_NAME, consumerGroupName, true); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), consumerGroupName, true); + PwUtils.waitForContainsText(SingleGroupPage.breadcrumbGroupName(tcc.page()), consumerGroupName, true); // Confirm the producer/consumer didn't error during the scenario, and clean up the Jobs before the next one WaitUtils.waitForClientsSuccess(clients); @@ -298,8 +298,8 @@ void testResetConsumerOffsetAllTopicsAllPartitions(int messageCount, LOGGER.debug("Found {} topic(s) with prefix '{}' for offset reset: {}", kafkaTopicNames.size(), RESET_OFFSET_TOPIC_PREFIX, kafkaTopicNames); PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), Identifiers.encode(RESET_OFFSET_CONSUMER_GROUP_NAME))); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, RESET_OFFSET_CONSUMER_GROUP_NAME, true); - PwUtils.waitForElementEnabledState(tcc, SingleGroupPageSelectors.SGPS_RESET_CONSUMER_OFFSET_BUTTON, true, true); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), RESET_OFFSET_CONSUMER_GROUP_NAME, true); + PwUtils.waitForElementEnabledState(SingleGroupPage.resetOffsetButton(tcc.page()), true, true); // Look at the offset in UI for (String kafkaTopicName : kafkaTopicNames) { @@ -329,14 +329,14 @@ void testResetConsumerOffsetAllTopicsAllPartitions(int messageCount, } PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), Identifiers.encode(RESET_OFFSET_CONSUMER_GROUP_NAME))); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, RESET_OFFSET_CONSUMER_GROUP_NAME, true); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_CONSUMER_OFFSET_BUTTON); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), RESET_OFFSET_CONSUMER_GROUP_NAME, true); + PwUtils.waitForLocatorAndClick(SingleGroupPage.resetOffsetButton(tcc.page())); LOGGER.info("Performing dry-run offset reset for topic '{}' with value '{}'", kafkaTopicName, resetValue); GroupsTestUtils.execDryRun(tcc, resetType, dateTimeType, resetValue); PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), Identifiers.encode(RESET_OFFSET_CONSUMER_GROUP_NAME))); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, RESET_OFFSET_CONSUMER_GROUP_NAME, true); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_CONSUMER_OFFSET_BUTTON); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), RESET_OFFSET_CONSUMER_GROUP_NAME, true); + PwUtils.waitForLocatorAndClick(SingleGroupPage.resetOffsetButton(tcc.page())); LOGGER.info("Performing offset reset for topic '{}' with value '{}'", kafkaTopicName, resetValue); GroupsTestUtils.execResetOffset(tcc, resetType, dateTimeType, resetValue); @@ -419,8 +419,8 @@ void testResetConsumerOffsetSpecificTopic(int messageCount, LOGGER.debug("Selected target topic '{}' for single-topic offset reset", kafkaTopicName); PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), Identifiers.encode(RESET_OFFSET_CONSUMER_GROUP_NAME))); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, RESET_OFFSET_CONSUMER_GROUP_NAME, true); - PwUtils.waitForElementEnabledState(tcc, SingleGroupPageSelectors.SGPS_RESET_CONSUMER_OFFSET_BUTTON, true, true); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), RESET_OFFSET_CONSUMER_GROUP_NAME, true); + PwUtils.waitForElementEnabledState(SingleGroupPage.resetOffsetButton(tcc.page()), true, true); LOGGER.info("Setting baseline consumer offset {} for topic '{}', group '{}'", messageCount, kafkaTopicName, RESET_OFFSET_CONSUMER_GROUP_NAME); KafkaCmdUtils.setConsumerGroupOffset(tcc.namespace(), tcc.kafkaName(), brokerPodName, RESET_OFFSET_CONSUMER_GROUP_NAME, kafkaTopicName, String.valueOf(messageCount), @@ -449,21 +449,21 @@ void testResetConsumerOffsetSpecificTopic(int messageCount, // Dry-run PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), Identifiers.encode(RESET_OFFSET_CONSUMER_GROUP_NAME))); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, RESET_OFFSET_CONSUMER_GROUP_NAME, true); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_CONSUMER_OFFSET_BUTTON); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_SELECTED_TOPIC_DROPDOWN_BUTTON); - PwUtils.waitForLocatorAndFill(tcc, SingleGroupPageSelectors.SGPS_SELECTED_TOPIC_DROPDOWN_SEARCH_INPUT, kafkaTopicName); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_TOPIC_NAME_DROPDOWN_RESULT); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), RESET_OFFSET_CONSUMER_GROUP_NAME, true); + PwUtils.waitForLocatorAndClick(SingleGroupPage.resetOffsetButton(tcc.page())); + PwUtils.waitForLocatorAndClick(SingleGroupPage.topicDropdownButton(tcc.page())); + PwUtils.waitForLocatorAndFill(SingleGroupPage.topicDropdownSearchInput(tcc.page()), kafkaTopicName); + PwUtils.waitForLocatorAndClick(SingleGroupPage.topicDropdownResult(tcc.page(), kafkaTopicName)); LOGGER.info("Performing dry-run offset reset for topic '{}' with value '{}'", kafkaTopicName, resetValue); GroupsTestUtils.execDryRun(tcc, resetType, dateTimeType, resetValue); // Reset offset PwUtils.navigate(tcc, PwPageUrls.getGroupsMembersPage(tcc, tcc.kafkaName(), Identifiers.encode(RESET_OFFSET_CONSUMER_GROUP_NAME))); - PwUtils.waitForContainsText(tcc, SingleGroupPageSelectors.SGPS_PAGE_HEADER_NAME, RESET_OFFSET_CONSUMER_GROUP_NAME, true); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_CONSUMER_OFFSET_BUTTON); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_SELECTED_TOPIC_DROPDOWN_BUTTON); - PwUtils.waitForLocatorAndFill(tcc, SingleGroupPageSelectors.SGPS_SELECTED_TOPIC_DROPDOWN_SEARCH_INPUT, kafkaTopicName); - PwUtils.waitForLocatorAndClick(tcc, SingleGroupPageSelectors.SGPS_RESET_PAGE_TOPIC_NAME_DROPDOWN_RESULT); + PwUtils.waitForContainsText(SingleGroupPage.heading(tcc.page()), RESET_OFFSET_CONSUMER_GROUP_NAME, true); + PwUtils.waitForLocatorAndClick(SingleGroupPage.resetOffsetButton(tcc.page())); + PwUtils.waitForLocatorAndClick(SingleGroupPage.topicDropdownButton(tcc.page())); + PwUtils.waitForLocatorAndFill(SingleGroupPage.topicDropdownSearchInput(tcc.page()), kafkaTopicName); + PwUtils.waitForLocatorAndClick(SingleGroupPage.topicDropdownResult(tcc.page(), kafkaTopicName)); LOGGER.info("Performing offset reset for topic '{}' with value '{}'", kafkaTopicName, resetValue); GroupsTestUtils.execResetOffset(tcc, resetType, dateTimeType, resetValue); diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/ConnectST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/ConnectST.java index c8de9ebfd..859f9b6e1 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/ConnectST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/ConnectST.java @@ -8,7 +8,7 @@ import com.github.streamshub.systemtests.clients.KafkaClientsBuilder; import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.constants.TestTags; -import com.github.streamshub.systemtests.locators.KafkaConnectPageSelectors; +import com.github.streamshub.systemtests.locators.pages.KafkaConnectPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaConnectSetup; @@ -22,6 +22,7 @@ import com.github.streamshub.systemtests.utils.resourceutils.kafka.KafkaNamingUtils; import com.github.streamshub.systemtests.utils.resourceutils.kafka.KafkaTopicUtils; import com.github.streamshub.systemtests.utils.resourceutils.kafka.KafkaUtils; +import com.microsoft.playwright.Locator; import io.skodjob.kubetest4j.resources.KubeResourceManager; import io.strimzi.api.kafka.model.connect.KafkaConnectResources; import org.apache.kafka.common.security.auth.SecurityProtocol; @@ -93,58 +94,60 @@ void testFilterKafkaConnectClustersAndConnectors() { PwUtils.navigate(tcc, PwPageUrls.getKafkaConnectPage(tcc, tcc.kafkaName())); LOGGER.debug("Verifying Kafka Connect page header is visible"); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.KCPS_PAGE_TITLE_NAME, "Kafka Connect", true); + PwUtils.waitForContainsText(KafkaConnectPage.pageTitle(tcc.page()), "Kafka Connect", true); LOGGER.debug("Waiting for connectors table and verifying initial count (expected: 2)"); - PwUtils.waitForLocatorVisible(tcc, KafkaConnectPageSelectors.KCPS_NAME_FILTER_INPUT); - PwUtils.waitForLocatorCount(tcc, 2, KafkaConnectPageSelectors.KCPS_TABLE_ITEMS, true); + PwUtils.waitForLocatorVisible(KafkaConnectPage.nameFilterInput(tcc.page())); + PwUtils.waitForLocatorCount(2, KafkaConnectPage.table(tcc.page(), true).rows(), true); // ---- SRC connector filter ---- LOGGER.info("Filtering connectors by source connector name: {}", SOURCE_CONNECTOR_NAME); - PwUtils.waitForLocatorAndFill(tcc, KafkaConnectPageSelectors.KCPS_NAME_FILTER_INPUT, SOURCE_CONNECTOR_NAME); + PwUtils.waitForLocatorAndFill(KafkaConnectPage.nameFilterInput(tcc.page()), SOURCE_CONNECTOR_NAME); LOGGER.debug("Verifying exactly 1 connector remains, named '{}', on cluster '{}', with type 'Source'", SOURCE_CONNECTOR_NAME, KAFKA_CONNECT_SRC_NAME); - PwUtils.waitForLocatorCount(tcc, 1, KafkaConnectPageSelectors.KCPS_TABLE_ITEMS, false); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 1), SOURCE_CONNECTOR_NAME, true); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 2), KAFKA_CONNECT_SRC_NAME, true); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 3), "Source", true); + PwUtils.waitForLocatorCount(1, KafkaConnectPage.table(tcc.page(), true).rows(), false); + Locator connectorRow = KafkaConnectPage.table(tcc.page(), true).rows().first(); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), true).cell(connectorRow, "Name"), SOURCE_CONNECTOR_NAME, true); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), true).cell(connectorRow, "Connect cluster"), KAFKA_CONNECT_SRC_NAME, true); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), true).cell(connectorRow, "Type"), "Source", true); // ---- Sink connector filter ---- LOGGER.info("Filtering connectors by sink connector name: {}", SINK_CONNECTOR_NAME); - PwUtils.waitForLocatorAndFill(tcc, KafkaConnectPageSelectors.KCPS_NAME_FILTER_INPUT, SINK_CONNECTOR_NAME); + PwUtils.waitForLocatorAndFill(KafkaConnectPage.nameFilterInput(tcc.page()), SINK_CONNECTOR_NAME); LOGGER.debug("Verifying exactly 1 connector remains, named '{}', on cluster '{}', with type 'Sink'", SINK_CONNECTOR_NAME, KAFKA_CONNECT_SINK_NAME); - PwUtils.waitForLocatorCount(tcc, 1, KafkaConnectPageSelectors.KCPS_TABLE_ITEMS, false); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 1), SINK_CONNECTOR_NAME, true); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 2), KAFKA_CONNECT_SINK_NAME, true); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 3), "Sink", true); + PwUtils.waitForLocatorCount(1, KafkaConnectPage.table(tcc.page(), true).rows(), false); + connectorRow = KafkaConnectPage.table(tcc.page(), true).rows().first(); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), true).cell(connectorRow, "Name"), SINK_CONNECTOR_NAME, true); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), true).cell(connectorRow, "Connect cluster"), KAFKA_CONNECT_SINK_NAME, true); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), true).cell(connectorRow, "Type"), "Sink", true); // Filter connect clusters LOGGER.info("Navigating to Kafka Connect clusters page"); PwUtils.navigate(tcc, PwPageUrls.getKafkaConnectClusterPage(tcc, tcc.kafkaName())); LOGGER.debug("Verifying Kafka Connect clusters page header"); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.KCPS_PAGE_TITLE_NAME, "Kafka Connect", true); + PwUtils.waitForContainsText(KafkaConnectPage.pageTitle(tcc.page()), "Kafka Connect", true); LOGGER.debug("Waiting for clusters table and verifying initial count (expected: 2)"); - PwUtils.waitForLocatorVisible(tcc, KafkaConnectPageSelectors.KCPS_NAME_FILTER_INPUT); - PwUtils.waitForLocatorCount(tcc, 2, KafkaConnectPageSelectors.KCPS_TABLE_ITEMS, true); + PwUtils.waitForLocatorVisible(KafkaConnectPage.nameFilterInput(tcc.page())); + PwUtils.waitForLocatorCount(2, KafkaConnectPage.table(tcc.page(), false).rows(), true); // ---- Source cluster filter ---- LOGGER.info("Filtering Kafka Connect clusters by source cluster name: {}", KAFKA_CONNECT_SRC_NAME); - PwUtils.waitForLocatorAndFill(tcc, KafkaConnectPageSelectors.KCPS_NAME_FILTER_INPUT, KAFKA_CONNECT_SRC_NAME); + PwUtils.waitForLocatorAndFill(KafkaConnectPage.nameFilterInput(tcc.page()), KAFKA_CONNECT_SRC_NAME); LOGGER.debug("Verifying exactly 1 Kafka Connect cluster remains, named '{}'", KAFKA_CONNECT_SRC_NAME); - PwUtils.waitForLocatorCount(tcc, 1, KafkaConnectPageSelectors.KCPS_TABLE_ITEMS, false); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 1), KAFKA_CONNECT_SRC_NAME, true); + PwUtils.waitForLocatorCount(1, KafkaConnectPage.table(tcc.page(), false).rows(), false); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), false).cell(KafkaConnectPage.table(tcc.page(), false).rows().first(), "Name"), KAFKA_CONNECT_SRC_NAME, true); // ---- Sink cluster filter ---- LOGGER.info("Filtering Kafka Connect clusters by sink cluster name: {}", KAFKA_CONNECT_SINK_NAME); - PwUtils.waitForLocatorAndFill(tcc, KafkaConnectPageSelectors.KCPS_NAME_FILTER_INPUT, KAFKA_CONNECT_SINK_NAME); + PwUtils.waitForLocatorAndFill(KafkaConnectPage.nameFilterInput(tcc.page()), KAFKA_CONNECT_SINK_NAME); LOGGER.debug("Verifying exactly 1 Kafka Connect cluster remains, named '{}'", KAFKA_CONNECT_SINK_NAME); - PwUtils.waitForLocatorCount(tcc, 1, KafkaConnectPageSelectors.KCPS_TABLE_ITEMS, false); - PwUtils.waitForContainsText(tcc, KafkaConnectPageSelectors.getTableRowItem(1, 1), KAFKA_CONNECT_SINK_NAME, true); + PwUtils.waitForLocatorCount(1, KafkaConnectPage.table(tcc.page(), false).rows(), false); + PwUtils.waitForContainsText(KafkaConnectPage.table(tcc.page(), false).cell(KafkaConnectPage.table(tcc.page(), false).rows().first(), "Name"), KAFKA_CONNECT_SINK_NAME, true); LOGGER.info("Kafka Connect connector and cluster filtering test finished successfully"); } diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/KafkaST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/KafkaST.java index b357b370c..9386d81e0 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/KafkaST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/KafkaST.java @@ -17,10 +17,9 @@ import com.github.streamshub.systemtests.constants.Labels; import com.github.streamshub.systemtests.constants.TestTags; import com.github.streamshub.systemtests.constants.TimeConstants; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; -import com.github.streamshub.systemtests.locators.CssBuilder; -import com.github.streamshub.systemtests.locators.CssSelectors; -import com.github.streamshub.systemtests.locators.NodesPageSelectors; +import com.github.streamshub.systemtests.locators.components.Modal; +import com.github.streamshub.systemtests.locators.pages.ClusterOverviewPage; +import com.github.streamshub.systemtests.locators.pages.NodesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaSetup; @@ -91,7 +90,7 @@ void testPauseResumeReconciliationAndScaleKafkaNodes() { LOGGER.debug("Verify default Kafka broker count on OverviewPage"); PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_BROKER_COUNT, + PwUtils.waitForContainsText(ClusterOverviewPage.brokerCount(tcc.page()), Constants.REGULAR_BROKER_REPLICAS + "/" + Constants.REGULAR_BROKER_REPLICAS, true); LOGGER.debug("Verify default Kafka node count on Nodes page"); @@ -108,23 +107,23 @@ void testPauseResumeReconciliationAndScaleKafkaNodes() { PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); LOGGER.debug("Open pop-up modal for pause reconciliation"); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON, "Pause Reconciliation", false); - PwUtils.waitForLocatorAndClick(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON); + PwUtils.waitForContainsText(ClusterOverviewPage.pauseReconciliationButton(tcc.page()), "Pause Reconciliation", false); + PwUtils.waitForLocatorAndClick(ClusterOverviewPage.pauseReconciliationButton(tcc.page())); LOGGER.debug("Check pop-up modal for pause reconciliation"); - PwUtils.waitForLocatorVisible(tcc, CssSelectors.PAGES_POPUP_MODAL); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_POPUP_MODAL_HEADER, "Pause cluster reconciliation?", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_POPUP_MODAL_BODY, "While paused, updates to the cluster are ignored until reconciliation is resumed.", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CANCEL_BUTTON, "Cancel", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON, "Confirm", false); + PwUtils.waitForLocatorVisible(Modal.root(tcc.page())); + PwUtils.waitForContainsText(Modal.heading(tcc.page()), "Pause cluster reconciliation?", false); + PwUtils.waitForContainsText(Modal.root(tcc.page()), "While paused, updates to the cluster are ignored until reconciliation is resumed.", false); + PwUtils.waitForContainsText(Modal.cancelButton(tcc.page()), "Cancel", false); + PwUtils.waitForContainsText(Modal.confirmButton(tcc.page()), "Confirm", false); LOGGER.debug("Confirm pause reconciliation"); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON); + PwUtils.waitForLocatorAndClick(Modal.confirmButton(tcc.page())); // Check aftermath LOGGER.info("Verify UI state after pausing Kafka reconciliation"); - PwUtils.waitForLocatorVisible(tcc, ClusterOverviewPageSelectors.COPS_RECONCILIATION_PAUSED_NOTIFICATION); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_RECONCILIATION_PAUSED_NOTIFICATION, + PwUtils.waitForLocatorVisible(ClusterOverviewPage.reconciliationPausedBanner(tcc.page())); + PwUtils.waitForContainsText(ClusterOverviewPage.reconciliationPausedBanner(tcc.page()), "Cluster reconciliation paused. Changes to the Kafka resource will not be applied.", false); LOGGER.debug("Verify Kafka has pause reconciliation annotation set to true"); @@ -134,13 +133,12 @@ void testPauseResumeReconciliationAndScaleKafkaNodes() { LOGGER.info("Verify ReconciliationPaused warning appears in UI"); // Check the reconciliation paused warning is displayed in the warnings card - if (tcc.page().locator(ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_WARNING_DROPDOWN_LIST).isHidden()) { + if (ClusterOverviewPage.warningsList(tcc.page()).isHidden()) { LOGGER.debug("Warnings dropdown list is hidden, opening it to reveal warning messages"); - PwUtils.waitForLocatorAndClick(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_WARNINGS_DROPDOWN_BUTTON); + PwUtils.waitForLocatorAndClick(ClusterOverviewPage.warningsToggle(tcc.page())); } - PwUtils.waitForContainsText(tcc, - ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_WARNING_MESSAGE_ITEMS, + PwUtils.waitForContainsText(ClusterOverviewPage.warningMessages(tcc.page()), "Cluster reconciliation paused. Changes to the Kafka resource will not be applied.", false); @@ -162,17 +160,17 @@ void testPauseResumeReconciliationAndScaleKafkaNodes() { WaitUtils.waitForPodsReadyAndStable(tcc.namespace(), Labels.getKnpBrokerLabelSelector(tcc.kafkaName()), Constants.REGULAR_BROKER_REPLICAS, true); LOGGER.info("Resume Kafka reconciliation using UI"); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON, "Resume Reconciliation", true); - PwUtils.waitForLocatorAndClick(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON); + PwUtils.waitForContainsText(ClusterOverviewPage.pauseReconciliationButton(tcc.page()), "Resume Reconciliation", true); + PwUtils.waitForLocatorAndClick(ClusterOverviewPage.pauseReconciliationButton(tcc.page())); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CANCEL_BUTTON, "Cancel", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON, "Confirm", false); + PwUtils.waitForContainsText(Modal.cancelButton(tcc.page()), "Cancel", false); + PwUtils.waitForContainsText(Modal.confirmButton(tcc.page()), "Confirm", false); LOGGER.debug("Confirm resume reconciliation"); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON); + PwUtils.waitForLocatorAndClick(Modal.confirmButton(tcc.page())); // Reconciliation is resumed and button should display Pause - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON, "Pause Reconciliation", true); + PwUtils.waitForContainsText(ClusterOverviewPage.pauseReconciliationButton(tcc.page()), "Pause Reconciliation", true); // Check annotation LOGGER.debug("Verify Kafka has pause reconciliation annotation set back to false"); @@ -187,7 +185,7 @@ void testPauseResumeReconciliationAndScaleKafkaNodes() { LOGGER.debug("Verify new Kafka broker count on OverviewPage is {}", scaledBrokersCount); PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_BROKER_COUNT, + PwUtils.waitForContainsText(ClusterOverviewPage.brokerCount(tcc.page()), scaledBrokersCount + "/" + scaledBrokersCount, TimeConstants.ACTION_WAIT_LONG); LOGGER.debug("Verify new Kafka node count on Nodes page"); @@ -197,23 +195,23 @@ void testPauseResumeReconciliationAndScaleKafkaNodes() { // Now verify resume from top notification and just check the annotation on Kafka cluster LOGGER.info("Pause Kafka reconciliation using UI"); PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); - PwUtils.waitForLocatorAndClick(tcc, ClusterOverviewPageSelectors.COPS_KAFKA_PAUSE_RECONCILIATION_BUTTON); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_POPUP_MODAL_HEADER, "Pause cluster reconciliation?", false); + PwUtils.waitForLocatorAndClick(ClusterOverviewPage.pauseReconciliationButton(tcc.page())); + PwUtils.waitForContainsText(Modal.heading(tcc.page()), "Pause cluster reconciliation?", false); LOGGER.debug("Confirm pause reconciliation"); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON); + PwUtils.waitForLocatorAndClick(Modal.confirmButton(tcc.page())); LOGGER.debug("Verify Kafka has pause reconciliation annotation set to true"); WaitUtils.waitForKafkaHasAnnotationWithValue(tcc.namespace(), tcc.kafkaName(), ResourceAnnotations.ANNO_STRIMZI_IO_PAUSE_RECONCILIATION, "true"); LOGGER.info("Resume Kafka reconciliation using button from top notification"); - PwUtils.waitForLocatorAndClick(tcc, ClusterOverviewPageSelectors.COPS_RECONCILIATION_PAUSED_NOTIFICATION_RESUME_BUTTON); + PwUtils.waitForLocatorAndClick(ClusterOverviewPage.reconciliationResumeButton(tcc.page())); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CANCEL_BUTTON, "Cancel", false); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON, "Confirm", false); + PwUtils.waitForContainsText(Modal.cancelButton(tcc.page()), "Cancel", false); + PwUtils.waitForContainsText(Modal.confirmButton(tcc.page()), "Confirm", false); LOGGER.debug("Confirm resume reconciliation"); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_MODAL_CONFIRM_BUTTON); + PwUtils.waitForLocatorAndClick(Modal.confirmButton(tcc.page())); LOGGER.debug("Verify Kafka has pause reconciliation annotation set back to false"); WaitUtils.waitForKafkaHasAnnotationWithValue(tcc.namespace(), tcc.kafkaName(), ResourceAnnotations.ANNO_STRIMZI_IO_PAUSE_RECONCILIATION, "false"); @@ -229,24 +227,24 @@ void testPauseResumeReconciliationAndScaleKafkaNodes() { private void checkNodesPageNodeCounts(int brokerCount) { LOGGER.debug("Verify Nodes page header shows total={}, working={}, warning=0", brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS, brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_HEADER_TITLE_BADGE_TOTAL_COUNT, + PwUtils.waitForContainsText(NodesPage.totalCountBadge(tcc.page()), Integer.toString(brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS), true); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_HEADER_TITLE_BADGE_WORKING_NODES_COUNT, + PwUtils.waitForContainsText(NodesPage.workingNodesBadge(tcc.page()), Integer.toString(brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS), true); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_HEADER_TITLE_BADGE_WARNING_NODES_COUNT, "0", true); + PwUtils.waitForContainsText(NodesPage.warningNodesBadge(tcc.page()), "0", true); // Page infobox // total nodes - PwUtils.waitForContainsText(tcc, new CssBuilder(NodesPageSelectors.NPS_OVERVIEW_NODE_ITEMS).nth(1).build(), + PwUtils.waitForContainsText(NodesPage.totalNodesCount(tcc.page()), Integer.toString(brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS), true); // with controller role - PwUtils.waitForContainsText(tcc, new CssBuilder(NodesPageSelectors.NPS_OVERVIEW_NODE_ITEMS).nth(2).build(), + PwUtils.waitForContainsText(NodesPage.controllerRoleCount(tcc.page()), Integer.toString(Constants.REGULAR_CONTROLLER_REPLICAS), true); // with broker role - PwUtils.waitForContainsText(tcc, new CssBuilder(NodesPageSelectors.NPS_OVERVIEW_NODE_ITEMS).nth(3).build(), + PwUtils.waitForContainsText(NodesPage.brokerRoleCount(tcc.page()), Integer.toString(brokerCount), true); // Node table LOGGER.debug("Verify Nodes page table row count equals {}", brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS); - assertEquals(brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS, tcc.page().locator(NodesPageSelectors.NPS_TABLE_BODY).all().size()); + assertEquals(brokerCount + Constants.REGULAR_CONTROLLER_REPLICAS, NodesPage.table(tcc.page()).rowCount()); } /** diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/RebalanceST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/RebalanceST.java index 757332689..679cc6d2b 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/RebalanceST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/kafka/RebalanceST.java @@ -3,7 +3,8 @@ import com.github.streamshub.systemtests.AbstractST; import com.github.streamshub.systemtests.TestCaseConfig; import com.github.streamshub.systemtests.constants.TestTags; -import com.github.streamshub.systemtests.locators.NodesPageSelectors; +import com.github.streamshub.systemtests.locators.components.Modal; +import com.github.streamshub.systemtests.locators.pages.NodesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaSetup; @@ -14,6 +15,8 @@ import com.github.streamshub.systemtests.utils.resourceutils.ResourceUtils; import com.github.streamshub.systemtests.utils.resourceutils.kafka.KafkaTopicUtils; import com.github.streamshub.systemtests.utils.resourceutils.kafka.KafkaUtils; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.options.AriaRole; import io.skodjob.kubetest4j.resources.KubeResourceManager; import io.strimzi.api.kafka.model.rebalance.KafkaRebalance; import io.strimzi.api.kafka.model.rebalance.KafkaRebalanceMode; @@ -81,36 +84,39 @@ void testKafkaRebalance() { LOGGER.info("Verifying rebalance proposals table shows a single 'Proposal Ready' entry for rebalance '{}'", rebalanceName); PwUtils.navigate(tcc, PwPageUrls.getKafkaRebalancePage(tcc, tcc.kafkaName())); - PwUtils.waitForLocatorCount(tcc, 1, NodesPageSelectors.NPS_REBALANCE_TABLE_ITEMS, true); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_REBALANCE_PROPOSAL_STATUS, "Proposal Ready", true); - assertTrue(tcc.page().locator(NodesPageSelectors.NPS_REBALANCE_PROPOSAL_NAME).allInnerTexts().toString().contains(rebalanceName)); + PwUtils.waitForLocatorCount(1, NodesPage.rebalanceTable(tcc.page()).rows(), true); + Locator rebalanceRow = NodesPage.rebalanceTable(tcc.page()).rows().first(); + PwUtils.waitForContainsText(NodesPage.rebalanceTable(tcc.page()).cell(rebalanceRow, "Status"), "Proposal Ready", true); + Locator rebalanceNameButton = rebalanceRow.getByRole(AriaRole.BUTTON); + assertTrue(rebalanceNameButton.allInnerTexts().toString().contains(rebalanceName)); LOGGER.info("Inspecting rebalance proposal dropdown for auto-approval flag and rebalance mode"); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_REBALANCE_PROPOSAL_DROPDOWN_BUTTON); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_REBALANCE_PROPOSAL_DROPDOWN_AUTO_APPROVAL_ENABLED, "false", true); - assertTrue(tcc.page().locator(NodesPageSelectors.NPS_REBALANCE_PROPOSAL_DROPDOWN_MODE).allInnerTexts().toString().toLowerCase(Locale.ENGLISH).contains(KafkaRebalanceMode.FULL.toValue())); + PwUtils.waitForLocatorAndClick(NodesPage.rebalanceExpandToggle(rebalanceRow)); + PwUtils.waitForContainsText(NodesPage.rebalanceAutoApprovalValue(tcc.page()), "false", true); + assertTrue(NodesPage.rebalanceModeValue(tcc.page()).allInnerTexts().toString().toLowerCase(Locale.ENGLISH).contains(KafkaRebalanceMode.FULL.toValue())); LOGGER.info("Opening proposal data modal for rebalance '{}' to verify optimization result values", rebalanceName); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_REBALANCE_PROPOSAL_NAME); + PwUtils.waitForLocatorAndClick(rebalanceNameButton); // table values Map<String, Object> status = ResourceUtils.getKubeResource(KafkaRebalance.class, tcc.namespace(), rebalanceName).getStatus().getOptimizationResult(); LOGGER.debug("KafkaRebalance '{}' optimizationResult from status: {}", rebalanceName, status); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_PROPOSAL_MODAL_DATA_TO_MOVE, status.get("dataToMoveMB").toString(), false); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_PROPOSAL_MODAL_DATA_MONITORED_PARTITIONS_PERCENTAGE, status.get("monitoredPartitionsPercentage").toString(), false); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_PROPOSAL_MODAL_DATA_NUMBER_REPLICA_MOVEMENTS, status.get("numReplicaMovements").toString(), false); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_PROPOSAL_MODAL_DATA_BALANCEDNESS_BEFORE, status.get("onDemandBalancednessScoreBefore").toString(), false); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_PROPOSAL_MODAL_DATA_BALANCEDNESS_AFTER, status.get("onDemandBalancednessScoreAfter").toString(), false); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_PROPOSAL_MODAL_CLOSE_BUTTON); + Locator modalRoot = Modal.root(tcc.page()); + PwUtils.waitForContainsText(NodesPage.rebalanceDataToMoveMb(tcc.page()), status.get("dataToMoveMB").toString(), false); + PwUtils.waitForContainsText(NodesPage.rebalanceMonitoredPartitionsPercentage(tcc.page()), status.get("monitoredPartitionsPercentage").toString(), false); + PwUtils.waitForContainsText(NodesPage.rebalanceNumReplicaMovements(tcc.page()), status.get("numReplicaMovements").toString(), false); + PwUtils.waitForContainsText(NodesPage.rebalanceBalancednessBefore(tcc.page()), status.get("onDemandBalancednessScoreBefore").toString(), false); + PwUtils.waitForContainsText(NodesPage.rebalanceBalancednessAfter(tcc.page()), status.get("onDemandBalancednessScoreAfter").toString(), false); + PwUtils.waitForLocatorAndClick(modalRoot.getByRole(AriaRole.BUTTON, new Locator.GetByRoleOptions().setName("Close"))); LOGGER.info("Approving rebalance proposal '{}' via UI action dropdown", rebalanceName); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_PROPOSAL_ACTION_DROPDOWN_BUTTON); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_PROPOSAL_ACTION_APPROVE_BUTTON); - PwUtils.waitForLocatorAndClick(tcc, NodesPageSelectors.NPS_PROPOSAL_MODAL_CONFIRM_BUTTON); + PwUtils.waitForLocatorAndClick(NodesPage.rebalanceActionsMenuButton(rebalanceRow)); + PwUtils.waitForLocatorAndClick(NodesPage.rebalanceMenuItem(tcc.page(), "Approve")); + PwUtils.waitForLocatorAndClick(Modal.confirmButton(tcc.page())); LOGGER.info("Verifying that UI approval transitioned KafkaRebalance '{}' to '{}' state", rebalanceName, KafkaRebalanceState.Rebalancing); WaitUtils.waitForKafkaRebalanceProposalStatus(tcc.namespace(), rebalanceName, KafkaRebalanceState.Rebalancing); LOGGER.debug("Confirming '{}' state is reflected in the UI proposal status for rebalance '{}'", KafkaRebalanceState.Rebalancing, rebalanceName); - PwUtils.waitForContainsText(tcc, NodesPageSelectors.NPS_REBALANCE_PROPOSAL_STATUS, KafkaRebalanceState.Rebalancing.name(), true); + PwUtils.waitForContainsText(NodesPage.rebalanceTable(tcc.page()).cell(NodesPage.rebalanceTable(tcc.page()).rows().first(), "Status"), KafkaRebalanceState.Rebalancing.name(), true); } @AfterEach diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/kroxylicious/KroxyST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/kroxylicious/KroxyST.java index aee43942e..606e92c59 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/kroxylicious/KroxyST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/kroxylicious/KroxyST.java @@ -5,8 +5,8 @@ import com.github.streamshub.systemtests.TestCaseConfig; import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.constants.TestTags; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; -import com.github.streamshub.systemtests.locators.CssSelectors; +import com.github.streamshub.systemtests.locators.components.Masthead; +import com.github.streamshub.systemtests.locators.pages.ClusterOverviewPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.kroxylicious.KroxyResourcesSetup; @@ -64,13 +64,13 @@ void testDisplayVirtualCluster() { LOGGER.info("Verify default kafka from strimzi CR"); PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_NAME, tcc.kafkaName(), true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_KAFKA_VERSION, Environment.ST_KAFKA_VERSION, true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_BROKER_COUNT, + PwUtils.waitForContainsText(ClusterOverviewPage.clusterName(tcc.page()), tcc.kafkaName(), true); + PwUtils.waitForContainsText(ClusterOverviewPage.kafkaVersion(tcc.page()), Environment.ST_KAFKA_VERSION, true); + PwUtils.waitForContainsText(ClusterOverviewPage.brokerCount(tcc.page()), Constants.REGULAR_BROKER_REPLICAS + "/" + Constants.REGULAR_BROKER_REPLICAS, true); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "2", true); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_NAV_KAFKA_SELECT_BUTTON); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "2", true); + PwUtils.waitForLocatorAndClick(Masthead.clusterSwitcherButton(tcc.page())); KroxyChecks.checkKafkaClusterDropdownContains(tcc, List.of(tcc.kafkaName(), tcc.virtualKafkaClusterName())); LOGGER.info("Verify virtual kafka from kroxy"); @@ -78,13 +78,13 @@ void testDisplayVirtualCluster() { String virtualClusterVersion = KroxyTestUtils.normalizeVirtualClusterVersionToMajorMinor(Environment.ST_KAFKA_VERSION); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_NAME, tcc.virtualKafkaClusterName(), true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_KAFKA_VERSION, virtualClusterVersion, true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CLUSTER_CARD_KAFKA_DATA_BROKER_COUNT, + PwUtils.waitForContainsText(ClusterOverviewPage.clusterName(tcc.page()), tcc.virtualKafkaClusterName(), true); + PwUtils.waitForContainsText(ClusterOverviewPage.kafkaVersion(tcc.page()), virtualClusterVersion, true); + PwUtils.waitForContainsText(ClusterOverviewPage.brokerCount(tcc.page()), Constants.REGULAR_BROKER_REPLICAS + "/" + Constants.REGULAR_BROKER_REPLICAS, true); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_TOTAL_AVAILABLE_KAFKA_COUNT, "2", true); - PwUtils.waitForLocatorAndClick(tcc, CssSelectors.PAGES_NAV_KAFKA_SELECT_BUTTON); + PwUtils.waitForContainsText(Masthead.totalAvailableKafkaCount(tcc.page()), "2", true); + PwUtils.waitForLocatorAndClick(Masthead.clusterSwitcherButton(tcc.page())); KroxyChecks.checkKafkaClusterDropdownContains(tcc, List.of(tcc.kafkaName(), tcc.virtualKafkaClusterName())); } diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/messages/MessagesST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/messages/MessagesST.java index 8f74f8193..6709ba426 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/messages/MessagesST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/messages/MessagesST.java @@ -10,8 +10,7 @@ import com.github.streamshub.systemtests.constants.TestTags; import com.github.streamshub.systemtests.enums.MessagesParameterType; import com.github.streamshub.systemtests.enums.MessagesWhereFilter; -import com.github.streamshub.systemtests.locators.CssSelectors; -import com.github.streamshub.systemtests.locators.MessagesPageSelectors; +import com.github.streamshub.systemtests.locators.pages.MessagesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaSetup; @@ -25,6 +24,8 @@ import com.github.streamshub.systemtests.utils.resourceutils.kafka.KafkaUtils; import com.github.streamshub.systemtests.utils.testchecks.MessagesChecks; import com.github.streamshub.systemtests.utils.testutils.MessagesTestUtils; +import com.microsoft.playwright.Locator; +import com.microsoft.playwright.Page; import io.skodjob.kubetest4j.resources.KubeResourceManager; import org.apache.kafka.common.security.auth.SecurityProtocol; import org.apache.logging.log4j.Logger; @@ -43,6 +44,7 @@ import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Map; +import java.util.function.Function; import java.util.stream.Stream; import static com.github.streamshub.systemtests.utils.Utils.getTestCaseConfig; @@ -92,24 +94,26 @@ public class MessagesST extends AbstractST { * </ul> */ public Stream<Arguments> searchUsingQueryScenarios() { + // Locator resolution is deferred via Function<Page, Locator> - this provider method runs during + // test discovery, before @BeforeAll sets up tcc's live Page, so a real Locator can't be built here yet. return Stream.of( Arguments.of(50, "", Map.of( - MessagesPageSelectors.getTableRowItems(1), VALUE_FILTER + " - 99", - MessagesPageSelectors.getTableRowItem(1, 1), "299")), + (Function<Page, Locator>) page -> MessagesPage.table(page).rows().nth(0), VALUE_FILTER + " - 99", + (Function<Page, Locator>) page -> MessagesPage.cellAt(page, 1, 1), "299")), Arguments.of(2, "messages=latest retrieve=2", Map.of( - MessagesPageSelectors.getTableRowItems(1), VALUE_FILTER + " - 99", - MessagesPageSelectors.getTableRowItems(2), VALUE_FILTER + " - 98")), + (Function<Page, Locator>) page -> MessagesPage.table(page).rows().nth(0), VALUE_FILTER + " - 99", + (Function<Page, Locator>) page -> MessagesPage.table(page).rows().nth(1), VALUE_FILTER + " - 98")), Arguments.of(20, "messages=offset:150 retrieve=20", Map.of( - MessagesPageSelectors.getTableRowItem(1, 5), HEADER_FILTER_MESSAGE + " - 50", - MessagesPageSelectors.getTableRowItem(1, 1), "150")), + (Function<Page, Locator>) page -> MessagesPage.cellAt(page, 1, 5), HEADER_FILTER_MESSAGE + " - 50", + (Function<Page, Locator>) page -> MessagesPage.cellAt(page, 1, 1), "150")), Arguments.of(1, "messages=offset:10 retrieve=100 " + KEY_FILTER_MESSAGE + " - 42", Map.of( - MessagesPageSelectors.getTableRowItem(1, 5), KEY_FILTER_MESSAGE + " - 42", - MessagesPageSelectors.getTableRowItem(1, 1), "42")), + (Function<Page, Locator>) page -> MessagesPage.cellAt(page, 1, 5), KEY_FILTER_MESSAGE + " - 42", + (Function<Page, Locator>) page -> MessagesPage.cellAt(page, 1, 1), "42")), Arguments.of(0, "messages=latest retrieve=40 " + KEY_FILTER_MESSAGE + " - 42", Map.of( - MessagesPageSelectors.MPS_SEARCH_RESULTS_TABLE_NO_DATA, "No messages data")), + (Function<Page, Locator>) MessagesPage::emptyBodyContent, "No messages data")), Arguments.of(50, "messages=totalyNotOkay retrieve=-9", Map.of( - MessagesPageSelectors.getTableRowItems(1), VALUE_FILTER + " - 99", - MessagesPageSelectors.getTableRowItems(2), VALUE_FILTER + " - 98")) + (Function<Page, Locator>) page -> MessagesPage.table(page).rows().nth(0), VALUE_FILTER + " - 99", + (Function<Page, Locator>) page -> MessagesPage.table(page).rows().nth(1), VALUE_FILTER + " - 98")) ); } @@ -152,30 +156,32 @@ public Stream<Arguments> searchUsingQueryScenarios() { * * @param expectedResults number of results expected to be displayed in the table * @param searchQuery the search query string to apply - * @param checks map of selectors to expected values used for content validation + * @param checks map of a locator-resolving function (deferred until a live {@link Page} exists, + * see {@link #searchUsingQueryScenarios()}) to expected values used for content validation */ @ParameterizedTest(name = "Query: {1}") @MethodSource("searchUsingQueryScenarios") @TestBucket(VARIOUS_MESSAGE_TYPES_BUCKET) - void testMessageSearchUsingQueries(int expectedResults, String searchQuery, Map<String, String> checks) { + void testMessageSearchUsingQueries(int expectedResults, String searchQuery, Map<Function<Page, Locator>, String> checks) { LOGGER.info("Running message search scenario on topic '{}' with query [{}], expecting {} result row(s)", kafkaTopicName, searchQuery, expectedResults); final String topicId = WaitUtils.waitForKafkaTopicToHaveIdAndReturn(tcc.namespace(), kafkaTopicName); PwUtils.navigate(tcc, PwPageUrls.getMessagesPage(tcc, tcc.kafkaName(), topicId)); LOGGER.debug("Waiting for message search page toolbar to be fully loaded before filtering messages"); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CONTENT_HEADER_TITLE_CONTENT, kafkaTopicName, true); - PwUtils.waitForLocatorVisible(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT); + PwUtils.waitForContainsText(MessagesPage.pageTitle(tcc.page()), kafkaTopicName, true); + PwUtils.waitForLocatorVisible(MessagesPage.searchInput(tcc.page())); LOGGER.info("Filling search query [{}] and awaiting {} result row(s)", searchQuery, expectedResults); - PwUtils.waitForLocatorAndFill(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT, searchQuery); - PwUtils.waitForLocatorAndClick(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_ENTER_BUTTON); - PwUtils.waitForLocatorCount(tcc, expectedResults, MessagesPageSelectors.MPS_SEARCH_RESULTS_TABLE_ITEMS, true); + PwUtils.waitForLocatorAndFill(MessagesPage.searchInput(tcc.page()), searchQuery); + PwUtils.waitForLocatorAndClick(MessagesPage.searchSubmitButton(tcc.page())); + PwUtils.waitForLocatorCount(expectedResults, MessagesPage.table(tcc.page()).rows(), true); LOGGER.info("Validating {} result check(s) for query [{}]", checks.size(), searchQuery); - checks.forEach((selector, expectedValue) -> { - LOGGER.debug("Checking selector [{}] contains expected value [{}]", selector, expectedValue); - PwUtils.waitForContainsText(tcc, selector, expectedValue, true); - assertTrue(tcc.page().locator(selector).allInnerTexts().toString().contains(expectedValue)); + checks.forEach((locatorFn, expectedValue) -> { + Locator locator = locatorFn.apply(tcc.page()); + LOGGER.debug("Checking locator [{}] contains expected value [{}]", locator, expectedValue); + PwUtils.waitForContainsText(locator, expectedValue, true); + assertTrue(locator.allInnerTexts().toString().contains(expectedValue)); }); } @@ -260,7 +266,7 @@ void testMessageFilteringByTimestamps() { LOGGER.info("Using topic '{}' with id '{}'", testTopic, topicId); PwUtils.navigate(tcc, PwPageUrls.getMessagesPage(tcc, tcc.kafkaName(), topicId)); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CONTENT_HEADER_TITLE_CONTENT, testTopic, true); + PwUtils.waitForContainsText(MessagesPage.pageTitle(tcc.page()), testTopic, true); LOGGER.info("Filtering messages using ISO query timestamp (current) - expect 0 new messages"); MessagesChecks.checkQueryBarFilter(tcc, TIMESTAMP_FILTER, Instant.now().atOffset(ZoneOffset.UTC).format(timestampFormatterQuery), 1, null); @@ -299,8 +305,8 @@ void testMessageFilteringByTimestamps() { // Verify via UI popover (ISO mode) PwUtils.navigate(tcc, PwPageUrls.getMessagesPage(tcc, tcc.kafkaName(), topicId)); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CONTENT_HEADER_TITLE_CONTENT, testTopic, true); - PwUtils.waitForLocatorVisible(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT); + PwUtils.waitForContainsText(MessagesPage.pageTitle(tcc.page()), testTopic, true); + PwUtils.waitForLocatorVisible(MessagesPage.searchInput(tcc.page())); LOGGER.info("Verifying timestamp filtering using UI popover (ISO mode) (current)"); MessagesChecks.checkPopoverIsoFilter(tcc, currentDateTimeForm, newMessageCount, newMessageText); @@ -360,19 +366,19 @@ void testFilterMessagesUsingUIForm() { PwUtils.navigate(tcc, PwPageUrls.getMessagesPage(tcc, tcc.kafkaName(), topicId)); LOGGER.debug("Waiting for page toolbar to be fully loaded before filtering"); - PwUtils.waitForContainsText(tcc, CssSelectors.PAGES_CONTENT_HEADER_TITLE_CONTENT, kafkaTopicName, true); - PwUtils.waitForLocatorVisible(tcc, MessagesPageSelectors.MPS_SEARCH_TOOLBAR_QUERY_INPUT); + PwUtils.waitForContainsText(MessagesPage.pageTitle(tcc.page()), kafkaTopicName, true); + PwUtils.waitForLocatorVisible(MessagesPage.searchInput(tcc.page())); LOGGER.info("Verifying default state: latest 50 messages shown, row 1 = offset 299, query = 'messages=latest retrieve=50'"); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.getTableRowItems(1), VALUE_FILTER + " - 99", true); - MessagesChecks.checkFilterResults(tcc, "messages=latest retrieve=50", 50, Map.of(MessagesPageSelectors.getTableRowItem(1, 1), "299")); + PwUtils.waitForContainsText(MessagesPage.table(tcc.page()).rows().nth(0), VALUE_FILTER + " - 99", true); + MessagesChecks.checkFilterResults(tcc, "messages=latest retrieve=50", 50, Map.of(MessagesPage.cellAt(tcc.page(), 1, 1), "299")); LOGGER.info("Filtering messages by key [{}] with no offset specified - expecting 'No messages data'", KEY_FILTER); MessagesTestUtils.openFilterForm(tcc); MessagesTestUtils.selectWhere(tcc, MessagesWhereFilter.KEY); MessagesTestUtils.fillHasWords(tcc, KEY_FILTER); MessagesTestUtils.search(tcc); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_SEARCH_RESULTS_TABLE_NO_DATA, "No messages data", true); + PwUtils.waitForContainsText(MessagesPage.emptyBodyContent(tcc.page()), "No messages data", true); // Take last messages of the first set and let it overlap with second set to see if it filters them out LOGGER.info("Setting offset to 95 and reapplying key filter [{}] - expecting 5 matching messages", KEY_FILTER); @@ -383,14 +389,14 @@ void testFilterMessagesUsingUIForm() { // Order is ASC LOGGER.debug("Verifying key-filtered messages: expecting 5 rows (offsets 95-99, ascending) matching key [{}]", KEY_FILTER); MessagesChecks.checkFilterResults(tcc, "messages=offset:95 retrieve=50 orderID where=key", 5, Map.of( - MessagesPageSelectors.getTableRowItem(1, 1), "95", - MessagesPageSelectors.getTableRowItem(1, 3), KEY_FILTER)); + MessagesPage.cellAt(tcc.page(), 1, 1), "95", + MessagesPage.cellAt(tcc.page(), 1, 3), KEY_FILTER)); LOGGER.debug("Resetting key filter - expecting fallback to default latest-50 view"); MessagesTestUtils.openFilterForm(tcc); MessagesTestUtils.resetFilters(tcc); // Order is DESC - MessagesChecks.checkFilterResults(tcc, "messages=latest retrieve=50", 50, Map.of(MessagesPageSelectors.getTableRowItem(1, 1), "299")); + MessagesChecks.checkFilterResults(tcc, "messages=latest retrieve=50", 50, Map.of(MessagesPage.cellAt(tcc.page(), 1, 1), "299")); LOGGER.info("Filtering messages by Headers lookup [{}] at offset 95 - expecting 45 matching messages", HEADER_FILTER_LOOK_UP_TEXT); MessagesTestUtils.openFilterForm(tcc); @@ -403,15 +409,15 @@ void testFilterMessagesUsingUIForm() { // Order is ASC LOGGER.debug("Verifying header-filtered messages: expecting 45 rows (offsets 100-144, ascending) starting with value [{} - 0]", HEADER_FILTER_MESSAGE); MessagesChecks.checkFilterResults(tcc, "messages=offset:95 retrieve=50 " + HEADER_FILTER_LOOK_UP_TEXT + " where=headers", 45, Map.of( - MessagesPageSelectors.getTableRowItem(1, 1), "100", - MessagesPageSelectors.getTableRowItem(1, 4), HEADER_FILTER_LOOK_UP_TEXT, - MessagesPageSelectors.getTableRowItem(1, 5), HEADER_FILTER_MESSAGE + " - 0")); + MessagesPage.cellAt(tcc.page(), 1, 1), "100", + MessagesPage.cellAt(tcc.page(), 1, 4), HEADER_FILTER_LOOK_UP_TEXT, + MessagesPage.cellAt(tcc.page(), 1, 5), HEADER_FILTER_MESSAGE + " - 0")); LOGGER.debug("Resetting header filter - expecting fallback to default latest-50 view"); MessagesTestUtils.openFilterForm(tcc); MessagesTestUtils.resetFilters(tcc); // Order is DESC - MessagesChecks.checkFilterResults(tcc, "messages=latest retrieve=50", 50, Map.of(MessagesPageSelectors.getTableRowItem(1, 1), "299")); + MessagesChecks.checkFilterResults(tcc, "messages=latest retrieve=50", 50, Map.of(MessagesPage.cellAt(tcc.page(), 1, 1), "299")); LOGGER.info("Filtering messages by Value [{}] at offset 195 - expecting 45 matching messages", VALUE_FILTER); MessagesTestUtils.openFilterForm(tcc); @@ -424,8 +430,8 @@ void testFilterMessagesUsingUIForm() { // Order is ASC LOGGER.debug("Verifying value-filtered messages: expecting 45 rows (offsets 200-244, ascending) starting with value [{} - 0]", VALUE_FILTER); MessagesChecks.checkFilterResults(tcc, "messages=offset:195 retrieve=50 " + VALUE_FILTER + " where=value", 45, Map.of( - MessagesPageSelectors.getTableRowItem(1, 1), "200", - MessagesPageSelectors.getTableRowItem(1, 5), VALUE_FILTER + " - 0")); + MessagesPage.cellAt(tcc.page(), 1, 1), "200", + MessagesPage.cellAt(tcc.page(), 1, 5), VALUE_FILTER + " - 0")); LOGGER.info("Completed popover filter-form scenarios (key, headers, value) on topic '{}'", kafkaTopicName); } diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/metrics/PrometheusST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/metrics/PrometheusST.java index 0985f3df9..9a713498f 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/metrics/PrometheusST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/metrics/PrometheusST.java @@ -8,7 +8,6 @@ import com.github.streamshub.systemtests.clients.KafkaClientsBuilder; import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.constants.TestTags; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.prometheus.PrometheusInstanceSetup; @@ -43,6 +42,14 @@ public class PrometheusST extends AbstractST { protected PrometheusOperatorSetup prometheusOperator; protected PrometheusInstanceSetup prometheusInstance; + // The overview page's charts (@patternfly/react-charts / Victory) render raw SVG text nodes with no + // data-testid/id/aria-label on individual axis labels - there is no semantic replacement available for + // these without a webui change (see design.md "Known webui gaps"). + private static final String DISK_SPACE_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(4) > div > div.pf-v6-c-chart > svg > g:nth-of-type(4) > text > tspan"; + private static final String CPU_USAGE_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(7) > div > div.pf-v6-c-chart > svg > g:nth-of-type(4) > text > tspan"; + private static final String MEMORY_USAGE_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(1) > div.pf-v6-l-flex > div:nth-of-type(2) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(10) > div > div.pf-v6-c-chart > svg > g:nth-of-type(4) > text > tspan"; + private static final String TOPIC_BYTES_CHART_NODE_TEXT_ITEMS = "body > div > div.pf-v6-c-page > div.pf-v6-c-page__main-container:nth-of-type(2) > main.pf-v6-c-page__main > div.pf-v6-c-drawer > div.pf-v6-c-drawer__main > div.pf-v6-c-drawer__content:nth-of-type(1) > div.pf-v6-c-drawer__body > section.pf-v6-c-page__main-section:nth-of-type(2) > div.pf-v6-c-page__main-body > div.pf-v6-l-grid > div.pf-v6-l-grid__item:nth-of-type(2) > div.pf-v6-l-flex > div:nth-of-type(3) > div.pf-v6-c-card > div.pf-v6-c-card__body:nth-of-type(2) > div.pf-v6-l-stack > div.pf-v6-l-stack__item:nth-of-type(3) > div > div > svg > g:nth-of-type(4) > text > tspan"; + /** * Tests that the console uses an externally provided (standalone) Prometheus instance for metrics * instead of deploying its own embedded one. @@ -79,23 +86,23 @@ void testCustomPrometheus() { LOGGER.info("Verifying disk space usage chart contains per-node series for 'Node 0' and 'Node 5'"); // Disk - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_DISK_SPACE_CHART_NODE_TEXT_ITEMS, "Node 0", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_DISK_SPACE_CHART_NODE_TEXT_ITEMS, "Node 5", true); + PwUtils.waitForContainsText(tcc, DISK_SPACE_CHART_NODE_TEXT_ITEMS, "Node 0", true); + PwUtils.waitForContainsText(tcc, DISK_SPACE_CHART_NODE_TEXT_ITEMS, "Node 5", true); LOGGER.info("Verifying CPU usage chart contains per-node series for 'Node 0' and 'Node 5'"); // CPU - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CPU_USAGE_CHART_NODE_TEXT_ITEMS, "Node 0", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_CPU_USAGE_CHART_NODE_TEXT_ITEMS, "Node 5", true); + PwUtils.waitForContainsText(tcc, CPU_USAGE_CHART_NODE_TEXT_ITEMS, "Node 0", true); + PwUtils.waitForContainsText(tcc, CPU_USAGE_CHART_NODE_TEXT_ITEMS, "Node 5", true); LOGGER.info("Verifying memory usage chart contains per-node series for 'Node 0' and 'Node 5'"); // Memory - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_MEMORY_USAGE_CHART_NODE_TEXT_ITEMS, "Node 0", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_MEMORY_USAGE_CHART_NODE_TEXT_ITEMS, "Node 5", true); + PwUtils.waitForContainsText(tcc, MEMORY_USAGE_CHART_NODE_TEXT_ITEMS, "Node 0", true); + PwUtils.waitForContainsText(tcc, MEMORY_USAGE_CHART_NODE_TEXT_ITEMS, "Node 5", true); LOGGER.info("Verifying topic bytes chart contains incoming and outgoing bytes series for topic '{}'", Constants.REPLICATED_TOPICS_PREFIX); // Topics - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPIC_BYTES_CHART_NODE_TEXT_ITEMS, "Incoming bytes", true); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.COPS_TOPIC_BYTES_CHART_NODE_TEXT_ITEMS, "Outgoing bytes", true); + PwUtils.waitForContainsText(tcc, TOPIC_BYTES_CHART_NODE_TEXT_ITEMS, "Incoming bytes", true); + PwUtils.waitForContainsText(tcc, TOPIC_BYTES_CHART_NODE_TEXT_ITEMS, "Outgoing bytes", true); } @BeforeAll diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/schema/ApicurioST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/schema/ApicurioST.java index 8ea90a4a6..72be33780 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/schema/ApicurioST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/schema/ApicurioST.java @@ -5,7 +5,7 @@ import com.github.streamshub.systemtests.clients.KafkaClients; import com.github.streamshub.systemtests.clients.KafkaClientsBuilder; import com.github.streamshub.systemtests.constants.Constants; -import com.github.streamshub.systemtests.locators.MessagesPageSelectors; +import com.github.streamshub.systemtests.locators.pages.MessagesPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.apicurio.ApicurioOperatorSetup; import com.github.streamshub.systemtests.setup.apicurio.ApicurioRegistry3Setup; @@ -205,20 +205,12 @@ void testApicurioSchemasWithExtraSchemaPage(String serializer, String schemaType PwUtils.navigate(tcc, PwPageUrls.getSingleTopicPage(tcc, tcc.kafkaName(), topicId)); - PwUtils.waitForLocatorAndClick(tcc, MessagesPageSelectors.getTableRowItem(1, 1)); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_MESSAGE_SIDEBAR_SCHEMA_NAME, schemaName, true); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_MESSAGE_SIDEBAR_VALUE_FORMAT, artifactType, true); - // TODO: removed? - // String schemaLink = tcc.page().locator(MessagesPageSelectors.MPS_MESSAGE_SIDEBAR_SCHEMA_LINK).getAttribute(CssBuilder.HREF); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_MESSAGE_SIDEBAR_SCHEMA_CODE, PwUtils.getTrimmedText(schema), true); + PwUtils.waitForLocatorAndClick(MessagesPage.cellAt(tcc.page(), 1, 1)); + PwUtils.waitForContainsText(MessagesPage.sidebarSchemaName(tcc.page()), schemaName, true); + PwUtils.waitForContainsText(MessagesPage.sidebarValueFormat(tcc.page()), artifactType, true); + PwUtils.waitForContainsText(MessagesPage.sidebarSchemaCode(tcc.page()), PwUtils.getTrimmedText(schema), true); - // TODO: removed? - // Content Id depends on how much artifacts are present in the registry - just like globalId LOGGER.info("Navigating to schema page to verify artifact content for {}", artifactId); - // PwUtils.navigate(tcc, PwPageUrls.getConsoleUrl(tcc) + schemaLink); - // - // PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_SCHEMA_PAGE_HEADER, schemaName, true); - // PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_SCHEMA_PAGE_CODE, PwUtils.getTrimmedText(schema), true); // Delete artifact LOGGER.info("Deleting artifact: {} from group: {}", artifactId, Constants.APICURIO_DEFAULT_GROUP); @@ -325,21 +317,9 @@ void testApicurioSchemasWithoutExtraSchemaPage(String serializer, String schemaT String topicId = WaitUtils.waitForKafkaTopicToHaveIdAndReturn(tcc.namespace(), topicName); PwUtils.navigate(tcc, PwPageUrls.getSingleTopicPage(tcc, tcc.kafkaName(), topicId)); - PwUtils.waitForLocatorAndClick(tcc, MessagesPageSelectors.getTableRowItem(1, 1)); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_MESSAGE_SIDEBAR_VALUE_FORMAT, valueFormat, true); - - // TODO: removed? - // String currentMessage = tcc.page().locator(MessagesPageSelectors.MPS_MESSAGE_SIDEBAR_SCHEMA_FORMATED_PLAIN) - // .getAttribute(Constants.VALUE_ATTRIBUTE) - // .replaceAll("\\s", "") - // .toLowerCase(Locale.ROOT); - // - // String expectedMessage = PwUtils.getTrimmedText(message) - // .replaceAll("\\s", "") - // .toLowerCase(Locale.ROOT); - // - // assertTrue(currentMessage.contains(expectedMessage)); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_MESSAGE_SIDEBAR_SCHEMA_CODE, PwUtils.getTrimmedText(message), false); + PwUtils.waitForLocatorAndClick(MessagesPage.cellAt(tcc.page(), 1, 1)); + PwUtils.waitForContainsText(MessagesPage.sidebarValueFormat(tcc.page()), valueFormat, true); + PwUtils.waitForContainsText(MessagesPage.sidebarSchemaCode(tcc.page()), PwUtils.getTrimmedText(message), false); // Delete artifact LOGGER.info("Deleting artifact: {} from group: {}", artifactId, Constants.APICURIO_DEFAULT_GROUP); diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/topics/TopicST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/topics/TopicST.java index 34052ef90..a29bbe8ea 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/topics/TopicST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/topics/TopicST.java @@ -11,10 +11,9 @@ import com.github.streamshub.systemtests.enums.FilterType; import com.github.streamshub.systemtests.enums.TopicStatus; import com.github.streamshub.systemtests.enums.TopicsPerPage; -import com.github.streamshub.systemtests.locators.ClusterOverviewPageSelectors; -import com.github.streamshub.systemtests.locators.CssBuilder; -import com.github.streamshub.systemtests.locators.MessagesPageSelectors; -import com.github.streamshub.systemtests.locators.TopicsPageSelectors; +import com.github.streamshub.systemtests.locators.pages.ClusterOverviewPage; +import com.github.streamshub.systemtests.locators.pages.MessagesPage; +import com.github.streamshub.systemtests.locators.pages.TopicsPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaSetup; @@ -95,15 +94,15 @@ void testPaginationWithManyTopics() { LOGGER.info("Verify top pagination navigation using page sizes {}", topicsPerPageList); TopicChecks.checkPaginationPage(tcc, TOTAL_TOPICS_COUNT, topicsPerPageList, - TopicsPageSelectors.TPS_TOP_PAGINATION_DROPDOWN_BUTTON, TopicsPageSelectors.TPS_PAGINATION_DROPDOWN_ITEMS, - TopicsPageSelectors.TPS_TOP_PAGINATION_DROPDOWN_BUTTON_TEXT, - TopicsPageSelectors.TPS_TOP_PAGINATION_NAV_PREV_BUTTON, TopicsPageSelectors.TPS_TOP_PAGINATION_NAV_NEXT_BUTTON); + TopicsPage.paginationDropdownButton(tcc.page(), true), TopicsPage.paginationDropdownItems(tcc.page()), + TopicsPage.paginationDropdownButton(tcc.page(), true), + TopicsPage.paginationPrevButton(tcc.page(), true), TopicsPage.paginationNextButton(tcc.page(), true)); LOGGER.info("Verify bottom pagination navigation using page sizes {}", topicsPerPageList); TopicChecks.checkPaginationPage(tcc, TOTAL_TOPICS_COUNT, topicsPerPageList, - TopicsPageSelectors.TPS_BOTTOM_PAGINATION_DROPDOWN_BUTTON, TopicsPageSelectors.TPS_PAGINATION_DROPDOWN_ITEMS, - TopicsPageSelectors.TPS_BOTTOM_PAGINATION_DROPDOWN_BUTTON_TEXT, - TopicsPageSelectors.TPS_BOTTOM_PAGINATION_NAV_PREV_BUTTON, TopicsPageSelectors.TPS_BOTTOM_PAGINATION_NAV_NEXT_BUTTON); + TopicsPage.paginationDropdownButton(tcc.page(), false), TopicsPage.paginationDropdownItems(tcc.page()), + TopicsPage.paginationDropdownButton(tcc.page(), false), + TopicsPage.paginationPrevButton(tcc.page(), false), TopicsPage.paginationNextButton(tcc.page(), false)); } /** @@ -182,7 +181,6 @@ void testSortTopics() { TopicChecks.checkTopicsPageTopicState(tcc, tcc.kafkaName(), TOTAL_TOPICS_COUNT, TOTAL_REPLICATED_TOPICS_COUNT, UNDER_REPLICATED_TOPICS_COUNT, UNAVAILABLE_TOPICS_COUNT); PwUtils.navigate(tcc, PwPageUrls.getTopicsPage(tcc, tcc.kafkaName())); - List<String> unavailableTopicsNames = ResourceUtils.listKubeResourcesByPrefix(KafkaTopic.class, tcc.namespace(), Constants.UNAVAILABLE_TOPICS_PREFIX).stream().map(kt -> kt.getMetadata().getName()).sorted().toList(); LOGGER.info("Verify sorting of offline topics by name in descending order"); @@ -190,15 +188,15 @@ void testSortTopics() { LOGGER.info("Filter topics by status {}", TopicStatus.OFFLINE.getName()); TopicsTestUtils.selectFilter(tcc, FilterType.STATUS); TopicsTestUtils.selectTopicStatus(tcc, TopicStatus.OFFLINE); - PwUtils.waitForLocatorCount(tcc, UNAVAILABLE_TOPICS_COUNT, TopicsPageSelectors.TPS_TABLE_ROWS, false); + PwUtils.waitForLocatorCount(UNAVAILABLE_TOPICS_COUNT, TopicsPage.table(tcc.page()).rows(), false); // Sort by name LOGGER.info("Sort {} offline topic(s) by name in descending order", UNAVAILABLE_TOPICS_COUNT); - TopicsTestUtils.selectSortBy(tcc, TopicsPageSelectors.TPS_TABLE_HEADER_SORT_BY_NAME, TopicsPageSelectors.TPS_TABLE_HEADER_SORT_BY_NAME_BUTTON, "descending"); - PwUtils.waitForLocatorCount(tcc, UNAVAILABLE_TOPICS_COUNT, TopicsPageSelectors.TPS_TABLE_ROWS, false); + TopicsTestUtils.selectSortBy(tcc, TopicsPage.sortByNameHeader(tcc.page()), "descending"); + PwUtils.waitForLocatorCount(UNAVAILABLE_TOPICS_COUNT, TopicsPage.table(tcc.page()).rows(), false); LOGGER.debug("Expecting topic '{}' to appear first after descending name sort", unavailableTopicsNames.get(UNAVAILABLE_TOPICS_COUNT - 1)); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.getTopicsTableRowItems(1), unavailableTopicsNames.get(UNAVAILABLE_TOPICS_COUNT - 1), true); + PwUtils.waitForContainsText(TopicsPage.table(tcc.page()).rows().nth(0), unavailableTopicsNames.get(UNAVAILABLE_TOPICS_COUNT - 1), true); - PwUtils.waitForLocatorAndClick(tcc, TopicsPageSelectors.TPS_TOP_TOOLBAR_SEARCH_CLEAR_ALL_FILTERS); + PwUtils.waitForLocatorAndClick(TopicsPage.clearAllFiltersButton(tcc.page())); LOGGER.info("Verify sorting of fully replicated topics by storage usage in descending order"); @@ -206,15 +204,15 @@ void testSortTopics() { LOGGER.info("Filter topics by status {}", TopicStatus.FULLY_REPLICATED.getName()); TopicsTestUtils.selectFilter(tcc, FilterType.STATUS); TopicsTestUtils.selectTopicStatus(tcc, TopicStatus.FULLY_REPLICATED); - PwUtils.waitForLocatorCount(tcc, Math.min(Constants.DEFAULT_TOPICS_PER_PAGE, TOTAL_REPLICATED_TOPICS_COUNT), TopicsPageSelectors.TPS_TABLE_ROWS, false); + PwUtils.waitForLocatorCount(Math.min(Constants.DEFAULT_TOPICS_PER_PAGE, TOTAL_REPLICATED_TOPICS_COUNT), TopicsPage.table(tcc.page()).rows(), false); // Sort by storage LOGGER.info("Sort {} fully replicated topic(s) by storage usage in descending order", Math.min(Constants.DEFAULT_TOPICS_PER_PAGE, TOTAL_REPLICATED_TOPICS_COUNT)); - TopicsTestUtils.selectSortBy(tcc, TopicsPageSelectors.TPS_TABLE_HEADER_SORT_BY_STORAGE, TopicsPageSelectors.TPS_TABLE_HEADER_SORT_BY_STORAGE_BUTTON, "descending"); - PwUtils.waitForLocatorCount(tcc, Math.min(Constants.DEFAULT_TOPICS_PER_PAGE, TOTAL_REPLICATED_TOPICS_COUNT), TopicsPageSelectors.TPS_TABLE_ROWS, false); + TopicsTestUtils.selectSortBy(tcc, TopicsPage.sortByStorageHeader(tcc.page()), "descending"); + PwUtils.waitForLocatorCount(Math.min(Constants.DEFAULT_TOPICS_PER_PAGE, TOTAL_REPLICATED_TOPICS_COUNT), TopicsPage.table(tcc.page()).rows(), false); // Last managed replicated has utilized more storage final String topicWithLargestStorageUsage = Constants.REPLICATED_TOPICS_PREFIX + "-" + (REPLICATED_TOPICS_COUNT - 1); LOGGER.debug("Expecting topic '{}' with the highest storage usage to appear first", topicWithLargestStorageUsage); - PwUtils.waitForContainsText(tcc, TopicsPageSelectors.getTopicsTableRowItem(1, 1), topicWithLargestStorageUsage, true); + PwUtils.waitForContainsText(TopicsPage.table(tcc.page()).cell(TopicsPage.table(tcc.page()).rows().nth(0), "Name"), topicWithLargestStorageUsage, true); } /** @@ -251,17 +249,17 @@ void testRecentlyViewedTopics() { String topicId = WaitUtils.waitForKafkaTopicToHaveIdAndReturn(tcc.namespace(), topicName); LOGGER.info("Visiting topic '{}' (id={}) messages page to mark it as recently viewed", topicName, topicId); PwUtils.navigate(tcc, PwPageUrls.getMessagesPage(tcc, tcc.kafkaName(), topicId), true, true); - PwUtils.waitForLocatorVisible(tcc, MessagesPageSelectors.MPS_EMPTY_BODY_CONTENT); - PwUtils.waitForContainsText(tcc, MessagesPageSelectors.MPS_EMPTY_BODY_CONTENT, "No messages data", false); + PwUtils.waitForLocatorVisible(MessagesPage.emptyBodyContent(tcc.page())); + PwUtils.waitForContainsText(MessagesPage.emptyBodyContent(tcc.page()), "No messages data", false); } LOGGER.info("Navigate to overview page to check the recently viewed card is not empty"); PwUtils.navigate(tcc, PwPageUrls.getOverviewPage(tcc, tcc.kafkaName())); LOGGER.info("Verify recently viewed topics card lists visited topics in reverse chronological order: {}", topicNames.reversed()); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.getTableRowItemLink(1), topicNames.get(2), false); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.getTableRowItemLink(2), topicNames.get(1), false); - PwUtils.waitForContainsText(tcc, ClusterOverviewPageSelectors.getTableRowItemLink(3), topicNames.get(0), false); + PwUtils.waitForContainsText(ClusterOverviewPage.recentTopicsListItems(tcc.page()).nth(0), topicNames.get(2), false); + PwUtils.waitForContainsText(ClusterOverviewPage.recentTopicsListItems(tcc.page()).nth(1), topicNames.get(1), false); + PwUtils.waitForContainsText(ClusterOverviewPage.recentTopicsListItems(tcc.page()).nth(2), topicNames.get(0), false); LOGGER.info("Delete {} topic(s), including the {} previously visited topic(s)", topics.size(), topicNames.size()); KubeResourceManager.get().deleteResourceWithWait(topics.toArray(new KafkaTopic[0])); @@ -269,9 +267,9 @@ void testRecentlyViewedTopics() { assertTrue(ResourceUtils.listKubeResourcesByPrefix(KafkaTopic.class, tcc.namespace(), KafkaNamingUtils.topicPrefixName(tcc.kafkaName())).isEmpty()); LOGGER.info("Verify recently viewed topics card still lists deleted topics: {}", topicNames.reversed()); - PwUtils.waitForContainsText(tcc, new CssBuilder(ClusterOverviewPageSelectors.COPS_RECENT_TOPICS_CARD_TABLE_ITEMS).nth(1).build(), topicNames.get(2), false); - PwUtils.waitForContainsText(tcc, new CssBuilder(ClusterOverviewPageSelectors.COPS_RECENT_TOPICS_CARD_TABLE_ITEMS).nth(2).build(), topicNames.get(1), false); - PwUtils.waitForContainsText(tcc, new CssBuilder(ClusterOverviewPageSelectors.COPS_RECENT_TOPICS_CARD_TABLE_ITEMS).nth(3).build(), topicNames.get(0), false); + PwUtils.waitForContainsText(ClusterOverviewPage.recentTopicsListItems(tcc.page()).nth(0), topicNames.get(2), false); + PwUtils.waitForContainsText(ClusterOverviewPage.recentTopicsListItems(tcc.page()).nth(1), topicNames.get(1), false); + PwUtils.waitForContainsText(ClusterOverviewPage.recentTopicsListItems(tcc.page()).nth(2), topicNames.get(0), false); } // ------ diff --git a/systemtests/src/test/java/com/github/streamshub/systemtests/users/UsersST.java b/systemtests/src/test/java/com/github/streamshub/systemtests/users/UsersST.java index 7bcb31d60..b10b3cb38 100644 --- a/systemtests/src/test/java/com/github/streamshub/systemtests/users/UsersST.java +++ b/systemtests/src/test/java/com/github/streamshub/systemtests/users/UsersST.java @@ -6,7 +6,7 @@ import com.github.streamshub.systemtests.annotations.TestBucket; import com.github.streamshub.systemtests.constants.Constants; import com.github.streamshub.systemtests.constants.TestTags; -import com.github.streamshub.systemtests.locators.KafkaUsersPageSelectors; +import com.github.streamshub.systemtests.locators.pages.KafkaUsersPage; import com.github.streamshub.systemtests.logs.LogWrapper; import com.github.streamshub.systemtests.setup.console.ConsoleInstanceSetup; import com.github.streamshub.systemtests.setup.strimzi.KafkaSetup; @@ -104,17 +104,17 @@ void testVariousKafkaUsersAcls(String userName) { // Verify navigation via url LOGGER.info("Navigating to single KafkaUser page for '{}'", userName); PwUtils.navigate(tcc, PwPageUrls.getSingleKafkaUserPage(tcc, tcc.kafkaName(), tcc.namespace(), userName)); - PwUtils.waitForContainsText(tcc, KafkaUsersPageSelectors.KUPS_KAFKA_USER_NAME_HEADER, userName, true); + PwUtils.waitForContainsText(KafkaUsersPage.userNameHeading(tcc.page()), userName, true); // Verify description list LOGGER.debug("Verifying description list (name, username, auth type, creation timestamp) for KafkaUser '{}'", userName); - PwUtils.waitForContainsText(tcc, KafkaUsersPageSelectors.KUPS_DESCRIPTION_NAME, userName, true); - PwUtils.waitForContainsText(tcc, KafkaUsersPageSelectors.KUPS_DESCRIPTION_USERNAME, userName, true); - PwUtils.waitForContainsText(tcc, KafkaUsersPageSelectors.KUPS_DESCRIPTION_AUTH, authenticationType, true); - PwUtils.waitForContainsText(tcc, KafkaUsersPageSelectors.KUPS_DESCRIPTION_CREATION_TIMESTAMP, creationTimestamp, true); + PwUtils.waitForContainsText(KafkaUsersPage.nameField(tcc.page()), userName, true); + PwUtils.waitForContainsText(KafkaUsersPage.usernameField(tcc.page()), userName, true); + PwUtils.waitForContainsText(KafkaUsersPage.authenticationField(tcc.page()), authenticationType, true); + PwUtils.waitForContainsText(KafkaUsersPage.creationTimeField(tcc.page()), creationTimestamp, true); List<AclRule> acls = ((KafkaUserAuthorizationSimple) kafkaUser.getSpec().getAuthorization()).getAcls(); - Locator rows = tcc.page().locator(KafkaUsersPageSelectors.KUPS_AUTHORIZATION_TABLE_ROWS); + Locator rows = KafkaUsersPage.authorizationTable(tcc.page()).rows(); LOGGER.info("Verifying authorization table for KafkaUser '{}' contains {} ACL row(s)", userName, acls.size()); assertEquals(acls.size(), rows.count(), "Mismatch between expected UI rows and actual KafkaUser ACL count");