用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/opendatahub-io/odh-dashboard --skill mock-to-unit命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | mock-to-unit |
| description | Convert fine-grain Cypress mock tests to Jest unit tests based on CI execution time |
| trigger | /mock-to-unit |
Converts fine-grain UI Cypress mock tests to faster Jest unit tests, focusing on CI execution time savings.
CRITICAL RULE: Convert tests that assert RENDERED STATE ONLY with:
cy.wait('@alias') for network requests)Formula: NO interaction + NO waits = FINE-GRAIN UI = CONVERT
KEEP in Cypress (workflow tests):
DO NOT use file size — focus on actual CI execution time from GitHub Actions runs.
# Example GitHub Actions URL:
# https://github.com/opendatahub-io/odh-dashboard/actions/runs/29022853065
# Find the "Run Cypress mocked tests" job
# Look at execution times per file (e.g., "connections.cy.ts (15m47s)")
# Prioritize the TOP 3 LONGEST-running files
Remember: Cypress mock tests run in parallel — total CI time = longest job time, NOT sum of all jobs.
For each test in the target file, count interactions:
# Quick classification script
import re
def count_interactions(test_content):
# User interactions
clicks = len(re.findall(r'\.click\(', test_content))
types = len(re.findall(r'\.type\(', test_content))
selects = len(re.findall(r'\.select\(', test_content))
checks = len(re.findall(r'\.check\(', test_content))
clears = len(re.findall(r'\.clear\(', test_content))
submits = len(re.findall(r'\.submit\(', test_content))
triggers = len(re.findall(r'\.trigger\(', test_content))
# API/network interactions
waits = len(re.findall(r'cy\.wait\(', test_content))
visits = len(re.findall(r'cy\.visit\(', test_content))
requests = len(re.findall(r'cy\.request\(', test_content))
intercepts = len(re.findall(r'cy\.intercept\(', test_content))
total = (clicks + types + selects + checks + clears + submits + triggers +
waits + visits + requests + intercepts)
# Known non-interaction Cypress commands and chaining methods
KNOWN_CY = {
'get', 'findByTestId', 'findByRole', 'findByText', 'findByLabelText',
'findByPlaceholderText', 'findAllByTestId', 'findAllByRole', 'findAllByText',
'contains', 'find', , , , , , ,
, , , , , , , ,
, , , , , , , ,
, , , , , ,
}
KNOWN_CHAIN = {
, , , , , , , ,
, , , , ,
, , , ,
, , , , , , , ,
, , , , , ,
, , , , , ,
, , , , , , ,
, , ,
, , , , , , ,
}
cy_cmds = re.findall(, test_content)
chain_cmds = re.findall(, test_content)
has_unknown = (c KNOWN_CY c cy_cmds) \
(c KNOWN_CHAIN c chain_cmds)
has_unknown total == :
total == :
clicks == total == :
:
Expected conversion rate: 40-60% for typical files.
Before converting, check if existing Jest unit tests already cover the same behavior:
# Find existing unit tests for the component (repository-wide search)
# Search patterns:
# - __tests__/ directories (co-located or in packages)
# - Adjacent test files (*.spec.ts, *.spec.tsx, *.test.ts, *.test.tsx)
# - Use component name without restricting to specific file extensions
# Example: search for ConnectionsTable tests
find . -type f \( -name "*ConnectionsTable*.spec.*" -o -name "*ConnectionsTable*.test.*" \) \
-not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/.cache/*"
# Alternative: use grep to find files importing/testing the component
grep -rl "ConnectionsTable" --include="*.spec.*" --include="*.test.*" \
frontend/ packages/ distributions/
# Read the test file(s) and compare coverage
# If Jest tests already cover it → DELETE the Cypress test (it's a duplicate)
# If no coverage exists → CONVERT the Cypress test to Jest
Actions:
Target components:
Conversion pattern:
// Cypress mock test (connections.cy.ts)
it('Display project-scoped label for a notebook in workbenches table', () => {
initIntercepts();
projectDetails.visitSection('test-project', 'workbenches');
// Just checking rendered state - no interaction!
workbenchPage.findNotebookRow('test-notebook')
.findByTestId('project-scoped-label')
.should('exist');
});
// Converted Jest unit test (NotebookTableRow.spec.tsx)
it('should display project-scoped label when notebook uses project image', () => {
const notebook = mockNotebookK8sResource({
opts: {
metadata: {
annotations: {
'notebooks.opendatahub.io/last-image-selection': 'test-imagestream:1.2',
},
},
},
});
renderRow(notebook);
expect(screen.getByTestId('project-scoped-label')).toBeInTheDocument();
});
Key differences:
Note: Speedup varies by test complexity and system. Measured results show 100-1000x improvements for fine-grain UI tests. Always measure before/after on your specific tests.
ALWAYS use shared mock factories (from @odh-dashboard/internal/__mocks__ or type-owning packages):
import { mockNotebookK8sResource } from '#~/__mocks__/mockNotebookK8sResource';
import { mockNotebookState } from '#~/__mocks__/mockNotebookState';
import { mockProjectK8sResource } from '@odh-dashboard/k8s-core/__mocks__/mockProjectK8sResource';
const renderRow = (notebook = mockNotebookK8sResource({})) => {
const notebookState = mockNotebookState(notebook);
return render(
<MemoryRouter>
<ProjectDetailsContext.Provider value={mockContextValue}>
<table><NotebookTableRow obj={notebookState} /></table>
</ProjectDetailsContext.Provider>
</MemoryRouter>
);
};
Option A: If converted to Jest:
// CONVERTED TO JEST UNIT TESTS:
// Tests for table row display (project-scoped label, image display, hardware profile, status)
// are now in:
// frontend/src/pages/projects/screens/detail/notebooks/__tests__/NotebookTableRow.spec.tsx
//
// Removed tests:
// - "Display project-scoped label for a notebook in workbenches table" (line 1192)
// - Total: 1 test converted, now 6 Jest tests running in ~50ms (was ~19.5s in Cypress)
// it('Display project-scoped label...', () => { ... }) ← REMOVED
Option B: If duplicate of existing Jest test:
// CONVERTED TO JEST UNIT TESTS:
// These tests are covered by existing unit tests in:
// frontend/src/pages/projects/screens/detail/connections/__tests__/ConnectionsTable.spec.tsx
//
// Removed tests:
// - "Empty state when no data connections are available" (line 51)
// - "List connections" (line 57)
//
// The ConnectionsTable.spec.tsx already tests:
// - Rendering table with connections
// - Showing connection name, type, description
// - Showing connection type display name
// - Showing connected resources
// it('Empty state when no data connections are available', () => { ... }) ← REMOVED
// it('List connections', () => { ... }) ← REMOVED
# Run new Jest unit tests
npm run test -- NotebookTableRow.spec.tsx
# Run updated Cypress mock tests (verify remaining tests still pass)
npm run test:cypress-ci -- --spec "**/workbench.cy.ts"
# Lint everything
npm run lint:fix
# IMPORTANT: Committing and pushing requires explicit human approval
# After running the above verification steps:
# 1. Review all changes carefully
# 2. Ask the user if they want to commit and push
# 3. Only proceed if the user explicitly approves
# When approved, the user or developer can run:
# git add -A
# git commit -m "test: Convert fine-grain UI tests from Cypress to Jest"
# git push
# Measure CI time savings:
# - Before: Longest Cypress job time (e.g., 15m47s)
# - After: New longest Cypress job time
# - Savings: Difference in parallel execution time
File: workbench.cy.ts (14m50s → target for conversion)
| Metric | Before | After | Improvement |
|---|---|---|---|
| Cypress test time | ~19.5s | Removed | N/A |
| Jest test time | N/A | ~50ms | 1800x faster |
| Jest test count | N/A | 6 tests | Better coverage |
| Total CI time | 14m50s | TBD | Target: 30-60s reduction |
File: connections.cy.ts (15m47s → target for conversion)
| Metric | Before | After | Improvement |
|---|---|---|---|
| Duplicate tests | 2 Cypress | 0 (deleted) | Reduced redundancy |
| Existing coverage | Jest unit | Jest unit | Already covered |
# Analyze top CI bottleneck files
/mock-to-unit analyze
# Convert specific file
/mock-to-unit convert packages/cypress/cypress/tests/mocked/projects/tabs/workbench.cy.ts
Before committing:
@odh-dashboard/internal/__mocks__ or type-owning packages) for all mock data❌ Using file size instead of CI execution time — Size ≠ execution time
❌ Converting workflow tests — Tests with clicks/waits/API calls should stay in Cypress
❌ Not checking for duplicates — Remove redundant Cypress tests already covered by Jest
❌ Inline mock data — Always use shared mock factories (@odh-dashboard/internal/__mocks__ or type-owning packages)
❌ Forgetting to document removals — Add comments explaining what was converted/removed
✅ Focus on CI bottlenecks — Use actual GitHub Actions execution times
✅ Convert fine-grain UI only — NO interaction + NO waits = convert
✅ Check for duplicates first — Delete if Jest already covers it
✅ Measure before/after — Track actual CI time savings