ios-workflow-to-playwright
npx skills add https://github.com/neonwatty/claude-skills --skill ios-workflow-to-playwright
Agent 安装分布
Skill 文档
iOS Workflow to Playwright Skill
You are a senior QA automation engineer. Your job is to translate human-readable iOS workflow markdown files into Playwright E2E test files that can run in CI using WebKit with mobile viewport emulation.
The Translation Pipeline
/workflows/ios-workflows.md â e2e/ios-mobile-workflows.spec.ts
(Human-readable) (Playwright WebKit mobile tests)
Important: WebKit vs iOS Simulator
What Playwright WebKit provides:
- Safari’s rendering engine (WebKit)
- Mobile viewport emulation
- Touch event simulation
- User agent spoofing
What Playwright WebKit cannot do (requires real iOS Simulator):
- Actual iOS Safari behavior (some quirks differ)
- Real device gestures (pinch-to-zoom physics)
- iOS system UI (permission dialogs, keyboards)
- Safe area inset testing on real notched devices
- Native app wrapper behavior (Capacitor, etc.)
Translation strategy: Generate tests that approximate iOS behavior in CI, while marking truly iOS-specific tests for the ios-workflow-executor skill.
When to Use This Skill
Use when:
- User has refined iOS workflows via
ios-workflow-executor - User wants to promote workflows to CI
- User says “convert ios workflows to CI”, “generate mobile tests”
Process
Phase 1: Read and Parse Workflows
- Read
/workflows/ios-workflows.md - If file doesn’t exist, inform user and stop
- Parse all workflows (each starts with
## Workflow:or### Workflow:) - For each workflow, extract:
- Name and description
- URL (if specified)
- Numbered steps and substeps
[MANUAL]tagged steps- iOS-specific steps (gestures, permissions, etc.)
Phase 2: Check for Existing Tests
- Look for existing
e2e/ios-mobile-workflows.spec.ts - If exists, parse to find which workflows are translated
- Determine diff:
- New workflows â Add
- Modified workflows â Update
- Removed workflows â Ask user
Phase 3: Explore Codebase for Selectors [DELEGATE TO AGENT]
Purpose: For each workflow step, explore the codebase to find reliable selectors with mobile-specific considerations. Delegate this to an Explore agent to save context.
Use the Task tool to spawn an Explore agent:
Task tool parameters:
- subagent_type: "Explore"
- model: "sonnet" (balance of speed and thoroughness)
- prompt: |
You are finding reliable Playwright selectors for iOS/mobile workflow steps.
These selectors will be used in WebKit mobile viewport tests.
## Workflows to Find Selectors For
[Include parsed workflow steps that need selectors]
## What to Search For
For each step, find the BEST available selector using this priority:
**Selector Priority (best to worst):**
1. data-testid="..." â Most stable
2. aria-label="..." â Accessible
3. role="..." + text â Semantic
4. .mobile-[component] â Mobile-specific classes
5. :has-text("...") â Text-based
6. Complex CSS path â Last resort
## Mobile-Specific Search Strategy
1. **Mobile Navigation Components**
- Search for bottom nav, tab bars: `bottom-nav`, `tab-bar`, `mobile-nav`
- Find mobile-specific layouts: `.mobile-only`, `@media` queries
- Look for touch-optimized components
2. **Touch Interaction Elements**
- Find touch-friendly button classes
- Locate gesture handlers (swipe, drag components)
- Identify long-press handlers
3. **iOS-Style Components**
- Search for iOS picker components
- Find action sheet / bottom sheet patterns
- Locate toggle switches vs checkboxes
4. **Responsive Breakpoints**
- Identify mobile breakpoint values
- Find conditionally rendered mobile components
## Return Format
Return a structured mapping:
```
## Selector Mapping (Mobile)
### Workflow: [Name]
| Step | Element Description | Recommended Selector | Confidence | Mobile Notes |
|------|---------------------|---------------------|------------|--------------|
| 1.1 | Bottom nav Guests tab | [data-testid="nav-guests"] | High | Mobile-only component |
| 1.2 | Guest list item | .guest-item | Medium | Needs .tap() not .click() |
| 2.1 | Action sheet | [role="dialog"].action-sheet | High | iOS-style sheet |
### Mobile-Specific Considerations
- Component X only renders on mobile viewport
- Gesture handler found in SwipeableList.tsx - may need approximation
### Ambiguous Selectors (need user input)
- Step 3.2: Found both mobile and desktop versions
### Missing Selectors (not found)
- Step 4.1: Could not find mobile-specific element
```
After agent returns: Use the selector mapping to generate accurate Playwright test code. Note mobile-specific considerations for each selector.
Phase 4: Map Actions to Playwright (Mobile)
| Workflow Language | Playwright Code |
|---|---|
| “Open Safari and navigate to [URL]” | await page.goto('URL') |
| “Tap [element]” | await page.locator(selector).tap() |
| “Long press [element]” | await page.locator(selector).click({ delay: 500 }) |
| “Type ‘[text]'” | await page.locator(selector).fill('text') |
| “Swipe up/down/left/right” | Custom swipe helper (see below) |
| “Pull to refresh” | Custom pull-to-refresh helper |
| “Pinch to zoom” | test.skip('Pinch gesture requires iOS Simulator') |
| “Verify [condition]” | await expect(...).toBe...(...) |
| “Wait for [element]” | await expect(locator).toBeVisible() |
| “[MANUAL] Grant permission” | test.skip('Permission dialogs require iOS Simulator') |
Swipe gesture helper:
async function swipe(
page: Page,
direction: 'up' | 'down' | 'left' | 'right',
options?: { startX?: number; startY?: number; distance?: number }
) {
const viewport = page.viewportSize()!;
const startX = options?.startX ?? viewport.width / 2;
const startY = options?.startY ?? viewport.height / 2;
const distance = options?.distance ?? 300;
const deltas = {
up: { x: 0, y: -distance },
down: { x: 0, y: distance },
left: { x: -distance, y: 0 },
right: { x: distance, y: 0 },
};
await page.mouse.move(startX, startY);
await page.mouse.down();
await page.mouse.move(startX + deltas[direction].x, startY + deltas[direction].y, { steps: 10 });
await page.mouse.up();
}
Phase 5: Handle iOS-Specific Steps
Many iOS workflow steps cannot be fully replicated in Playwright:
Translatable (approximate in WebKit):
- Basic taps and navigation
- Form input
- Scroll/swipe gestures
- Visual verification
- URL navigation
Not translatable (skip with note):
test.skip('Step N: [description]', async () => {
// iOS SIMULATOR ONLY: This step requires real iOS Simulator
// Original: "[step text]"
// Reason: [specific iOS feature needed]
// Test this via: ios-workflow-executor skill
});
iOS-only features:
- System permission dialogs (camera, location, notifications)
- iOS keyboard behavior (autocorrect, suggestions)
- Haptic feedback
- Face ID / Touch ID
- Safe area insets (real device only)
- iOS share sheet
- App Store interactions
Phase 6: Generate Test File [DELEGATE TO AGENT]
Purpose: Generate the Playwright WebKit mobile test file from the parsed workflows and selector mapping. Delegate to an agent for focused code generation.
Use the Task tool to spawn a code generation agent:
Task tool parameters:
- subagent_type: "general-purpose"
- model: "sonnet" (good balance for code generation)
- prompt: |
You are generating a Playwright E2E test file for iOS/mobile workflows.
These tests run in WebKit with mobile viewport emulation.
## Input Data
**Workflows:**
[Include parsed workflow data with names, steps, substeps]
**Selector Mapping:**
[Include selector mapping from Phase 3 agent]
**Existing Test File (if updating):**
[Include existing test content if this is an update, or "None - new file"]
## Your Task
Generate `e2e/ios-mobile-workflows.spec.ts` with:
1. **File header** explaining WebKit limitations vs real iOS
2. **Mobile viewport config** (iPhone 14: 393x852)
3. **WebKit + touch config** via test.use()
4. **Helper functions** (swipe, pullToRefresh)
5. **Test.describe block** for each workflow
6. **Individual tests** using .tap() for touch interactions
7. **test.skip** for iOS Simulator-only steps
## Mobile-Specific Requirements
- Use `.tap()` instead of `.click()` for touch interactions
- Use the swipe helper for swipe gestures
- Mark pinch/zoom as test.skip (iOS Simulator only)
- Mark permission dialogs as test.skip
- Add mobile user agent string
- Configure hasTouch: true
## Handle Special Cases
- [MANUAL] steps â `test.skip()` with explanation
- iOS-only gestures (pinch) â `test.skip()` with "iOS Simulator only" note
- Permission dialogs â `test.skip()` with "requires real iOS"
- Long press â `await element.click({ delay: 500 })`
## Return Format
Return the complete test file content ready to write.
Also return a summary:
```
## Generation Summary
- Workflows: [count]
- Total tests: [count]
- WebKit translatable: [count]
- iOS Simulator only: [count]
- Coverage: [percentage]% can run in CI
```
After agent returns: Write the generated test file to e2e/ios-mobile-workflows.spec.ts. Review coverage summary with user.
Create e2e/ios-mobile-workflows.spec.ts:
/**
* iOS Mobile Workflow Tests
*
* Auto-generated from /workflows/ios-workflows.md
* Generated: [timestamp]
*
* These tests run in Playwright WebKit with iPhone viewport.
* They approximate iOS Safari behavior but cannot fully replicate it.
*
* For full iOS testing, use the ios-workflow-executor skill
* with the actual iOS Simulator.
*
* To regenerate: Run ios-workflow-to-playwright skill
*/
import { test, expect, Page } from '@playwright/test';
// iPhone 14 viewport
const MOBILE_VIEWPORT = { width: 393, height: 852 };
// Configure for WebKit mobile
test.use({
viewport: MOBILE_VIEWPORT,
// Use WebKit for closest Safari approximation
browserName: 'webkit',
// Enable touch events
hasTouch: true,
// Mobile user agent
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
});
// ============================================================================
// HELPERS
// ============================================================================
async function swipe(
page: Page,
direction: 'up' | 'down' | 'left' | 'right',
options?: { startX?: number; startY?: number; distance?: number }
) {
const viewport = page.viewportSize()!;
const startX = options?.startX ?? viewport.width / 2;
const startY = options?.startY ?? viewport.height / 2;
const distance = options?.distance ?? 300;
const deltas = {
up: { x: 0, y: -distance },
down: { x: 0, y: distance },
left: { x: -distance, y: 0 },
right: { x: distance, y: 0 },
};
await page.mouse.move(startX, startY);
await page.mouse.down();
await page.mouse.move(
startX + deltas[direction].x,
startY + deltas[direction].y,
{ steps: 10 }
);
await page.mouse.up();
}
async function pullToRefresh(page: Page) {
await swipe(page, 'down', { startY: 150, distance: 400 });
}
// ============================================================================
// WORKFLOW: [Workflow Name]
// ============================================================================
test.describe('Workflow: [Name]', () => {
test.beforeEach(async ({ page }) => {
await page.goto('[base-url]');
});
test('Step 1: [Description]', async ({ page }) => {
// Substep: [description]
await page.locator('[selector]').tap();
// Substep: [description]
await expect(page.locator('[selector]')).toBeVisible();
});
test.skip('Step 2: [iOS Simulator Only]', async () => {
// iOS SIMULATOR ONLY: Permission dialog
// Test via: ios-workflow-executor
});
});
Phase 7: Playwright Config for WebKit Mobile
If WebKit mobile project doesn’t exist, suggest adding to playwright.config.ts:
// In playwright.config.ts projects array:
{
name: 'Mobile Safari',
use: {
...devices['iPhone 14'],
// Override to use WebKit
browserName: 'webkit',
},
},
Phase 8: Handle Updates (Diff Strategy)
Same as browser skill:
- Parse existing test file
- Compare with workflow markdown
- Add new, update changed, ask about removed
- Preserve
// CUSTOM:marked code
Phase 9: Review with User
Show translation summary:
iOS Workflows to translate: 5
Workflow: First-Time Onboarding
- 8 steps total
- 6 translatable to WebKit
- 2 iOS Simulator only (permission dialogs)
Workflow: Canvas Manipulation
- 9 steps total
- 7 translatable
- 2 need gesture approximation (pinch-to-zoom â skip)
Coverage: 72% of steps can run in CI
Remaining 28% require ios-workflow-executor for full testing
iOS-Specific Considerations
Viewport Sizes to Support
const IPHONE_SE = { width: 375, height: 667 };
const IPHONE_14 = { width: 393, height: 852 };
const IPHONE_14_PRO_MAX = { width: 430, height: 932 };
const IPAD_MINI = { width: 768, height: 1024 };
Touch vs Click
Always use .tap() instead of .click() for mobile tests:
// Preferred for mobile
await page.locator('button').tap();
// Fallback if tap doesn't work
await page.locator('button').click();
Handling Keyboard
Mobile keyboards behave differently:
// Fill and close keyboard
await page.locator('input').fill('text');
await page.keyboard.press('Enter'); // Dismiss keyboard
// Or tap outside to dismiss
await page.locator('body').tap({ position: { x: 10, y: 10 } });
Safe Area Handling
Cannot truly test safe areas, but can check CSS:
// Check that safe area CSS is present (informational)
const usesSafeArea = await page.evaluate(() => {
// Check for env(safe-area-inset-*) in styles
return document.documentElement.style.cssText.includes('safe-area');
});
Example Translation
iOS Workflow markdown:
## Workflow: Mobile Guest Assignment
> Tests assigning guests to tables on mobile Safari.
**URL:** http://localhost:5173/
1. Open app on mobile
- Open Safari and navigate to http://localhost:5173/
- Wait for app to load
- Verify mobile layout is active
2. Navigate to guest view
- Tap bottom nav "Guests" tab
- Verify guest list appears
3. Assign guest to table
- Long press on a guest name
- Drag to table (or tap assign button)
- Verify guest is assigned
4. [MANUAL] Test pinch-to-zoom on canvas
- This requires real iOS Simulator gesture testing
Generated Playwright:
test.describe('Workflow: Mobile Guest Assignment', () => {
test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:5173/');
await page.waitForLoadState('networkidle');
});
test('Step 1: Open app on mobile', async ({ page }) => {
// Substep: Wait for app to load
await expect(page.locator('[data-testid="app-container"]')).toBeVisible();
// Substep: Verify mobile layout is active
await expect(page.locator('.mobile-layout, .bottom-nav')).toBeVisible();
});
test('Step 2: Navigate to guest view', async ({ page }) => {
// Substep: Tap bottom nav "Guests" tab
await page.locator('.bottom-nav-item:has-text("Guests")').tap();
// Substep: Verify guest list appears
await expect(page.locator('[data-testid="guest-list"]')).toBeVisible();
});
test('Step 3: Assign guest to table', async ({ page }) => {
// Setup: Navigate to guests first
await page.locator('.bottom-nav-item:has-text("Guests")').tap();
await expect(page.locator('[data-testid="guest-list"]')).toBeVisible();
// Substep: Long press on a guest name
const guest = page.locator('.guest-item').first();
await guest.click({ delay: 500 }); // Long press approximation
// Substep: Tap assign button (drag not fully supported)
await page.locator('[data-testid="assign-btn"]').tap();
// Substep: Verify guest is assigned
await expect(page.locator('.guest-item.assigned')).toBeVisible();
});
test.skip('Step 4: [MANUAL] Test pinch-to-zoom on canvas', async () => {
// iOS SIMULATOR ONLY: Pinch gesture cannot be automated in Playwright
// Test via: ios-workflow-executor skill with actual iOS Simulator
// Original: "Test pinch-to-zoom on canvas"
});
});
Output Files
Primary output:
e2e/ios-mobile-workflows.spec.ts– Generated WebKit mobile tests
Optional outputs:
e2e/ios-mobile-workflows.selectors.ts– Extracted selectors.claude/ios-workflow-test-mapping.json– Diff tracking
Limitations to Communicate
Always inform user of what CI tests CANNOT cover:
â ï¸ CI Test Limitations (WebKit approximation):
These require ios-workflow-executor for real iOS Simulator testing:
- System permission dialogs
- Real iOS keyboard behavior
- Pinch/zoom gestures
- Safe area insets on notched devices
- iOS share sheet
- Face ID / Touch ID
- Safari-specific CSS quirks
CI tests cover: ~70-80% of typical iOS workflows
iOS Simulator covers: 100% (but requires manual/local execution)