# TestFly Part 2: Accessibility-First Locators, BasePage & WaitEngine

URL: https://hakangul.lovable.app/blog/testfly-part-2-scalable-page-object-model-and-smart-waits
Author: Hakan Gül — Team Lead Senior Test Automation Engineer
Published: 2026-08-28
Updated: 2026-08-29
Category: test-automation
Tags: TestFly, Java, BasePage, WaitEngine, Semantic Locators, Role, Locator, SDET

> Write resilient tests that survive DOM refactors. Master TestFly's Playwright-style semantic locators (getByRole, getByLabel), BasePage helpers, and auto-waiting WaitEngine.

## English

## Accessibility-First Semantic Locators

Traditional Selenium tests rely heavily on brittle CSS selectors or complex XPaths that break whenever the DOM hierarchy changes:
```java
// Brittle — breaks on CSS redesigns:
find(By.cssSelector("div.modal > form button.btn-primary")).click();
```

TestFly introduces Playwright-style **Semantic Locators** that target the **accessibility tree**—interacting with the page exactly as a real human perceives it:

```java
getByRole(Role.BUTTON).withName("Submit").click();
getByLabel("Email address").type("user@testfly.io");
getByPlaceholder("Search products…").type("laptop");
getByText("Forgot password?").click();
getByTestId("checkout-cta").click();
```

Every semantic locator returns an auto-waiting, chainable `Locator` instance with zero `Thread.sleep()`.

---

## Clean Page Objects with `BasePage`

`BasePage` provides wait-backed helper methods (`type`, `click`, `getText`, `getAttribute`, `isDisplayed`):

```java
package io.testfly.examples.pages;

import io.testfly.test.BasePage;
import io.testfly.locator.Role;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage extends BasePage {

    private static final By USERNAME = By.id("username");
    private static final By PASSWORD = By.id("password");

    public LoginPage(WebDriver driver) {
        super(driver);
    }

    public void login(String user, String pass) {
        type(USERNAME, user);
        type(PASSWORD, pass);
        getByRole(Role.BUTTON).withName("Sign In").click();
    }
}
```

---

## Advanced Synchronization with `WaitEngine`

When writing custom wait conditions, `WaitEngine` centralizes explicit waits using `timeouts.explicit` from `testfly.yml`:

```java
import io.testfly.wait.WaitEngine;

// Centralized explicit wait conditions:
WaitEngine.waitForVisible(By.id("dashboard-banner"));
WaitEngine.waitForClickable(By.id("pay-button"));
WaitEngine.waitForInvisible(By.cssSelector(".loading-spinner"));
```

## Türkçe

## Erişilebilirlik Öncelikli Semantik Seçiciler

Geleneksel Selenium testleri, DOM yapısı her değiştiğinde kırılan kırılgan CSS ve XPath seçicilerine dayanır:
```java
// Kırılgan — Arayüz tasarımı değiştiğinde patlar:
find(By.cssSelector("div.modal > form button.btn-primary")).click();
```

TestFly, kullanıcının sayfayı algılama biçimini (erişilebilirlik ağacını) hedefleyen **Semantik Seçiciler** sunar:

```java
getByRole(Role.BUTTON).withName("Gönder").click();
getByLabel("E-posta adresi").type("kullanici@testfly.io");
getByPlaceholder("Ürün ara…").type("laptop");
getByText("Şifremi unuttum").click();
getByTestId("odeme-butonu").click();
```

Her semantik seçici, arkasında otomatik bekleme barındıran zincirleme `Locator` nesnesi döner.

---

## `BasePage` ile Temiz Page Object Sınıfları

`BasePage` sınıfı, doğrudan bekleme destekli yardımcı metotlar sağlar:

```java
package io.testfly.examples.pages;

import io.testfly.test.BasePage;
import io.testfly.locator.Role;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class LoginPage extends BasePage {

    private static final By USERNAME = By.id("username");
    private static final By PASSWORD = By.id("password");

    public LoginPage(WebDriver driver) {
        super(driver);
    }

    public void girisYap(String user, String pass) {
        type(USERNAME, user);
        type(PASSWORD, pass);
        getByRole(Role.BUTTON).withName("Giriş Yap").click();
    }
}
```

---

## `WaitEngine` ile Güvenli Senkronizasyon

Özel bekleme durumları için `WaitEngine`, `testfly.yml` içindeki zaman aşımlarını kullanarak bekleme yönetir:

```java
import io.testfly.wait.WaitEngine;

WaitEngine.waitForVisible(By.id("dashboard-banner"));
WaitEngine.waitForClickable(By.id("odeme-butonu"));
WaitEngine.waitForInvisible(By.cssSelector(".yukleniyor-spinner"));
```
