Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
43 changes: 17 additions & 26 deletions frontend/e2e/clients/kubernetes-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,9 @@ export default class KubernetesClient {
const existing = await this.k8sApi.readNamespacedConfigMap({ name, namespace });
const existingData = (existing as any)?.data || {};
const mergedData = { ...existingData, ...patchData };
await this.mergePatchResource(
`/api/v1/namespaces/${namespace}/configmaps/${name}`,
{ data: mergedData },
);
await this.mergePatchResource(`/api/v1/namespaces/${namespace}/configmaps/${name}`, {
data: mergedData,
});
}

async createConfigMap(
Expand Down Expand Up @@ -455,21 +454,19 @@ export default class KubernetesClient {
namespace: string,
annotations: Record<string, string | null>,
): Promise<void> {
await this.mergePatchResource(
`/api/v1/namespaces/${namespace}/configmaps/${name}`,
{ metadata: { annotations } },
);
await this.mergePatchResource(`/api/v1/namespaces/${namespace}/configmaps/${name}`, {
metadata: { annotations },
});
}

async labelConfigMap(
name: string,
namespace: string,
labels: Record<string, string | null>,
): Promise<void> {
await this.mergePatchResource(
`/api/v1/namespaces/${namespace}/configmaps/${name}`,
{ metadata: { labels } },
);
await this.mergePatchResource(`/api/v1/namespaces/${namespace}/configmaps/${name}`, {
metadata: { labels },
});
}

async deleteConfigMap(name: string, namespace: string): Promise<void> {
Expand Down Expand Up @@ -640,11 +637,7 @@ export default class KubernetesClient {
}
}

async patchDeployment(
name: string,
namespace: string,
patch: object,
): Promise<unknown> {
async patchDeployment(name: string, namespace: string, patch: object): Promise<unknown> {
return this.appsApi.patchNamespacedDeployment({
name,
namespace,
Expand Down Expand Up @@ -687,7 +680,6 @@ export default class KubernetesClient {
});
}


async waitForDeploymentReady(
name: string,
namespace: string,
Expand All @@ -702,9 +694,7 @@ export default class KubernetesClient {
return (
status?.availableReplicas === desired &&
status?.updatedReplicas === desired &&
(status?.conditions ?? []).some(
(c) => c.type === 'Available' && c.status === 'True',
)
(status?.conditions ?? []).some((c) => c.type === 'Available' && c.status === 'True')
);
} catch {
return false;
Expand Down Expand Up @@ -748,9 +738,11 @@ export default class KubernetesClient {
const state = cs.state?.waiting
? `Waiting: ${cs.state.waiting.reason} - ${cs.state.waiting.message ?? ''}`
: cs.state?.terminated
? `Terminated: ${cs.state.terminated.reason}`
: 'Running';
lines.push(` container ${cs.name}: ready=${cs.ready}, restarts=${cs.restartCount}, ${state}`);
? `Terminated: ${cs.state.terminated.reason}`
: 'Running';
lines.push(
` container ${cs.name}: ready=${cs.ready}, restarts=${cs.restartCount}, ${state}`,
);
}
try {
const events = await this.k8sApi.listNamespacedEvent({
Expand All @@ -760,8 +752,7 @@ export default class KubernetesClient {
const recent = events.items
.sort(
(a, b) =>
new Date(b.lastTimestamp ?? 0).getTime() -
new Date(a.lastTimestamp ?? 0).getTime(),
new Date(b.lastTimestamp ?? 0).getTime() - new Date(a.lastTimestamp ?? 0).getTime(),
)
.slice(0, 10);
for (const ev of recent) {
Expand Down
4 changes: 3 additions & 1 deletion frontend/e2e/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
// happy path.
if (!recovering) {
// eslint-disable-next-line no-restricted-syntax -- waiting for state, no action follows
const authSettled = page.locator('html:not(.co-auth-pending)').waitFor({ state: 'attached', timeout: 30_000 });
const authSettled = page
.locator('html:not(.co-auth-pending)')
.waitFor({ state: 'attached', timeout: 30_000 });
const redirectedToLogin = page.waitForURL(OAUTH_REDIRECT_RE, { timeout: 30_000 });
await Promise.race([authSettled.catch(() => {}), redirectedToLogin.catch(() => {})]);
}
Expand Down
29 changes: 21 additions & 8 deletions frontend/e2e/mocks/operator-lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,16 @@ const activePhases = (): { name: string; startDate: string; endDate: string }[]
extendedStart.setDate(extendedStart.getDate() + 1);
const extendedEnd = new Date(now.getFullYear() + 3, 11, 31);
return [
{ name: 'Maintenance support', startDate: toDateStr(maintenanceStart), endDate: toDateStr(maintenanceEnd) },
{ name: 'Extended life cycle support', startDate: toDateStr(extendedStart), endDate: toDateStr(extendedEnd) },
{
name: 'Maintenance support',
startDate: toDateStr(maintenanceStart),
endDate: toDateStr(maintenanceEnd),
},
{
name: 'Extended life cycle support',
startDate: toDateStr(extendedStart),
endDate: toDateStr(extendedEnd),
},
];
};

Expand All @@ -33,8 +41,16 @@ const expiredPhases = (): { name: string; startDate: string; endDate: string }[]
extendedStart.setDate(extendedStart.getDate() + 1);
const extendedEnd = new Date(now.getFullYear() - 1, 11, 31);
return [
{ name: 'Maintenance support', startDate: toDateStr(maintenanceStart), endDate: toDateStr(maintenanceEnd) },
{ name: 'Extended life cycle support', startDate: toDateStr(extendedStart), endDate: toDateStr(extendedEnd) },
{
name: 'Maintenance support',
startDate: toDateStr(maintenanceStart),
endDate: toDateStr(maintenanceEnd),
},
{
name: 'Extended life cycle support',
startDate: toDateStr(extendedStart),
endDate: toDateStr(extendedEnd),
},
];
};

Expand Down Expand Up @@ -70,10 +86,7 @@ export const makeLifecycleSelfSupport = (
],
});

export const makeLifecycleIncompatible = (
packageName: string,
version: string,
): LifecycleData => ({
export const makeLifecycleIncompatible = (packageName: string, version: string): LifecycleData => ({
package: packageName,
schema: LIFECYCLE_SCHEMA,
versions: [
Expand Down
27 changes: 23 additions & 4 deletions frontend/e2e/mocks/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,18 @@ export const provisionersMap: Record<string, Parameter[]> = {
{ name: 'Availability zone', id: 'availability', values: 'lalitpur' },
],
'kubernetes.io/azure-file': [
{ name: 'SKU name', id: 'skuName', hintText: 'Azure storage account SKU tier', values: 'sample-name' },
{ name: 'Location', id: 'location', hintText: 'Azure storage account name', values: 'bhaktapur' },
{
name: 'SKU name',
id: 'skuName',
hintText: 'Azure storage account SKU tier',
values: 'sample-name',
},
{
name: 'Location',
id: 'location',
hintText: 'Azure storage account name',
values: 'bhaktapur',
},
{
name: 'Azure storage account name',
id: 'storageAccount',
Expand All @@ -115,7 +125,12 @@ export const provisionersMap: Record<string, Parameter[]> = {
},
],
'kubernetes.io/azure-disk': [
{ name: 'Storage account type', id: 'storageaccounttype', hintText: 'Storage account type', values: 'tester' },
{
name: 'Storage account type',
id: 'storageaccounttype',
hintText: 'Storage account type',
values: 'tester',
},
{ name: 'Account kind', id: 'kind', values: ['shared', 'dedicated', 'managed'] },
],
'kubernetes.io/quobyte': [
Expand All @@ -128,7 +143,11 @@ export const provisionersMap: Record<string, Parameter[]> = {
{ name: 'Quobyte tenant', id: 'quobyteTenant', values: 'tester' },
],
'kubernetes.io/vsphere-volume': [
{ name: 'Disk format', id: 'diskformat', values: ['thin', 'zeroed thick', 'eager zeroed thick'] },
{
name: 'Disk format',
id: 'diskformat',
values: ['thin', 'zeroed thick', 'eager zeroed thick'],
},
{ name: 'Datastore', id: 'datastore', values: 'store-thin' },
],
'kubernetes.io/portworx-volume': [
Expand Down
4 changes: 3 additions & 1 deletion frontend/e2e/pages/alertmanager-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ export function getGlobalsAndReceiverConfig(
} {
const parsed = yaml.load(yamlContent);
const config: AlertmanagerConfig =
typeof parsed === 'object' && parsed !== null ? (parsed as AlertmanagerConfig) : ({} as AlertmanagerConfig);
typeof parsed === 'object' && parsed !== null
? (parsed as AlertmanagerConfig)
: ({} as AlertmanagerConfig);
const receiver: AlertmanagerReceiver | undefined = config.receivers?.find(
(r) => r.name === receiverName,
);
Expand Down
10 changes: 4 additions & 6 deletions frontend/e2e/pages/base-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ export async function ensureDeveloperPerspective(
): Promise<boolean> {
const toggle = page.getByTestId('perspective-switcher-toggle');
await expect(toggle).toBeVisible();
const isSinglePerspective =
(await toggle.getAttribute('id')) === 'only-one-perspective';
const isSinglePerspective = (await toggle.getAttribute('id')) === 'only-one-perspective';
if (isSinglePerspective) {
await k8sClient.customObjectsApi.patchClusterCustomObject({
group: 'operator.openshift.io',
Expand Down Expand Up @@ -218,10 +217,9 @@ export default abstract class BasePage {
}

async waitForEditorReady(): Promise<void> {
await this.page.waitForFunction(
() => !!(window as any).monaco?.editor?.getModels()?.[0],
{ timeout: 30_000 },
);
await this.page.waitForFunction(() => !!(window as any).monaco?.editor?.getModels()?.[0], {
timeout: 30_000,
});
}

async getEditorContent(): Promise<string> {
Expand Down
38 changes: 25 additions & 13 deletions frontend/e2e/pages/cluster-dashboard-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,8 @@ export class ClusterDashboardPage extends BasePage {
private readonly utilizationItem = this.page.getByTestId('utilization-item');
private readonly utilizationItemTitle = this.page.getByTestId('utilization-item-title');
private readonly durationSelect = this.page.getByTestId('duration-select');
private readonly insightsHealthItem = this.page.locator(
'[data-item-id="Insights-health-item"]',
);
private readonly insightsButton = this.page
.getByTestId('Insights')
.locator('button');
private readonly insightsHealthItem = this.page.locator('[data-item-id="Insights-health-item"]');
private readonly insightsButton = this.page.getByTestId('Insights').locator('button');
private readonly popover = this.page.locator('.pf-v6-c-popover');

async navigateToDashboard(): Promise<void> {
Expand All @@ -32,9 +28,12 @@ export class ClusterDashboardPage extends BasePage {
async waitForStatusCardLoaded(): Promise<void> {
await expect(this.statusCard).toBeVisible({ timeout: 30_000 });
// eslint-disable-next-line no-restricted-syntax
await this.statusCard.locator('.skeleton-health').waitFor({ state: 'hidden', timeout: 30_000 }).catch(() => {
// Skeletons may have already disappeared
});
await this.statusCard
.locator('.skeleton-health')
.waitFor({ state: 'hidden', timeout: 30_000 })
.catch(() => {
// Skeletons may have already disappeared
});
}

getDetailsCard(): Locator {
Expand Down Expand Up @@ -107,10 +106,23 @@ export class ClusterDashboardPage extends BasePage {
const timeout = 30_000;
/* eslint-disable no-restricted-syntax */
const result = await Promise.race([
popover.getByText('Temporarily unavailable.').waitFor({ state: 'visible', timeout }).then(() => 'no-data' as const),
popover.getByText('Waiting for results.').waitFor({ state: 'visible', timeout }).then(() => 'no-data' as const),
popover.getByText('Disabled.').waitFor({ state: 'visible', timeout }).then(() => 'no-data' as const),
popover.locator('a[href*="console.redhat.com/openshift/insights/advisor"]').first().waitFor({ state: 'visible', timeout }).then(() => 'data' as const),
popover
.getByText('Temporarily unavailable.')
.waitFor({ state: 'visible', timeout })
.then(() => 'no-data' as const),
popover
.getByText('Waiting for results.')
.waitFor({ state: 'visible', timeout })
.then(() => 'no-data' as const),
popover
.getByText('Disabled.')
.waitFor({ state: 'visible', timeout })
.then(() => 'no-data' as const),
popover
.locator('a[href*="console.redhat.com/openshift/insights/advisor"]')
.first()
.waitFor({ state: 'visible', timeout })
.then(() => 'data' as const),
]).catch(() => 'no-data' as const);
/* eslint-enable no-restricted-syntax */
return result === 'data';
Expand Down
8 changes: 2 additions & 6 deletions frontend/e2e/pages/console-plugin-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,11 @@ export class ConsolePluginPage extends BasePage {
private readonly pfCodeEditor = this.page.locator('.pf-v6-c-code-editor');

async navigateToConsolePlugins(): Promise<void> {
await this.goTo(
'/k8s/cluster/operator.openshift.io~v1~Console/cluster/console-plugins',
);
await this.goTo('/k8s/cluster/operator.openshift.io~v1~Console/cluster/console-plugins');
}

async navigateToPluginDetails(pluginName: string): Promise<void> {
await this.goTo(
`/k8s/cluster/console.openshift.io~v1~ConsolePlugin/${pluginName}`,
);
await this.goTo(`/k8s/cluster/console.openshift.io~v1~ConsolePlugin/${pluginName}`);
}

async navigateToPluginManifest(pluginName: string): Promise<void> {
Expand Down
4 changes: 1 addition & 3 deletions frontend/e2e/pages/details-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ export class DetailsPage extends BasePage {
}

async confirmDelete(): Promise<void> {
await this.robustClick(
this.page.getByRole('button', { name: 'Delete', exact: true }),
);
await this.robustClick(this.page.getByRole('button', { name: 'Delete', exact: true }));
}
}
Loading