AI-powered Selenium to Playwright migration tool
Project description
QAMigrate
AI-powered test framework migration
A QATonic Innovations product.
QAMigrate converts test suites between frameworks automatically. It parses your code with Tree-sitter, generates equivalent code with Claude or GPT-4, and produces a full migration report with per-file diffs, confidence scores, and estimated time savings.
Supported today (v0.4.x)
Selenium (Java) -> Playwright (Java) only.
- Source language: Java 11+ with Selenium 3/4, TestNG or JUnit 4/5, Maven or Gradle
- Target language: Java 17+ with Playwright for Java, JUnit 5, same build tool
- LLM providers: Anthropic (default) or OpenAI
Roadmap (not yet shipped)
- v0.5.x: Python Selenium -> Python Playwright (pytest)
- v0.5.x+: JavaScript/TypeScript Selenium -> Playwright
- v0.6.x+: Cypress -> Playwright, RestAssured -> Karate
If you have a Selenium project in Python/JS/C#, QAMigrate will NOT migrate it today. Follow the GitHub repo for when support lands.
Why QAMigrate
You don't just get generated code. You get evidence:
- Per-file diffs -- every pattern we touched, why, and side-by-side before/after
- Confidence score -- how sure we are about each file, 0 to 100
- Batch report -- HTML, JSON, and Markdown; share with your team
- Time-saved estimate -- how many hours of manual work you avoided
- Dry-run mode -- preview what would change without calling the LLM
What it does
Give it a Selenium file:
// Selenium
@FindBy(id = "username")
private WebElement usernameInput;
public void login(String user, String pass) {
usernameInput.clear();
usernameInput.sendKeys(user);
passwordInput.sendKeys(pass);
wait.until(ExpectedConditions.elementToBeClickable(loginButton));
loginButton.click();
}
Get Playwright back:
// Playwright (generated by QAMigrate)
private final Locator usernameInput;
public LoginPage(Page page) {
this.usernameInput = page.locator("#username");
}
public void login(String user, String pass) {
usernameInput.fill(user);
passwordInput.fill(pass);
loginButton.click(); // Playwright auto-waits
}
Supported patterns (Selenium -> Playwright Java)
@FindByannotations (id, css, xpath, className, name, linkText)PageFactory.initElementsremovalWebDriverWait/ExpectedConditionsremoval (Playwright auto-waits)sendKeys->fill,clearremoval,clickdirect conversionActions(hover, drag) -> Playwright equivalentsSelectdropdowns ->selectOptionJavascriptExecutor->page.evaluate()- Alert / iframe handling
- File uploads ->
setInputFiles - TestNG -> JUnit 5 lifecycle (
@BeforeMethod->@BeforeEach, etc.) @DataProvider->@MethodSourceAssert.assertEquals/assertTrue-> Playwright assertionsChromeDriversetup ->Playwright.create()+browser.newContext()- Base class inheritance preservation
Quick start
Prerequisites
- Python 3.11+
- Java 17+ (for compilation validation; optional — see
compiler.skip) - One of: Anthropic API key or OpenAI API key
Install
pip install qamigrate
That's it. Both the Anthropic and OpenAI SDKs are included; you pick which provider to use at runtime with --provider or in qamigrate.yaml.
Configure
Set one API key (the one for the provider you'll use). If you plan to switch between them, set both.
echo "ANTHROPIC_API_KEY=sk-ant-your-key" > .env
# and/or
echo "OPENAI_API_KEY=sk-your-openai-key" >> .env
Initialize (once per project)
# Auto-injects Playwright + JUnit 5 into your pom.xml so compile-check works.
qamigrate init --project ./my-selenium-project --yes
Migrate
# Dry-run: see what would happen without calling the LLM
qamigrate migrate --all --project ./my-selenium-project --dry-run
# Migrate a single file
qamigrate migrate src/test/java/LoginPage.java --project ./my-selenium-project
# Migrate the whole project with batch compile + report (recommended)
qamigrate migrate --all --project ./my-selenium-project --report ./qamigrate-report
Output goes to playwright/ subdirectories next to each original file.
If you use --report <dir>, you get report.html, report.json, and report.md.
Choose your LLM provider
v0.4.0 supports both Anthropic and OpenAI. You can switch providers per-run without editing the config file:
# Use OpenAI for everything
qamigrate migrate --all --project ./p --provider openai --model gpt-4o
# Use Claude for code generation (reasoning-heavy), GPT-4o-mini for
# compile-error cleanup (cheaper and faster):
qamigrate migrate --all --project ./p \
--coder-provider anthropic --coder-model claude-sonnet-4-20250514 \
--fixer-provider openai --fixer-model gpt-4o-mini
Or pin it in qamigrate.yaml:
coder:
provider: anthropic
model: claude-sonnet-4-20250514
fixer:
provider: openai
model: gpt-4o-mini
Example report summary
QAMigrate - Migrating 7 file(s)
-> [1/7] BaseTest.java
OK src/.../playwright/BaseTest.java (14.1s, 75% confidence)
-> [2/7] LoginPage.java
OK src/.../playwright/LoginPage.java (17.2s, 75% confidence)
...
Files 7 / 7
Patterns handled 149
Avg confidence 75%
Time 105.3s
Est. hours saved ~37.2
Report written:
HTML: ./qamigrate-report/report.html
JSON: ./qamigrate-report/report.json
Markdown: ./qamigrate-report/report.md
CLI commands
| Command | Description |
|---|---|
qamigrate init |
Initialize QAMigrate in a project |
qamigrate scan |
Analyze codebase, detect patterns, show complexity |
qamigrate analyze |
NEW in v0.5 -- build a full architecture report (roles, conventions, infra, dependency graph) WITHOUT calling the LLM |
qamigrate migrate |
Convert files to the target framework (now context-aware: sees the whole project's conventions and siblings) |
Common flags for migrate:
<file>— migrate a single file--all, -a— migrate all Java files undersrc/testorsrc/--dry-run— analyze only, no LLM calls, no writes--report <dir>— write HTML + JSON + Markdown report--project, -p <path>— project root (default: current dir)--no-batch-compile— fall back to the v0.2 per-file compile loop
LLM provider flags (v0.4+):
--provider <name>— LLM provider for both agents (anthropic|openai)--model <id>— model for both agents (e.g.gpt-4o,claude-sonnet-4-20250514)--coder-provider <name>/--coder-model <id>— override Coder only--fixer-provider <name>/--fixer-model <id>— override Fixer only
Flags on init:
--yes, -y— auto-inject Playwright + JUnit 5 deps into pom.xml / build.gradle without prompting
How it works
Scanner (Tree-sitter) Deterministic AST parsing, pattern detection
|
Coder (LLM) Generates Playwright Java via structured output
| (Anthropic or OpenAI, pluggable per agent)
|
Compiler (javac) Validates generated code compiles
|
Fixer (rules + LLM) Fixes compilation errors, retries up to 3x
|
Report Records every file, pattern, confidence, diff
Plain Python — no LangChain, no graph framework, no agents-of-agents. Each step is a clean module you can read in 5 minutes. Providers live under qamigrate.llm.providers and implement a single generate_structured method; adding Gemini or Ollama is a few hundred lines.
Configuration
Optional qamigrate.yaml in your project root:
pipeline:
max_retries: 3
coder:
model: "claude-sonnet-4-20250514"
max_tokens: 8192
compiler:
build_tool: "maven" # or "gradle"
java_version: "17"
skip: false # set true when Playwright JARs aren't on classpath
scanner:
exclude_patterns:
- "**/target/**"
- "**/build/**"
- "**/playwright/**"
You can also use environment variables with the QAMIGRATE_ prefix:
export QAMIGRATE_COMPILER__SKIP=true # skip compilation check
export QAMIGRATE_PIPELINE__MAX_RETRIES=5 # more retries
Python API
from qamigrate.agents.pipeline import run_migration
from qamigrate.core.config import QAMigrateConfig
from qamigrate.core.report import MigrationReport
config = QAMigrateConfig()
report = MigrationReport()
result = run_migration(
file_path=Path("src/test/java/LoginPage.java"),
project_path=Path("."),
config=config,
)
if result.record:
report.add(result.record)
report.finish()
report.write_html(Path("./report.html"))
Development
pip install -e ".[dev]"
pytest tests -v
ruff check src tests
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qamigrate-0.5.5.tar.gz.
File metadata
- Download URL: qamigrate-0.5.5.tar.gz
- Upload date:
- Size: 204.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e18d39ece7d800b536196eb078a69958b75c84eba366ef581949a8ac4ba65a70
|
|
| MD5 |
bf612440c88a3862e6b885970341845b
|
|
| BLAKE2b-256 |
ecfbd956380f2564292b6d8a77d78ff0efe8b39090aed8c17cb020d70ba92795
|
File details
Details for the file qamigrate-0.5.5-py3-none-any.whl.
File metadata
- Download URL: qamigrate-0.5.5-py3-none-any.whl
- Upload date:
- Size: 127.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d5dd6a6b81feda65d0e6e6876e4b2999fbec92d16a0c922812f933d7c4770da
|
|
| MD5 |
9048b6b9855f30631cf09e258278c741
|
|
| BLAKE2b-256 |
116f5e0c62beb3f60039897d11607a96107b1adc20da6ef23a37b759acaa1b1e
|