Skip to content
Open
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
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
},
"metadata": {
"description": "Orchestrator skill for RHDH plugin development - onboard, update, and maintain plugins in the Extensions Catalog",
"version": "0.6.1"
"version": "0.7.0"
},
"plugins": [
{
"name": "rhdh",
"source": "./",
"description": "Skills for RHDH plugin lifecycle management",
"version": "0.6.1",
"version": "0.7.0",
"strict": true
}
]
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "rhdh",
"description": "All-in-one toolkit for Red Hat Developer Hub (RHDH). Covers plugin development, overlay management, environment setup, version compatibility, CI/CD, and RHDH ecosystem navigation.",
"version": "0.6.1",
"version": "0.7.0",
"author": {
"name": "RHDH Store Manager"
},
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ Track work across the four RHDH Jira projects.
- **[to-issue](./skills/rhdh-jira/references/to-issue.md)** — Create a Story, Task, Bug, or Spike with automatic type inference. Grills on implementation details and story points.
- **[update-jira-status](./skills/rhdh-jira/references/update-jira-status.md)** — Update an issue with session progress. Detects the related issue, adds a status comment, proposes transitions, and checks upward cascade to parent Epic/Feature.

### PR Workflow

Automate the full PR lifecycle — build, changeset, commit, push, and create — for plugin monorepos.

- **[raise-pr](./skills/raise-pr/SKILL.md)** — Full PR workflow for `rhdh-plugins` and `community-plugins`: detect workspace from staged changes, run build/validation, generate changesets, commit with sign-off, push, and create the GitHub PR. Auto-detects which repo you're in. Supports `--a` auto-approve mode to skip all approval gates. Accepts an optional Jira key or URL to link the PR — adds Web Link, comment, and transitions the issue to Review.

### Bug Fix

Reproduce, diagnose, fix, and PR RHDH plugin bugs from Jira tickets with automated Playwright-based before/after screen recordings.

- **[bug-fix](./skills/bug-fix/SKILL.md)** — End-to-end bug fix workflow: fetch Jira issue, map component to workspace, write Playwright reproduction test with video recording, diagnose root cause, apply fix, verify, and create PR with before/after recordings embedded. Chains into `raise-pr` for the full PR lifecycle including post-PR Jira updates.

### PR Review

- **[rhdh-pr-review](./skills/rhdh-pr-review/SKILL.md)** — PR code review with inline comments (GitHub, GitLab planned) and live cluster testing for rhdh-operator PRs. Layered architecture: fetch → analyze → post.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "rhdh-skill"
version = "0.6.1"
version = "0.7.0"
description = "Claude Code skill for RHDH plugin development"
readme = "README.md"
license = "Apache-2.0"
Expand Down
345 changes: 345 additions & 0 deletions skills/bug-fix/SKILL.md

Large diffs are not rendered by default.

196 changes: 196 additions & 0 deletions skills/bug-fix/references/e2e-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# E2E Patterns: Shared Playwright Infrastructure

Common patterns across all `rhdh-plugins` workspaces. Reference this when writing reproduction tests (Step 3).

## Shared Architecture

All workspaces with e2e tests follow the same structure:

```
workspaces/<workspace>/
├── playwright.config.ts # Multi-locale, dual-mode config
├── e2e-tests/
│ ├── *.test.ts # Test files
│ └── utils/
│ ├── translations.ts # i18n helper
│ ├── *Helpers.ts # Workspace-specific helpers
│ ├── accessibility.ts # a11y testing
│ └── localeSkip.ts # Locale skip logic
├── app-config.yaml # Base Backstage config
└── e2e-tests/test_yamls/ # Per-locale test configs
```

## Multi-Locale Setup

All workspaces use the same 6 locales:

```typescript
const LOCALES = ['en', 'de', 'es', 'fr', 'it', 'ja'] as const;
```

Each locale gets its own frontend and backend port:

```typescript
const FRONTEND_PORT_BASE = 3000; // en=3000, de=3001, es=3002, ...
const BACKEND_PORT_BASE = 7007; // en=7007, de=7008, es=7009, ...
```

**For reproduction tests**: always run against `en` only (fastest feedback):

```
APP_MODE=legacy npx playwright test e2e-tests/_repro-<KEY>.test.ts --project=en
```

## Dual-Mode (APP_MODE)

All workspaces support two frontend modes via the `APP_MODE` environment variable:

- `legacy` (default) — uses `packages/app-legacy` or `yarn start:legacy`
- `nfs` — uses the New Frontend System via `packages/app` or `yarn start`

The `playwright.config.ts` reads this:

```typescript
const appMode = process.env.APP_MODE || 'legacy';
const startCommand = appMode === 'legacy' ? 'yarn start:legacy' : 'yarn start';
```

**For reproduction**: run legacy first. After the fix, verify both modes.

## Translation Helpers

Most workspaces have a `getTranslations()` helper that loads the plugin's translation keys for the current locale. Use these for i18n-safe selectors:

```typescript
import { getTranslations, type InsightsMessages } from './utils/translations.js';

test.beforeAll(async ({ browser }) => {
const translations = getTranslations(locale);
// Use translations.header.title instead of hardcoded "Adoption Insights"
});
```

**Why**: hardcoded English strings break in non-`en` locales. Always use translation keys when available.

## Common Playwright Selectors

Prefer accessibility-first selectors:

```typescript
// Role-based (best)
page.getByRole('button', { name: 'Submit' })
page.getByRole('combobox')
page.getByRole('listbox')
page.getByRole('tab', { name: translations.tabs.overview })

// Text-based (good for translated text)
page.getByText(translations.header.dateRange.defaultLabel)

// Test ID (fallback)
page.getByTestId('date-range-select')

// CSS selector (last resort)
page.locator('.MuiSelect-root')
```

## Waiting Patterns

```typescript
// Wait for navigation to complete
await page.waitForURL('**/adoption-insights');

// Wait for network idle
await page.waitForLoadState('networkidle');

// Wait for specific element
await expect(page.getByRole('heading', { name: title })).toBeVisible();

// Custom wait for data flush
await waitForDataFlush(); // workspace-specific helper
```

## MUI Component Patterns

Many rhdh-plugins use Material-UI. Common interaction patterns:

```typescript
// MUI Select (dropdown)
const select = page.getByText(translations.header.dateRange.defaultLabel).first();
await select.click();
const listbox = page.getByRole('listbox');
await expect(listbox).toBeVisible();

// MUI Select with keyboard
await page.keyboard.press('ArrowDown');
const focused = listbox.locator(':focus');
await expect(focused).toHaveCount(1);
await page.keyboard.press('Enter');

// MUI Tab
await page.getByRole('tab', { name: 'Details' }).click();

// MUI Table
const rows = page.getByRole('row');
await expect(rows).toHaveCount(expectedCount);
```

## Repro Test Template

Use this skeleton for reproduction tests:

```typescript
import { test, expect, Page, BrowserContext } from '@playwright/test';

test.use({
video: { mode: 'on', size: { width: 1280, height: 720 } },
});

test.describe('Repro: <JIRA-KEY> — <short description>', () => {
let page: Page;
let context: BrowserContext;

test.beforeAll(async ({ browser }) => {
context = await browser.newContext();
page = await context.newPage();
// Navigate to the relevant page
});

test.afterAll(async () => {
await context.close();
});

test('<expected behavior that currently fails>', async () => {
// Steps to reproduce from Jira
// ...
// Assertion that should pass when the bug is fixed
});
});
```

## Running a Single Test

```bash
# Legacy mode, en locale only (fastest for reproduction)
APP_MODE=legacy npx playwright test e2e-tests/_repro-<KEY>.test.ts --project=en

# NFS mode (for verification after fix)
APP_MODE=nfs npx playwright test e2e-tests/_repro-<KEY>.test.ts --project=en

# With headed browser (for debugging)
APP_MODE=legacy npx playwright test e2e-tests/_repro-<KEY>.test.ts --project=en --headed

# With trace (for detailed debugging)
APP_MODE=legacy npx playwright test e2e-tests/_repro-<KEY>.test.ts --project=en --trace on
```

## Common yarn Scripts

All workspaces follow the same script naming:

```bash
yarn test:e2e:legacy # APP_MODE=legacy playwright test
yarn test:e2e:nfs # APP_MODE=nfs playwright test
yarn test:e2e:all # Run both modes
yarn tsc:full # Full TypeScript type check
yarn test --watchAll=false # Unit tests (no watch mode)
```
148 changes: 148 additions & 0 deletions skills/bug-fix/references/video-recording.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Video Recording: Playwright Capture & Conversion

How to capture before/after screen recordings for bug fix PRs. Reference this in Steps 3, 4, 6, and 7.

## Playwright Video Configuration

The reproduction test must **always create its own browser context** with `recordVideo` to guarantee video capture regardless of how the workspace's e2e infrastructure manages contexts:

```typescript
test('repro', async ({ browser }) => {
const context = await browser.newContext({
recordVideo: { dir: 'test-results/', size: { width: 1280, height: 720 } },
});
const page = await context.newPage();

// ... test steps ...

await context.close(); // finalizes the video file
});
```

- **`recordVideo.dir`** — directory where Playwright saves the `.webm` file.
- **`size`** — 1280x720 gives good quality at reasonable file size. Matches most laptop viewports.
- **`context.close()`** — MUST be called to finalize the video. Without it the file may be incomplete.

All rhdh-plugins workspaces use `@playwright/test` >= 1.60.0, which supports this config.

### Why not `test.use({ video: ... })`?

`test.use()` only applies to Playwright's auto-created contexts. Many rhdh-plugins workspaces (e.g., `lightspeed`) manually create contexts in `beforeAll` helpers, which bypasses `test.use()` entirely. By always creating our own context with `recordVideo`, we avoid this pitfall.

## Where Videos Land

Playwright saves videos to the `test-results/` directory inside the workspace:

```
workspaces/<workspace>/test-results/
└── <test-describe-title>-<test-title>-<browser>/
└── video.webm
```

The exact path depends on the test title. After running, find the video:

```bash
find test-results -name "video.webm" -type f
```

## Capturing Before/After Videos

### Before fix (Step 4)

After the reproduction test **fails** (bug is present):

```bash
mkdir -p e2e-tests/_repro-artifacts
cp test-results/*/video.webm e2e-tests/_repro-artifacts/before-fix.webm
```

### After fix (Step 6)

Clean the test results first, then re-run:

```bash
rm -rf test-results/
APP_MODE=legacy npx playwright test e2e-tests/_repro-<KEY>.test.ts --project=en
cp test-results/*/video.webm e2e-tests/_repro-artifacts/after-fix.webm
```

## Converting to GIF

GitHub PR descriptions support inline images (PNG, GIF, JPEG) but NOT inline `.webm` video. Convert to GIF for embedding.

### With ffmpeg (recommended)

```bash
ffmpeg -i e2e-tests/_repro-artifacts/before-fix.webm \
-vf "fps=10,scale=800:-1" -loop 0 \
e2e-tests/_repro-artifacts/before-fix.gif

ffmpeg -i e2e-tests/_repro-artifacts/after-fix.webm \
-vf "fps=10,scale=800:-1" -loop 0 \
e2e-tests/_repro-artifacts/after-fix.gif
```

Options explained:
- `fps=10` — 10 frames per second (balances smoothness vs file size)
- `scale=800:-1` — scale width to 800px, maintain aspect ratio
- `-loop 0` — loop the GIF infinitely

### Check if ffmpeg is available

```bash
which ffmpeg >/dev/null 2>&1 && echo "available" || echo "not found"
```

### Without ffmpeg (fallback)

If `ffmpeg` is not installed:

1. Keep the `.webm` files as-is.
2. After the PR is created, upload the `.webm` files as PR comment attachments.
3. Reference them in the PR body as download links rather than inline images.
4. Inform the user: "Install `ffmpeg` for inline GIF previews in PRs: `brew install ffmpeg` (macOS) or `sudo apt install ffmpeg` (Linux)."

## Embedding in PR Description

### Automated approach (preferred) — Upload to branch via GitHub Contents API

After `git push`, upload GIFs to `<workspace>/.github/screenshots/<before|after>-fix.gif` on the branch using the GitHub Contents API. This is handled automatically by `raise-pr` Step 10.2.

```bash
TOKEN=$(gh auth token)
GIF_B64=$(base64 -i e2e-tests/_repro-artifacts/before-fix.gif)
curl -s -X PUT \
-H "Authorization: token $TOKEN" \
-H "Accept: application/vnd.github+json" \
-d '{"message":"docs: add before-fix recording","content":"'"$GIF_B64"'","branch":"<branch-name>"}' \
"https://api.github.com/repos/<fork-owner>/<repo-name>/contents/<workspace>/.github/screenshots/before-fix.gif"
```

Extract the `download_url` from the JSON response — this is the `raw.githubusercontent.com` URL. Use it in the PR body:

```markdown
## UI before changes
![Before fix](https://raw.githubusercontent.com/<fork-owner>/<repo>/branch/.github/screenshots/before-fix.gif)

## UI after changes
![After fix](https://raw.githubusercontent.com/<fork-owner>/<repo>/branch/.github/screenshots/after-fix.gif)
```

The GIF files live on the feature branch only — they never reach `main`. When the PR merges and the branch is deleted, the files disappear. GitHub's camo proxy caches the rendered image permanently in the PR history.

### Fallback — Manual upload

If the Contents API upload fails (permissions error, file too large):

1. Create the PR with placeholder text for the image sections.
2. Inform the user to manually drag the GIF files into the PR description on GitHub's web UI.
3. GitHub uploads them to `user-images.githubusercontent.com` and generates permanent URLs.

## Cleanup

After the PR is created and images are uploaded, remove all temporary artifacts:

```bash
rm -rf e2e-tests/_repro-artifacts/
rm -rf test-results/
```
Loading
Loading