Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions frontend/e2e/pages/base-page.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { type Locator, type Page, expect } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';

import { expect } from '../fixtures';
Comment thread
shahsahil264 marked this conversation as resolved.

export async function getEditorContent(page: Page): Promise<string> {
await page.waitForFunction(
Expand Down Expand Up @@ -166,14 +168,14 @@ export default abstract class BasePage {
Administrator: ['Administrator', 'Core platform'],
Developer: ['Developer'],
};
const toggle = this.page.locator('[data-test-id="perspective-switcher-toggle"]');
const toggle = this.page.getByTestId('perspective-switcher-toggle');
const labels = labelMap[target] || [target];
const currentText = (await toggle.textContent()) || '';
if (labels.some((label) => currentText.includes(label))) {
return;
}
await this.robustClick(toggle);
const menuOption = this.page.locator('[data-test-id="perspective-switcher-menu-option"]');
const menuOption = this.page.getByTestId('perspective-switcher-menu-option');
for (const label of labels) {
const option = menuOption.filter({ hasText: label });
if ((await option.count()) > 0) {
Expand Down
111 changes: 111 additions & 0 deletions frontend/e2e/pages/dev-console/vulnerability-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { Locator } from '@playwright/test';

import BasePage from '../base-page';

export class VulnerabilityPage extends BasePage {
private readonly vulnerabilitiesTab = this.page.getByTestId(
'horizontal-link-Vulnerabilities',
);
private readonly detailsTab = this.page.getByTestId('horizontal-link-Details');
private readonly yamlTab = this.page.getByTestId('horizontal-link-YAML');
private readonly affectedPodsTab = this.page.getByTestId('horizontal-link-Affected Pods');
private readonly vulnerabilityTable = this.page.locator(
'[aria-label="Image Manifest Vulnerabilities"]',
);
private readonly filterMenuToggle = this.page.getByTestId('console-select-menu-toggle');
private readonly vulnPopup = this.page.getByTestId('vul popup');
private readonly vulnAlertLink = this.page.getByTestId('Image Vulnerabilities');
private readonly viewAllLink = this.page.getByTestId('view-all');
private readonly vulnerabilityTypeSection = this.page.getByRole('group', {
name: 'Vulnerability type',
});

async navigateToProjectOverview(namespace: string): Promise<void> {
await this.goTo(`/project-details/ns/${namespace}`);
}

getVulnerabilitiesTab(): Locator {
return this.vulnerabilitiesTab;
}

getDetailsTab(): Locator {
return this.detailsTab;
}

getYamlTab(): Locator {
return this.yamlTab;
}

getAffectedPodsTab(): Locator {
return this.affectedPodsTab;
}

async clickVulnerabilitiesTab(): Promise<void> {
await this.robustClick(this.vulnerabilitiesTab);
}

getVulnerabilityTable(): Locator {
return this.vulnerabilityTable;
}

getFilterMenuToggle(): Locator {
return this.filterMenuToggle;
}

async clickFilterMenuToggle(): Promise<void> {
await this.robustClick(this.filterMenuToggle);
}

getVulnPopup(): Locator {
return this.vulnPopup;
}

getVulnAlertLink(): Locator {
return this.vulnAlertLink;
}

async clickVulnAlert(): Promise<void> {
await this.robustClick(this.vulnAlertLink);
}

async clickViewAll(): Promise<void> {
await this.robustClick(this.viewAllLink);
}

getVulnLink(index: number): Locator {
return this.page.getByTestId(`vuln-${index}`);
}

async clickVulnLink(index: number): Promise<void> {
await this.robustClick(this.getVulnLink(index));
}

getVulnImageLink(imageName: string): Locator {
return this.page.getByRole('link', { name: imageName });
}

async clickVulnImageLink(imageName: string): Promise<void> {
await this.robustClick(this.getVulnImageLink(imageName));
}

getVulnerabilityTypeSection(): Locator {
return this.vulnerabilityTypeSection;
}

getVulnerabilityTypeButton(typeName: string): Locator {
return this.page.getByRole('button', { name: typeName, exact: true });
}

async clickVulnerabilityType(typeName: string): Promise<void> {
await this.robustClick(this.getVulnerabilityTypeButton(typeName));
}

getSectionHeading(text: string): Locator {
return this.page.locator(`[data-test-section-heading="${text}"]`);
Comment thread
shahsahil264 marked this conversation as resolved.
Outdated
}

async selectFilterOption(optionId: string): Promise<void> {
const option = this.page.getByTestId('console-select-item').filter({ hasText: optionId });
await this.robustClick(option);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
apiVersion: apps/v1
Comment thread
shahsahil264 marked this conversation as resolved.
Outdated
kind: Deployment
metadata:
name: test-vulnerability
Comment thread
shahsahil264 marked this conversation as resolved.
Outdated
namespace: aut-image-vulnerability
spec:
selector:
matchLabels:
app: quarkus
replicas: 1
template:
metadata:
labels:
app: quarkus
spec:
automountServiceAccountToken: false
containers:
- name: quarkus
image: >-
quay.io/redhat-appstudio-qe/quarkus:7dd062e2e8cb4ba599185e48d628b65a
command: ["sleep", "infinity"]
ports:
- containerPort: 8080
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 128Mi
livenessProbe:
exec:
command: ["true"]
periodSeconds: 30
readinessProbe:
exec:
command: ["true"]
periodSeconds: 10
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
59 changes: 59 additions & 0 deletions frontend/e2e/tests/dev-console/utils/operator-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import KubernetesClient from '../../../clients/kubernetes-client';
Comment thread
shahsahil264 marked this conversation as resolved.
Outdated

const SUBSCRIPTION_GROUP = 'operators.coreos.com';
const SUBSCRIPTION_VERSION = 'v1alpha1';
const SUBSCRIPTION_PLURAL = 'subscriptions';

/**
* Check whether an OLM operator subscription exists in the cluster.
* Searches the given namespace (defaults to openshift-operators) for a
* Subscription whose metadata.name matches `subscriptionName`.
*/
export async function hasOperatorSubscription(
k8sClient: KubernetesClient,
subscriptionName: string,
namespace = 'openshift-operators',
): Promise<boolean> {
try {
await k8sClient.getCustomResource(
SUBSCRIPTION_GROUP,
SUBSCRIPTION_VERSION,
namespace,
SUBSCRIPTION_PLURAL,
subscriptionName,
);
return true;
} catch (err) {
const code = (err as any).statusCode ?? (err as any).response?.statusCode;
if (code === 404) {
return false;
}
throw err;
}
}

/**
* Check whether a ClusterServiceVersion whose name starts with
* `csvPrefix` exists in the given namespace and has phase "Succeeded".
*/
export async function hasCSVReady(
k8sClient: KubernetesClient,
csvPrefix: string,
namespace = 'openshift-operators',
): Promise<boolean> {
try {
const csvs = await k8sClient.listCustomResources(
SUBSCRIPTION_GROUP,
SUBSCRIPTION_VERSION,
namespace,
'clusterserviceversions',
);
return csvs.some(
(csv) =>
(csv as any).metadata?.name?.startsWith(csvPrefix) &&
(csv as any).status?.phase === 'Succeeded',
);
} catch {
return false;
}
}
Loading