--- name: test-case-to-selenium description: Convert Katalon True Platform/TestOps manual test cases into Selenium WebDriver automation in whichever language binding the project already uses. Use when you need to read Katalon Platform cases and write Selenium code, detect the binding from pom.xml, build.gradle, requirements.txt, pyproject.toml, or package.json, scaffold or extend a Page Object Model layer, wire the project's own runner (JUnit 5, TestNG, pytest, or Mocha) that owns execution and reporting, and turn manual steps into stable locators and explicit waits. Selenium is a library rather than a runner, so this skill generates and compiles the code and stops at the runner boundary; to land an already-executed run in the platform use upload-report. Written for the automation tester who already has a Java, Python, or JavaScript Selenium project and needs a manual case expressed in its idiom. --- # Katalon Test Case To Selenium Use this skill to turn Katalon Platform/TestOps test cases into maintainable Selenium WebDriver automation **in the binding the project already uses**. Selenium is a library, not a test framework: the runner underneath it (JUnit 5, TestNG, pytest, Mocha) owns execution, assertions, and reporting, and this skill writes code that fits that runner rather than replacing it. Prefer existing project patterns when a Selenium project already exists. Treat the human as a tool: ask concise questions whenever a required target, credential, repository, AUT detail, or test data value cannot be discovered safely. ## Availability Boundary State the boundary before promising automation. It has three parts and none of them is optional. **Available through Katalon MCP** - Resolve scope with `list_projects` and `list_repositories`. - Read the source cases with `find_test_suites`, `read_test_suite`, `find_test_cases`, and `read_test_case`. - Follow a requirement key with `find_requirements`, `read_requirement`, and `find_test_cases_by_requirement`. - Read the application under test with `read_auts`, for the base URL and the environment, rather than guessing them. **Not available through Katalon MCP** - **No MCP tool executes anything.** Nothing compiles Java, runs `mvn`, `gradle`, `pytest`, or `npm test`, or starts a browser. `schedule_test_run` schedules *platform* executions of cases already registered in the platform; it does not run a Selenium project sitting in a local repository. - **No MCP tool reads or writes source code.** `list_repositories` returns Katalon repositories and Test Projects, not Git contents. Every file this skill produces is written to the local workspace with ordinary file tools. - **No MCP tool attaches a script to a test case.** `create_test_case` and `update_test_case` write manual-format cases only. There is no tool that stores a code path, a framework name, or an automation flag on a case. - **No MCP tool inspects the live application.** `read_auts` returns AUT configuration, not page structure. Locators come from the running application through a browser tool, from the existing page-object layer, or from the human. - **Requirements cannot be created through MCP.** They are synced from Jira or Azure and can only be found, read, and linked. This matters here because requirement-linked conversion is a common entry path. - **This skill does not upload results.** Execution belongs to the project's runner; landing results in the platform belongs to `upload-report` with `--type=junit`. **The tool that would help does not exist, so traceability is a workaround and is labelled as one** The clean design would be an MCP tool that records, on the Katalon case itself, that automation now exists and where the code lives. It is not in the tool index, so it is not used and nothing here pretends otherwise. **Do not tell the user the platform now knows the case is automated.** It does not, and it will not until a run is uploaded. What exists instead: the runner writes a JUnit XML `` for every test, and `upload-report` carries that XML into the platform. So the Katalon case ID travels inside the **test's own name**. Because `TC-1042` is not a legal identifier in any binding, the rule is stated once and applied everywhere: > `TC-1042` becomes `TC_1042` inside identifiers and stays `TC-1042` inside display names, titles, and comments. Write it in **both** places, so the ID survives whichever string the runner puts into the XML. ## Workflow ```text +-------------------+ --> +-------------------+ --> +---------------------+ | Resolve Katalon | | Read test cases | | Detect the binding | +-------------------+ +-------------------+ +---------------------+ | v +-------------------+ <-- +-------------------+ <-- +---------------------+ | Verify + hand off | | Write automation | | Map manual steps | +-------------------+ +-------------------+ +---------------------+ ``` ## Katalon Source Resolve the Katalon context before writing a line of code: 1. Call `list_projects`, then `list_repositories`, and confirm which project the cases live in. 2. If the user names a test suite, call `read_test_suite` and then `read_test_case` on every included case before generating anything. A suite converted case by case without reading the suite loses shared preconditions. 3. If the user names a requirement key, call `find_test_cases_by_requirement` first, then `read_test_case` on each result. Call `read_requirement` when the acceptance criteria are needed to phrase an assertion. 4. If the user names a case key or title, call `find_test_cases` and then `read_test_case`. 5. Call `read_auts` for the application URL and environment. Never invent a base URL. 6. If MCP tools are unavailable or authentication fails, ask for exported cases, case URLs, case keys, or the case text, and say which tools were unreachable. Extract for each case: title, priority, requirement links, folder or suite, preconditions, test data, manual steps, and expected results. Extract for the run: AUT URL, user roles and accounts, environment, browser assumptions, and cleanup requirements. Flag ambiguous selectors and business data that need human input rather than filling them in. ## Binding Resolution Selenium has one API and several language bindings. **Detect, never ask first, and never assume Java.** Search the workspace before creating anything: | Signal found | Binding | Runner to expect | Build/run command | |---|---|---|---| | `pom.xml` with `selenium-java` | Java | JUnit 5 (`junit-jupiter`) or TestNG (`testng`) | `mvn test` | | `build.gradle` / `build.gradle.kts` with `selenium-java` | Java | JUnit 5 or TestNG | `gradle test` | | `requirements.txt` or `pyproject.toml` with `selenium` | Python | pytest | `pytest` | | `package.json` with `selenium-webdriver` | JavaScript/TypeScript | Mocha, or the runner already in `scripts.test` | `npm test` | Rules: 1. Read `references/selenium-bindings.md` before scaffolding anything or making broad changes to an existing project. 2. If exactly one binding is detected, use it and say out loud which signal decided it. 3. If several are detected, or a manifest exists but declares no Selenium dependency, ask the user which project to extend. Do not pick the largest one. 4. If no Selenium project exists, ask whether the user wants to provide a repository or path, or wants a project created. If the user does not answer and the workspace is writable, scaffold the binding that matches the surrounding repository's primary language. 5. Never introduce a second binding into a repository that already has one. **Selenium Manager handles drivers. Do not write driver-download instructions.** Selenium 4.6 and later resolve the browser driver automatically, and 4.11 and later can download the browser itself when a `browserVersion` is requested that is not installed. Never instruct the user to download ChromeDriver or GeckoDriver, never add a driver manager dependency to a new project, and never set a driver system property. If an existing project already uses a driver manager, leave it alone and say why it is now redundant rather than ripping it out mid-task. ## Runner Ownership The runner, not this skill, owns execution and reporting. Respect that split: - Do not write a `main()`, a shell wrapper, or a bespoke runner. Wire the tests into the runner the project already declares. - Do not add a reporting library. Every runner in the table above emits JUnit XML that `upload-report` accepts, and that is the supported path into the platform. - Setup and teardown use the runner's own lifecycle: `@BeforeEach`/`@AfterEach` in JUnit 5, `@BeforeMethod`/`@AfterMethod` in TestNG, a fixture in pytest, `beforeEach`/`afterEach` in Mocha. Always quit the driver in teardown, on the failure path as well as the success path. - Parallelism, retries, and timeouts are runner configuration. Change them only when the user asks. - Stop at the runner boundary. Run the narrowest check that proves the code is real, report the JUnit XML path, and hand the upload to `upload-report`. ## Automation Design Translate manual Katalon steps into Selenium code using these rules: - Use Page Object Model for page structure and domain actions. - **Page objects never assert.** This is Selenium's own guidance and it differs on purpose from the Playwright sibling skill, which puts expectations inside page objects. A page object exposes actions that return `void` or the next page object, and state readers that return plain values. The test method does every assertion, with the runner's assertion library. A page object that imports an assertion library is a defect. - Write test names and steps that remain understandable to someone who only ever read the manual case. - Prefer stable locators in this order: `id`, `name`, a CSS selector on a test attribute such as `data-testid`, then accessible text. Avoid absolute XPath and generated class names. - Use explicit waits (`WebDriverWait` with expected conditions, or the binding's equivalent) at the point where the application is actually slow. **Never `Thread.sleep`, `time.sleep`, or a bare timeout promise, and never mix implicit and explicit waits in the same project.** The combination produces unpredictable wait times. - Convert manual expected results into assertions in the test, next to the action that produces them, in the same order the manual case lists them. - Keep one automated test aligned to one Katalon test case unless the existing project groups scenarios differently. - Do not silently invent credentials, URLs, product IDs, account state, or selectors. Ask the human, or leave a narrow TODO that names the exact missing value. Read `references/page-objects-and-waits.md` before writing the first page object. ## Traceability There is no MCP tool that links a test case to code, so the ID travels inside the test name and reaches the platform through JUnit XML: - Put the case ID in the **test name**: `TC_1042` inside identifiers, `TC-1042` inside display names and titles. Write both, because different runners put different strings into the XML `name` attribute. - Java: name the method `TC_1042_...` **and** add `@DisplayName("TC-1042 ...")`. - Python: name the function `test_TC_1042_...`. - JavaScript: start the `it()` title with `TC-1042`. - Keep the requirement key in a comment or a tag next to the test when the case has one. - Do not rely on annotations, markers, or tags alone. They are documentation; the name is the carrier. - After the first run, grep the generated XML for the ID. If it is not in a `name` attribute, the traceability does not exist yet, whatever the annotations say. ## Implementation Rules - Follow the existing repository's naming, package or module layout, linting, and assertion conventions when present. - Keep generated code idiomatic for its binding: `camelCase` methods and one public class per file in Java, `snake_case` and fixtures in Python, `camelCase` and `async`/`await` in JavaScript. - Store test data in the project's existing data pattern. If none exists, create one typed or structured holder rather than scattering literals across tests. - Add only the dependencies the tests need, at the versions the project already pins where possible. - Avoid changing unrelated build config, CI files, or runner settings. - If the application must be inspected to identify selectors, use a browser tool and keep the selectors stable. - If live application access is blocked, implement the structure and mark only the selector and test data gaps that need human input. ## Worked Example **Input.** Katalon case `TC-1042` in project *Cellphone Shop*, read with `read_test_case`, linked to requirement `CEL-6`: ```text TC-1042 Search returns an in-stock phone and adds it to the cart Precondition Guest session, storefront reachable 1. Open the storefront home page -> search box is visible 2. Search for "Nokia Lumia 1020" -> results contain "Nokia Lumia 1020" 3. Open the product from the results -> price is 349.00 and stock status is "In stock" 4. Add the product to the cart -> cart count is 1 and the cart contains the product ``` **Detection.** `rg --files` finds `pom.xml` declaring `selenium-java 4.25.0` and `junit-jupiter 5.11.0`, and `src/test/java/shop/pages/` already exists. Binding is Java, runner is JUnit 5, build is Maven. Say this before writing anything. **Expected output, page object with no assertions:** ```java public class SearchResultsPage { private final WebDriver driver; private final WebDriverWait wait; private final By productTitles = By.cssSelector("[data-testid='product-title']"); public SearchResultsPage(WebDriver driver) { this.driver = driver; this.wait = new WebDriverWait(driver, Duration.ofSeconds(10)); } public List visibleProductTitles() { wait.until(ExpectedConditions.visibilityOfElementLocated(productTitles)); return driver.findElements(productTitles).stream().map(WebElement::getText).toList(); } public ProductPage openProduct(String title) { driver.findElements(productTitles).stream() .filter(e -> e.getText().equals(title)).findFirst().orElseThrow().click(); return new ProductPage(driver); } } ``` **Expected output, the test that owns every assertion:** ```java @Test @DisplayName("TC-1042 Search returns an in-stock phone and adds it to the cart") void TC_1042_searchReturnsInStockPhoneAndAddsItToTheCart() { HomePage home = new HomePage(driver).open(data.baseUrl()); // step 1 SearchResultsPage results = home.searchFor(data.phoneName()); // step 2 assertTrue(results.visibleProductTitles().contains(data.phoneName())); ProductPage product = results.openProduct(data.phoneName()); // step 3 assertEquals("In stock", product.stockStatus()); assertEquals("349.00", product.price()); CartPage cart = product.addToCart(); // step 4 assertEquals(1, cart.itemCount()); assertTrue(cart.itemTitles().contains(data.phoneName())); } ``` **Expected files:** `src/test/java/shop/pages/{HomePage,SearchResultsPage,ProductPage,CartPage}.java` and `src/test/java/shop/CellphoneShopSearchTest.java`. No driver download, no `Thread.sleep`, no assertion import anywhere under `pages/`. **Expected verification.** Run the narrowest check, then hand off: ```bash mvn -q -Dtest=CellphoneShopSearchTest test # Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 # writes target/surefire-reports/TEST-shop.CellphoneShopSearchTest.xml # containing: ``` Then say: *the JUnit XML is at `target/surefire-reports/`; use `upload-report` with `--type=junit` to land it in the platform.* This skill stops there. **The same case in a Python project, abbreviated.** Detection finds `pyproject.toml` declaring `selenium` and `pytest`, so nothing above is reused except the mapping: ```python def test_TC_1042_search_returns_in_stock_phone(driver, data): results = HomePage(driver).open(data.base_url).search_for(data.phone_name) assert data.phone_name in results.visible_product_titles() product = results.open_product(data.phone_name) assert product.stock_status() == "In stock" cart = product.add_to_cart() assert cart.item_count() == 1 ``` ```bash pytest -k TC_1042 --junitxml=reports/junit.xml # 1 passed # writes reports/junit.xml with ``` Same case, same page-object rule, same traceability rule, different idiom. That is what binding-adaptive means in practice. ## Verification After writing code, run the narrowest check that the detected binding allows and name whatever stays unproven: 1. **Compile or collect.** `mvn -q test-compile`, `gradle compileTestJava`, `pytest --collect-only`, or `npx tsc --noEmit`. A generated suite that does not compile is not a deliverable. 2. **Run one targeted test** when the application and credentials are reachable: `mvn -Dtest= test`, `gradle test --tests `, `pytest -k TC_1042`, or the project's own `npm test` filter. 3. **Confirm the JUnit XML exists and carries the case ID.** Grep the file for the ID and check it sits in a `name` attribute. If it does not, fix the test name before reporting anything. 4. **Grep for the two banned patterns.** No assertion import under the page-object folder, and no `Thread.sleep`, `time.sleep`, or bare timeout anywhere in the generated files. 5. **Report the blocker, not a guess.** If the test cannot run because credentials, application access, or dependencies are missing, say exactly which one and what stays unverified. Do not claim the case is automated in the platform. This skill produces code; the platform learns about it only after `upload-report`. ## Report Back Report the created and updated files with full paths, the detected binding and the signal that decided it, the runner and build command, the Katalon cases each test maps to, the commands actually run and their output, the JUnit XML path for the hand-off, what stays unverified, and any value still needed from a human. ## Prompt recipes - `Turn TC-1042 into a Selenium test in this Maven project.` - `We use pytest and Selenium. Can you convert the cases for CEL-6 into automation?` - `Convert the smoke suite into Selenium tests using our existing page objects under src/test/java/shop/pages.` - `This project mixes implicit and explicit waits. Convert TC-1042 and fix the waits in the pages it touches.` ## Hand-offs - Results into the platform -> `upload-report`, `--type=junit`. - Suite has gone flaky or needs repair -> `test-maintenance`. - A red run that needs triage -> `analyze-failures`. - Cases do not exist yet -> `create-test-cases`. - The project uses Playwright, not Selenium -> `test-case-to-playwright`. - The project is a Katalon Studio Test Project, not a code-first framework -> `test-case-to-katalon-studio`. Read `references/selenium-bindings.md` for detection signals, project shapes, runner wiring, and where each runner writes its JUnit XML. Read `references/page-objects-and-waits.md` for the page-object contract, the locator order, and the wait discipline. Read the orchestrator `true-platform-testing/references/unavailable-capabilities.md` when the user asks what Katalon can do through MCP.