Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill migration-patterns명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | migration-patterns |
| description | > Use when this capability is needed. |
Guide for migrating test automation frameworks to Playwright with TypeScript, with special focus on incremental migration and code reusability.
┌─────────────────────────────────────────┐
│ 1. Pre-Migration Analysis │
│ - Review what's already migrated │
│ - Identify reusable components │
│ - Determine correct location │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 2. Migration │
│ - Convert selectors │
│ - Update assertions │
│ - Follow best practices │
└──────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 3. Post-Migration Review │
│ - Update migration log │
│ - Document reusable components │
│ - Verify business logic preserved │
└─────────────────────────────────────────┘
Review Migration Log
Plan Today's Migration
Day 1 - Foundation
// Migrated: create-prescription.spec.ts
// Created:
// - PrescriptionSearchPage (page-objects/pharmacy/)
// - PrescriptionFormPage (page-objects/pharmacy/)
// - createTestPrescription() (utils/pharmacy/)
Day 2 - Building On Day 1 ⚠️ CRITICAL: Reuse existing code!
// Migrating: refill-prescription.spec.ts
// ✅ BEFORE migrating, check:
// 1. Does PrescriptionSearchPage already exist? YES - REUSE IT!
// 2. Is there a prescription helper? YES - createTestPrescription() exists!
// 3. Where should this test live? /tests/pharmacy/prescription-flow/
// ✅ GOOD: Reuse existing code
import { PrescriptionSearchPage } from '../../page-objects/pharmacy/PrescriptionSearchPage';
import { createTestPrescription } from '../../utils/pharmacy/prescription-helpers';
test('refill prescription flow', async ({ page }) => {
// Reuse existing page object
const searchPage = new PrescriptionSearchPage(page);
// Reuse existing helper
const prescription = await createTestPrescription(page, {
medication: 'Lisinopril 10mg',
refillsRemaining: 3
});
await searchPage.navigate();
await searchPage.searchById(prescription.id);
await searchPage.clickRefill(prescription.id);
// Only create NEW page object if needed
const refillPage = new RefillConfirmationPage(page); // This is NEW
await refillPage.confirmRefill();
(page.()).();
});
❌ BAD: Creating duplicates
// Don't create PrescriptionSearchPageV2 just because you didn't check!
// Don't create createPrescription() when createTestPrescription() exists!
Before migrating ANY test, complete this checklist:
## Pre-Migration Checklist for [Test Name]
### 1. Code Reusability Analysis
- [ ] Searched /page-objects/ for existing page objects
- [ ] Searched /utils/ for existing helpers
- [ ] Reviewed yesterday's migration log
- [ ] Identified what can be REUSED vs what needs to be CREATED
### 2. Business Domain Identification
- [ ] Determined which domain this test belongs to (pharmacy, billing, etc.)
- [ ] Checked if similar tests exist in that domain folder
- [ ] Identified which user flow this test covers
### 3. Location Planning
- [ ] Determined correct folder: /tests/[domain]/[sub-category]/
- [ ] Checked for naming conflicts
- [ ] Verified folder structure matches our standards
### 4. Dependencies Identification
- [ ] Listed all pages this test touches
- [ ] Listed all API calls this test makes
- [ ] Listed all test data requirements
### 5. Business Logic Verification
- [ ] Understood the business purpose of this test
- [ ] Identified critical business rules being tested
- [ ] Noted any complex workflows or edge cases
# Search for similar page objects
grep -r "class.*Prescription.*Page" page-objects/
# Search for specific methods
grep -r "searchByMedication\|searchById" page-objects/
# Check templates/migration-tracker/MIGRATION-LOG.md
## January 30, 2026
### Migrated: create-prescription.spec.ts
**Business Domain:** Pharmacy > Prescription Management
**Created Files:**
- page-objects/pharmacy/PrescriptionSearchPage.ts
- page-objects/pharmacy/PrescriptionFormPage.ts
- utils/pharmacy/prescription-helpers.ts
**Reusable Components:**
- `PrescriptionSearchPage.searchByMedication(name: string)`
- `PrescriptionSearchPage.searchById(id: string)`
- `createTestPrescription(page, options)`
Prompt Template:
Using the migration-patterns skill:
Before migrating [test name], analyze our codebase:
1. Search /page-objects/pharmacy/ for existing prescription-related page objects
2. Search /utils/pharmacy/ for existing helpers
3. Review /tests/pharmacy/ for similar test flows
4. Identify what can be REUSED vs what needs to be CREATED
Then show me:
- List of existing components that can be reused
- List of new components that need to be created
- Recommended file locations
Does similar functionality exist?
│
├─ YES
│ └─ Can I extend the existing class/function?
│ ├─ YES → EXTEND existing code
│ └─ NO → Is it truly different?
│ ├─ YES → CREATE new, document why
│ └─ NO → REFACTOR existing to be reusable
│
└─ NO
└─ CREATE new code
// ✅ EXTEND existing page object when pages are similar
// page-objects/pharmacy/BasePrescriptionPage.ts
export class BasePrescriptionPage {
constructor(protected page: Page) {}
protected async selectMedication(name: string) {
await this.page.getByLabel('Medication').fill(name);
}
}
// page-objects/pharmacy/CreatePrescriptionPage.ts
export class CreatePrescriptionPage extends BasePrescriptionPage {
async create(medication: string, dosage: string) {
await this.selectMedication(medication); // Reused!
await this.page.getByLabel('Dosage').fill(dosage);
await this.page.getByRole('button', { name: }).();
}
}
{
() {
.(prescriptionId);
..(, { : }).();
}
}
tests/
├── pharmacy/
│ ├── prescription-flow/
│ │ ├── create-prescription.spec.ts (Day 1)
│ │ ├── refill-prescription.spec.ts (Day 2)
│ │ ├── cancel-prescription.spec.ts (Day 3)
│ │ └── transfer-prescription.spec.ts (Day 4)
│ ├── medication-search/
│ │ ├── search-by-name.spec.ts
│ │ └── search-by-ndc.spec.ts
│ └── inventory/
│ ├── stock-check.spec.ts
│ └── reorder-alerts.spec.ts
├── patient-portal/
│ ├── appointments/
│ │ ├── schedule-appointment.spec.ts
│ │ ├── cancel-appointment.spec.ts
│ │ └── reschedule-appointment.spec.ts
│ ├── medical-records/
│ │ ├── view-records.spec.ts
│ │ └── download-records.spec.ts
│ └── messaging/
│ ├── send-message.spec.ts
│ └── read-messages.spec.ts
└── billing/
├── payments/
│ ├── add-payment-method.spec.ts
│ ├── make-payment.spec.ts
│ └── view-payment-history.spec.ts
└── insurance/
├── add-insurance.spec.ts
└── verify-coverage.spec.ts
page-objects/
├── pharmacy/
│ ├── PrescriptionSearchPage.ts
│ ├── PrescriptionFormPage.ts
│ ├── RefillConfirmationPage.ts
│ └── MedicationDetailsPage.ts
├── patient-portal/
│ ├── AppointmentSchedulerPage.ts
│ ├── MedicalRecordsPage.ts
│ └── MessagingPage.ts
└── billing/
├── PaymentMethodsPage.ts
└── InsuranceFormPage.ts
utils/
├── pharmacy/
│ ├── prescription-helpers.ts
│ ├── medication-data.ts
│ └── pharmacy-api-helpers.ts
├── patient-portal/
│ ├── appointment-helpers.ts
│ └── patient-data.ts
└── billing/
├── payment-helpers.ts
└── insurance-helpers.ts
Ask these questions:
Example Decision Process:
Test: User refills a prescription
1. Business Domain: Pharmacy
→ /tests/pharmacy/
2. User Workflow: Prescription management flow
→ /tests/pharmacy/prescription-flow/
3. Specific Action: Refilling
→ /tests/pharmacy/prescription-flow/refill-prescription.spec.ts
// ❌ Puppeteer
await page.click('#submit-button');
await page.waitForSelector('.success-message');
// ✅ Playwright
await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByRole('alert')).toBeVisible();
// ❌ Puppeteer
await page.waitFor(2000); // Hard-coded wait
await page.waitForSelector('#loading', { hidden: true });
// ✅ Playwright
// Auto-waiting - no explicit wait needed
await page.getByRole('progressbar').waitFor({ state: 'hidden' });
// ❌ Puppeteer
await page.goto('https://example.com');
await page.waitForNavigation();
// ✅ Playwright
await page.goto('https://example.com');
// No need for waitForNavigation - goto waits automatically
// ❌ Puppeteer
const text = await page.$eval('.message', el => el.textContent);
expect(text).toBe('Success');
// ✅ Playwright
await expect(page.getByRole('status')).toHaveText('Success');
// ❌ Selenium
driver.findElement(By.id('email')).sendKeys('test@example.com');
driver.findElement(By.id('submit')).click();
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.className('success')));
// ✅ Playwright
await page.getByLabel('Email').fill('test@example.com');
await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByRole('alert')).toBeVisible();
Old Framework (Puppeteer):
await page.waitFor(3000); // Wait for data to load
const items = await page.$$('.prescription-item');
Playwright Solution:
// Wait for specific condition
await page.getByRole('progressbar').waitFor({ state: 'hidden' });
// Or wait for first item to appear
await page.getByRole('article').first().waitFor();
// Then get all items
const items = await page.getByRole('article').all();
Old Framework:
const fileInput = await page.$('input[type="file"]');
await fileInput.uploadFile('/path/to/file.pdf');
Playwright Solution:
await page.getByLabel('Upload prescription').setInputFiles('/path/to/file.pdf');
// Or for hidden inputs
await page.locator('input[type="file"]').setInputFiles('/path/to/file.pdf');
Old Framework (Puppeteer):
const newPagePromise = new Promise(resolve =>
browser.once('targetcreated', target => resolve(target.page()))
);
await page.click('a[target="_blank"]');
const newPage = await newPagePromise;
Playwright Solution:
const [newPage] = await Promise.all([
page.context().waitForEvent('page'),
page.getByRole('link', { name: 'Open in new tab' }).click()
]);
When migrating, document the business purpose:
// ❌ Bad - Lost business context
test('test prescription', async ({ page }) => {
await page.goto('/prescriptions');
await page.click('#refill-123');
// What business rule are we testing?
});
// ✅ Good - Business logic preserved
test('should allow refill only when prescription has remaining refills', async ({ page }) => {
// Business Rule: Users can refill prescriptions if refills remaining > 0
const prescription = await createTestPrescription(page, {
medication: 'Lisinopril 10mg',
refillsRemaining: 2 // Critical business data
});
const searchPage = new PrescriptionSearchPage(page);
await searchPage.navigate();
await searchPage.searchById(prescription.id);
// Verify refill button is enabled (business logic)
await expect(searchPage.getRefillButton(prescription.id)).toBeEnabled();
await searchPage.clickRefill(prescription.id);
// Verify business outcome
await expect(page.()).();
});
See templates/migration-tracker/MIGRATION-LOG-TEMPLATE.md for the complete template.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.