# effiTest-ui-code-gen

> Generate or update Selenium-Java UI automation code on top of the effiTest hybrid framework from UI screenshots and a test-case file (txt/csv/xlsx). Produces a <feature>_Page.java, <feature>_Test.java, and optional TestNG xml. Use this skill whenever the user asks to build, scaffold, update, or regenerate UI automation / page-object / TestNG code for effiTest, effitest, Selenium+Java page objects, Java hybrid framework tests, or anything involving --page-classpath / --test-classpath / --update-page-class / --update-test-class / --testng-xmlpath flags, or when they mention generating Java UI tests from screenshots.

- Skill: `nataraaj-shanmugam/effitest-ui-code-gen` (Agent Skill, multi-file: 17 files)
- Install (CLI): `npx skillmds@latest add nataraaj-shanmugam/effitest-ui-code-gen`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nataraaj-shanmugam/effitest-ui-code-gen/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: Nataraaj-Shanmugam (https://skillmd.com/u/nataraaj-shanmugam)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/nataraaj-shanmugam/effitest-ui-code-gen

---


# effiTest UI Code Generator

This skill converts UI screenshots + a test-case document into compileable Java automation code for the effiTest hybrid framework (effiTest wrappers preferred; raw Selenium only as fallback). It handles both new-file generation and surgical updates to an existing Page / Test class.

## When to invoke

Trigger whenever the user wants Page/Test automation code generated or updated. They must supply **at least one of** `--images` or `--testcases`, always a page path flag, and a test path flag **only when test cases are supplied**.

## Inputs

| Flag | Meaning |
|---|---|
| `--page-classpath <path>` | Create new Page class at this path |
| `--update-page-class <path>` | Update existing Page class at this path |
| `--test-classpath <path>` | Create new Test class at this path (required only with `--testcases`) |
| `--update-test-class <path>` | Update existing Test class at this path (required only with `--testcases`) |
| `--testng-xmlpath <path>` | Optional; honored only when a Test class is being generated |
| `--feature-name <name>` | Required on first run for a page path (cached after) |
| `--images <glob-or-dir>` | One or more UI screenshots. Optional if `--testcases` given. |
| `--testcases <file>` | txt / csv / xlsx of test cases. Optional if `--images` given. |

Validation:
- Page pair is always required: exactly one of (`--update-page-class`, `--page-classpath`).
- Test pair is required **only when** `--testcases` is supplied: exactly one of (`--update-test-class`, `--test-classpath`).
- At least one of `--images` / `--testcases` must be supplied.
- `parse_args.py` enforces all of the above and prints a `flow` field telling you which mode to run.

## Flow modes

`parse_args.py` emits a `flow` field in its JSON output — branch on it:

- **`full`** — both `--images` and `--testcases` supplied. Run all phases (A→F) as documented below.
- **`page-only`** — only `--images` supplied. Run phases A, B, C. **Skip** phases D (test class), E's test-class compile checks (still compile the Page alone), and F (testng xml is ignored).
- **`testcases-only`** — only `--testcases` supplied. Run phase A. **Skip phase B (no OCR).** In phase C, read the existing Page class to learn what fields/methods already exist; in phase D, parse test cases and map each step to a Page method — for any step whose `target` has **no matching field or method** in the Page, **stub** it into the Page: add `By <name> = null;` and an **empty** action method with the right signature (see "Stubbing rules" below). Then emit the Test class. Compile and fix as usual.

## Workflow overview

The skill runs six phases. Do not skip phases: each later phase depends on state from the earlier ones. Run the bundled scripts rather than re-implementing their logic inline — they are deterministic and reused across invocations.

```
Phase A: Init              → parse args, ensure config.json, (re)build effitest_methods.json
Phase B: OCR per image     → spawn vision subagents, write per-image field JSONs (+ tesseract merge if low-confidence)
Phase C: Page class        → create or update <feature>_Page.java (effiTest-first)
Phase D: Test class        → parse test cases → map steps → emit <feature>_Test.java
Phase E: Compile & fix     → mvn test-compile loop (max 5); on exhaustion, summarize root cause
Phase F: TestNG xml        → render assets/testng_template.xml if --testng-xmlpath supplied
```

All absolute paths below assume the skill root is the directory containing this SKILL.md. All project-relative paths resolve against the working directory (the effitest project root).

### Phase A — Init

1. Run the arg parser:

   ```bash
   python scripts/parse_args.py <all forwarded flags> --project-root <cwd>
   ```

   It prints a single JSON blob on stdout with normalized paths and a resolved `feature_name` (looking up the cache if `--feature-name` wasn't passed). If validation fails, it exits non-zero with a human-readable error — surface that and stop.

2. Build / refresh the method catalog:

   ```bash
   python scripts/init_methods_catalog.py \
     --project-root <cwd> \
     --config <cwd>/.claude/effiTest-code-gen/config.json \
     --methods-out <cwd>/.claude/effiTest-code-gen/effitest_methods.json
   ```

   This script:
   - Creates `<project>/.claude/effiTest-code-gen/config.json` with `{"effiTest.version": null, "last used time": "<ISO>"}` if missing.
   - Reads the pom, finds the effitest dependency version.
   - If `config.effiTest.version` matches and `effitest_methods.json` exists, it just updates `last used time` and exits fast.
   - Otherwise it locates the jar under `~/.m2/repository/...`, runs `javap` on the curated class list from `scripts/effitest_paths.py`, and writes the catalog in the exact schema the spec demands.

   If the script reports the jar isn't in `~/.m2` (fresh clone, user hasn't built), tell the user to run `mvn -q dependency:resolve` and rerun. Don't fabricate methods.

### Phase B — OCR per image   (skip entirely in `testcases-only`)

For each image path resolved by `parse_args.py` (the `images` array in the JSON), spawn **one `Explore` subagent per image, in parallel, up to 3 at a time**. Use `agents/ocr_vision.md` as the subagent prompt, injecting the image path and the target JSON output path:

```
<project>/.claude/effiTest-code-gen/ocr/<feature_name>_<image-stem>.json
```

When all subagents return, inspect each JSON. If a JSON has `"confidence": "low"` or is missing critical fields the user's test cases reference, merge in tesseract results:

```bash
python scripts/ocr_fallback.py --image <image-path> --merge-into <json-path>
```

`ocr_fallback.py` no-ops gracefully if `pytesseract` / `Pillow` / the `tesseract` binary are missing (prints a single warning line, exit 0). Do not block the pipeline on its absence.

### Phase C — Page class (new or update)

Read everything you need into context:
- The catalog at `<project>/.claude/effiTest-code-gen/effitest_methods.json`.
- (If any) each per-image OCR JSON.
- The existing Page file if update mode.
- `references/generation_patterns.md` (effitest-first mapping rules and the class skeleton).

Then, based on `flow`:

**`full` or `page-only` — create mode** (`--page-classpath`): emit a full class at that path. Derive the `package` by taking the portion of the path after `src/main/java/` or `src/test/java/` and replacing `/` with `.`. The class name is `<feature_name>_Page`. Add one `By <fieldName> = null;` per OCR field (camelCase, unique), plus one action method per field (`enter<Field>`, `click<Field>`, `select<Field>`, `toggle<Field>`, etc., driven by the field type). For each action, **prefer** a matching method from the effitest catalog; only emit raw Selenium when no match exists. Imports: include only what you use.

**`full` or `page-only` — update mode** (`--update-page-class`): read the existing file, compute the set of already-declared `By` fields (parse the file contents — don't guess), and append only the missing fields and their action methods. Preserve the existing package line, imports, brace style, and surrounding code. Add any new imports the new methods require, placed with the existing import block. Do not reformat unrelated code.

**`testcases-only`**: there's no OCR JSON to drive the Page. Read the existing Page file (or, in create mode, start an empty one at `--page-classpath`). Then during phase D, as each test step is mapped, **stub** into the Page any missing field/method (see "Stubbing rules" in `references/generation_patterns.md`). Don't generate speculative stubs before phase D — wait until a test step actually references them, so the Page only grows what's actually used.

The exact skeleton and the effitest-first decision rule live in `references/generation_patterns.md` — read it before generating.

### Phase D — Test class   (skip entirely in `page-only`)

1. Normalize the test cases:

   ```bash
   python scripts/parse_testcases.py --file <testcases path> --out <project>/.claude/effiTest-code-gen/<feature>_testcases.json
   ```

   Emits `[{id, title, preconditions, steps:[{action, target, data, expected}], postconditions}]`. For `.txt` it uses a line-heuristic; for `.csv` it relies on headers (`id,title,step,data,expected,...`); for `.xlsx` it uses `openpyxl`. If `openpyxl` is unavailable, the script exits with a clear message — tell the user to `pip install openpyxl` and stop.

2. **Bind each step to a Page method.** For every step, locate a matching field (or method) on the current Page class by matching `step.target` (normalized to camelCase) against declared `By` names and existing method names. If a match exists, call the method. If not:
   - In `full` flow, this should be rare — note it as a warning and fall back to stubbing (same rule as below).
   - In `testcases-only` flow, **stub** the Page: append a `By <name> = null;` declaration and an **empty** action method with the correct signature (`public void <verb><Name>(String value) {}` for data-entry verbs, `public void <verb><Name>() {}` for click/toggle, `public String <verb><Name>() { return null; }` for verify/read). Leave a `// TODO: implement from <feature>_Page stub — raised from test case <TC-id>` comment inside the body. After stubbing, re-emit the Page file and then call the method from the test.

3. **Emit `<feature>_Test.java`** at the configured test path:
   - One `@Test` per test case, method name = camelCased `title` (or `tc_<id>` fallback).
   - Preconditions → `@BeforeMethod`; postconditions → `@AfterMethod`. If the project appears to have a base test class (detectable by a `BaseTest` / `TestBase` file sibling to the target path), extend it and skip the lifecycle hooks.
   - Each step calls a method on the `<feature>_Page` instance; only use methods that exist in the Page class after stubs have been applied.
   - Each step with an `expected` becomes an assertion — prefer an `EffiAssert`-style method from the catalog; fall back to `org.testng.Assert.*`. If the Page has no verify-style getter, stub one (see above) so the assertion has something to read.

4. **Final report** for `testcases-only` flow should list every stubbed field and method so the human knows what to fill in:

   ```
   ⚠ Stubbed in Page (needs human implementation):
     • By captchaInput — added from TC03 step 2
     • void enterCaptchaInput(String value) — empty body
     • String getErrorBanner() — returns null
   ```

### Phase E — Compile & fix loop

```bash
python scripts/compile_check.py --project-root <cwd>
```

Returns JSON: `{"ok": bool, "errors": [{"file","line","message"}, ...], "raw": "<trailing maven output>"}`.

If `ok` is false, for **each** error, open the file, read the offending line ± 5 lines, and patch only what's needed. Rerun the script. Iterate at most **5 times**. If still failing:

- Print a concise summary: the top 3 unique error messages, the files they touch, and a one-line "likely cause / suggested fix" for each (e.g. "effitest method signature drift → regenerate catalog with `rm .claude/effiTest-code-gen/effitest_methods.json` and rerun", "missing pom dependency → add `<dependency>...` for X").
- Leave the generated files on disk — the user may want to inspect and hand-fix.

### Phase F — TestNG xml

Only if `--testng-xmlpath` was supplied. Read `assets/testng_template.xml`, replace `{{SUITE_NAME}}`, `{{CLASS}}` (FQCN of the generated test class), `{{LISTENERS}}` (comma-less XML snippet from `effitest_paths.LISTENERS_BY_MAJOR`, keyed on the effitest major version parsed from the pom — fall back to the `default` entry and log a warning if unknown). Write to `--testng-xmlpath`.

## Final report to the user

Print a compact summary:

```
✔ Page:   <absolute path>    (<N added fields, 0 removed>)
✔ Test:   <absolute path>    (<N test methods>)
✔ XML:    <absolute path or "skipped">
✔ Catalog: effiTest <version> (<M classes, K methods>)   [regenerated | cached]
✔ Compile: OK | 5 iterations exhausted — see errors above
```

## Tips

- Always run scripts via `python scripts/<name>.py` from the skill directory, passing absolute project paths — keep the skill location-independent.
- Field names must be deterministic: snake from the visible label, then camelCase. If two fields would collide, disambiguate with a trailing index (`email`, `email2`) and log the collision.
- Never invent effitest methods. If the needed action isn't in the catalog, emit the Selenium fallback and leave a `// TODO: effitest equivalent?` comment so humans can triage.
- When updating an existing file, never re-order existing members — append only. This keeps diffs reviewable.
- For OCR, bias toward *semantic* field names (e.g. `usernameInput`) over layout ones (`textField1`). The vision subagent prompt enforces this.

## Files in this skill

- `scripts/parse_args.py` — CLI validation & normalization.
- `scripts/effitest_paths.py` — curated FQCN list, description overrides, listener version map.
- `scripts/init_methods_catalog.py` — pom → m2 jar → javap → methods JSON.
- `scripts/parse_testcases.py` — txt/csv/xlsx normalizer.
- `scripts/ocr_fallback.py` — pytesseract fallback merger.
- `scripts/compile_check.py` — `mvn test-compile` wrapper.
- `agents/ocr_vision.md` — prompt for the per-image vision subagent.
- `assets/testng_template.xml` — TestNG suite template.
- `references/generation_patterns.md` — Page/Test generation rules & skeleton.
- `references/examples.md` — worked end-to-end example.

