Skip to content
Closed
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
24 changes: 23 additions & 1 deletion workspaces/redhat-resource-optimization/app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,26 @@ integrations:
# target: 'https://example.com'
# changeOrigin: true

proxy:
endpoints:
'/cost-management/v1':
target: https://console.redhat.com/api/cost-management/v1
allowedHeaders: ['Authorization']
# See: https://backstage.io/docs/releases/v1.28.0/#breaking-proxy-backend-plugin-protected-by-default
credentials: dangerously-allow-unauthenticated

# Resource Optimization plugin configuration
# Replace `${RHHCC_SA_CLIENT_ID}` and `${RHHCC_SA_CLIENT_SECRET}` with the service account credentials.
resourceOptimization:
clientId: ${RHHCC_SA_CLIENT_ID}
clientSecret: ${RHHCC_SA_CLIENT_SECRET}
optimizationWorkflowId: 'patch-k8s-resource'

# Orchestrator plugin configuration
orchestrator:
dataIndexService:
url: http://localhost:8080

# Reference documentation http://backstage.io/docs/features/techdocs/configuration
# Note: After experimenting with basic setup, use CI/CD to generate docs
# and an external cloud storage when deploying TechDocs for production use-case.
Expand All @@ -78,7 +98,9 @@ auth:
# see https://backstage.io/docs/auth/ to learn about auth providers
providers:
# See https://backstage.io/docs/auth/guest/provider
guest: {}
guest:
# Enable guest authentication for testing
allowGuestAccess: true

scaffolder:
{}
Expand Down
10 changes: 9 additions & 1 deletion workspaces/redhat-resource-optimization/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@
"prettier:check": "prettier --check .",
"prettier:all": "prettier --write .",
"new": "backstage-cli new --scope @red-hat-developer-hub",
"postinstall": "cd ../../ && yarn install"
"postinstall": "cd ../../ && yarn install",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:headed": "playwright test --headed",
"test:e2e:debug": "playwright test --debug",
"test:e2e:chromium": "playwright test --project=chromium",
"test:e2e:firefox": "playwright test --project=firefox",
"test:e2e:webkit": "playwright test --project=webkit"
},
"workspaces": {
"packages": [
Expand All @@ -47,6 +54,7 @@
"@microsoft/api-extractor-model": "^7.29.2",
"@microsoft/tsdoc": "^0.15.0",
"@microsoft/tsdoc-config": "^0.17.0",
"@playwright/test": "1.55.1",
"@useoptic/optic": "^0.55.0",
"concurrently": "^9.0.0",
"knip": "^5.27.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Resource Optimization Plugin E2E Tests

This directory contains end-to-end tests for the Resource Optimization plugin using Playwright.

## Structure

```
e2e-tests/
├── fixtures/
│ └── optimizationResponses.ts # Mock data for API responses
├── pages/
│ └── ResourceOptimizationPage.ts # Page object for optimization UI
├── utils/
│ ├── devMode.ts # Mock utilities for development mode
│ └── apiUtils.ts # General API testing utilities
├── app.test.ts # Basic app functionality test
├── optimization.test.ts # Comprehensive optimization plugin tests
└── README.md # This file
```

## Mock Utilities

### Development Mode vs Production Mode

The tests automatically detect the environment:

- **Development Mode** (`!process.env.PLAYWRIGHT_URL`): Uses mocks for all API calls
- **Production Mode** (`process.env.PLAYWRIGHT_URL`): Uses real API endpoints

### Using Mock Utilities

```typescript
import {
setupOptimizationMocks,
mockOptimizationsResponse,
} from './utils/devMode';

test.beforeEach(async ({ page }) => {
if (devMode) {
// Setup all mocks at once
await setupOptimizationMocks(page);

// Or setup specific mocks
await mockOptimizationsResponse(page, customOptimizations);
}
});
```

### Available Mock Functions

#### `devMode.ts`

- `setupOptimizationMocks(page)` - Setup all mocks for basic testing
- `mockClustersResponse(page, clusters)` - Mock clusters API
- `mockOptimizationsResponse(page, optimizations, status)` - Mock optimizations API
- `mockEmptyOptimizationsResponse(page)` - Mock empty optimizations
- `mockWorkflowExecutionResponse(page, execution, status)` - Mock workflow execution
- `mockAuthTokenResponse(page, token)` - Mock authentication
- `mockAccessCheckResponse(page, hasAccess)` - Mock access check
- `mockAuthGuestRefreshResponse(page)` - Mock guest token refresh
- `mockPermissionResponse(page, hasPermission)` - Mock permission checks
- `mockCostManagementResponse(page, data)` - Mock cost management API
- `mockEmptyCostManagementResponse(page)` - Mock empty cost management data
- `mockCostManagementErrorResponse(page, status)` - Mock cost management errors

#### `apiUtils.ts`

- `waitUntilApiCallSucceeds(page, urlPart)` - Wait for API success
- `mockApiEndpoint(page, urlPattern, responseData, status)` - Generic API mock
- `mockApiError(page, urlPattern, errorMessage, status)` - Mock API errors
- `verifyApiCallMade(page, urlPattern, method)` - Verify API calls

## Page Objects

### ResourceOptimizationPage

Encapsulates all interactions with the optimization plugin UI:

```typescript
const optimizationPage = new ResourceOptimizationPage(page);

// Navigation
await optimizationPage.navigateToOptimization();

// Cluster selection
await optimizationPage.selectCluster('Production Cluster');

// View optimizations
await optimizationPage.viewOptimizations();

// Apply recommendations
await optimizationPage.applyRecommendation('opt-1');

// Verify states
await optimizationPage.verifyOptimizationDisplayed(optimization);
await optimizationPage.expectEmptyState();
await optimizationPage.expectErrorState();
```

## Test Data

### Mock Data Structure

The `fixtures/optimizationResponses.ts` file contains realistic mock data:

```typescript
export const mockOptimizations = [
{
id: 'opt-1',
clusterId: 'cluster-1',
workloadName: 'frontend-deployment',
resourceType: 'CPU',
currentValue: '2000m',
recommendedValue: '1000m',
savings: { cost: 45.5 },
status: 'pending',
severity: 'medium',
// ... more fields
},
// ... more optimizations
];
```

## Running Tests

### Local Development

```bash
# Run all tests
yarn test:e2e

# Run specific test file
yarn playwright test optimization.test.ts

# Run with UI
yarn test:e2e:ui

# Run in headed mode
yarn test:e2e:headed
```

### CI Environment

Tests automatically run in CI when changes are made to the optimization plugin workspace.

## Environment Variables

For production mode testing, set these environment variables:

```bash
export PLAYWRIGHT_URL=http://localhost:3000
export RHHCC_SA_CLIENT_ID=your-client-id
export RHHCC_SA_CLIENT_SECRET=your-client-secret
```

## Writing New Tests

1. **Use page objects** for UI interactions
2. **Mock API calls** in development mode
3. **Test both success and error scenarios**
4. **Validate accessibility** with proper ARIA labels
5. **Use descriptive test names** that explain the user journey

### Example Test Structure

```typescript
test('should handle optimization workflow', async ({ page }) => {
// Setup
if (devMode) {
await mockOptimizationsResponse(page, testOptimizations);
await mockWorkflowExecutionResponse(page, successExecution);
}

// Action
await optimizationPage.navigateToOptimization();
await optimizationPage.selectCluster('test-cluster');
await optimizationPage.viewOptimizations();
await optimizationPage.applyRecommendation('opt-1');

// Verification
await optimizationPage.expectWorkflowSuccess();
});
```

## Configuration

The plugin requires these configurations in `app-config.yaml`:

```yaml
proxy:
endpoints:
'/cost-management/v1':
target: https://console.redhat.com/api/cost-management/v1
allowedHeaders: ['Authorization']
credentials: dangerously-allow-unauthenticated

resourceOptimization:
clientId: ${RHHCC_SA_CLIENT_ID}
clientSecret: ${RHHCC_SA_CLIENT_SECRET}
optimizationWorkflowId: 'patch-k8s-resource'
```
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
*/

import { test, expect } from '@playwright/test';
import { performGuestLogin } from './fixtures/auth';

test('App should render the welcome page', async ({ page }) => {
await page.goto('/');

const enterButton = page.getByRole('button', { name: 'Enter' });
await expect(enterButton).toBeVisible();
await enterButton.click();
// Perform guest login - no mocks needed, real auth works fine
await performGuestLogin(page);

await expect(page.getByText('My Company Catalog')).toBeVisible();
// The app redirects to /catalog and shows the Red Hat Catalog heading
await expect(
page.getByRole('heading', { name: 'Red Hat Catalog' }),
).toBeVisible({ timeout: 10000 });
});
Loading