bf2343b7e4
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
138 lines
5.8 KiB
Markdown
138 lines
5.8 KiB
Markdown
# UI test conventions
|
|
|
|
Read this before writing or porting any `*UIIT.java`. It exists so the suite stays
|
|
consistent as we migrate 258 specs.
|
|
|
|
## Layering — strict, top-only depends down
|
|
|
|
```
|
|
playwright.scenarios.<domain>.*UIIT.java — tests
|
|
↓
|
|
playwright.ui.pages.*Page.java — Page Objects (locators + actions)
|
|
↓
|
|
playwright.ui.{SessionBrowser,UiSession,UiSessionExtension,TraceRecorder}
|
|
it.auth.{AuthBackend,AuthSession,BasicJwtBackend,OidcBackend,…}
|
|
↓
|
|
it.server.*, it.search.* — server lifecycle + SDK helpers
|
|
```
|
|
|
|
**Rule:** prefer keeping `Locator` / `Page` / `BrowserContext` use inside Page Objects.
|
|
Page Objects may expose `Locator`-returning accessors when a test legitimately needs to
|
|
make a Playwright-level assertion on a specific element, and `PageObject.rawPage()` is a
|
|
documented escape hatch for URL assertions and SSO redirect flows. If a test reaches for
|
|
Playwright primitives for routine interactions (clicks, typing, navigation), promote the
|
|
interaction into a Page Object method.
|
|
|
|
## Test skeleton
|
|
|
|
```java
|
|
@ExtendWith({UiSessionExtension.class, TestNamespaceExtension.class})
|
|
class FooUIIT {
|
|
|
|
@BeforeAll
|
|
static void setup() {
|
|
SdkClients.useFluentApis(UiTestServer.get().sdk());
|
|
Apps.setDefaultClient(UiTestServer.get().sdk());
|
|
}
|
|
|
|
@Test
|
|
void scenario(final UiSession ui, final TestNamespace ns) {
|
|
Table t = TableTestFactory.createSimple(ns); // SDK setup
|
|
FooPage page = FooPage.open(ui, t.getFullyQualifiedName()); // page object navigation
|
|
page.doSomething(); // page object action
|
|
assertThat(page.someResult()).isVisible(); // assertion
|
|
}
|
|
}
|
|
```
|
|
|
|
Setup via SDK. Cleanup via `TestNamespace`. Never click through the UI to seed state.
|
|
|
|
## Page Objects
|
|
|
|
- One class per page, ≤ 200 lines. Split if bigger.
|
|
- Static `open(...)` factory navigates and returns a loaded instance.
|
|
- Actions return `this` (or another `PageObject`) for chaining.
|
|
- Locators: `getByTestId` > `getByRole`/`getByLabel` > `getByPlaceholder` > text > CSS.
|
|
- Define every selector as `private static final` — no inline literals.
|
|
- `waitForLoaded()` overridden per page — the readiness signal must be page-specific
|
|
(a known testid visible, an API response done) not a generic `networkidle` if a better
|
|
signal exists.
|
|
|
|
## Auth
|
|
|
|
The whole suite is parameterized by an `AuthBackend`. Pick one with `-Djpw.auth=<name>`
|
|
(or `JPW_AUTH=<name>`); the same UI tests run unchanged regardless of which is active.
|
|
|
|
| `jpw.auth` | OM auth provider | Token acquired by |
|
|
|---|---|---|
|
|
| `basic` *(default)* | OM built-in JWT | `JwtAuthProvider` (admin signing key) |
|
|
| `sso-google-public` | Google + public client (`response_type=id_token`) | mock IdP `/google/token` (password grant) |
|
|
| `sso-google-confidential` | Google + confidential client (`response_type=code`) | same |
|
|
| `sso-okta-public` | Okta + public client | mock IdP `/okta/token` |
|
|
| `sso-okta-confidential` | Okta + confidential client | same |
|
|
| `sso-custom-oidc-public` | Custom OIDC + public | mock IdP `/custom-oidc/token` |
|
|
| `sso-custom-oidc-confidential` | Custom OIDC + confidential | same |
|
|
|
|
Per-test override (only when the test explicitly drives the sign-in surface and doesn't
|
|
want a token preloaded):
|
|
|
|
```java
|
|
@Test
|
|
@NoPreloadAuth
|
|
void clickingSignInWithGoogleCompletesLogin(UiSession ui) { ... }
|
|
```
|
|
|
|
Tests that only make sense under one backend gate themselves with
|
|
`AuthAssumptions.onlyWhenBackendIs("sso-google-confidential")`. They skip cleanly when the
|
|
suite is running under any other backend.
|
|
|
|
A daemon `TokenRefresher` keeps `AuthSession.current()` valid across long runs; tests
|
|
read the current token at `@BeforeEach`, so refresh is transparent.
|
|
|
|
## Parallelism
|
|
|
|
- Per-method parallel by default; classes serial. Configured in pom failsafe.
|
|
- Tests that mutate global state must be tagged:
|
|
- `@ResourceLock("GLOBAL_SETTINGS")` — settings, themes, login config
|
|
- `@ResourceLock("SEARCH_INDEX_APP")` — re-triggers the global reindex app
|
|
- `@ResourceLock("APPS")` — install/uninstall apps
|
|
- `@ResourceLock("ALERTS")` — global alert config
|
|
- `@Execution(SAME_THREAD)` — inherently sequential UI flows (Tour, SSO renewal)
|
|
- Default: assume parallel-safe. `TestNamespace` keeps entity names unique.
|
|
|
|
## Java code rules (recap from project CLAUDE.md)
|
|
|
|
- Methods ≤ 15 lines, ≤ 3 nesting, ≤ 5 params, ≤ 10 cyclomatic.
|
|
- `final` on every parameter and local that doesn't change.
|
|
- No comments stating *what* — only *why*. Name the code so it reads.
|
|
- No magic strings in `equals`/`contains`/`switch` — define a constant or an enum.
|
|
- No deep `else if` chains — refactor to `switch`, `Map` dispatch, or named predicate.
|
|
- No empty catches, no `printStackTrace`, no `catch(Exception)` for control flow.
|
|
- `mvn spotless:apply` before every commit.
|
|
|
|
## Headed debugging
|
|
|
|
Knobs (all available as env var or `-D<name>=...` system property):
|
|
|
|
| Knob | Effect |
|
|
|---|---|
|
|
| `PW_HEADED=true` | Visible Chromium |
|
|
| `PW_SLOWMO=<ms>` | Inter-action delay; defaults to 250ms in headed, 0 in headless |
|
|
| `PW_VIDEO=true` | Records every test, saves to `target/playwright-videos/<class>-<test>-<ts>.webm` |
|
|
|
|
Trace zips for **failed** runs are saved to `target/playwright-traces/`. Replay any trace:
|
|
```
|
|
npx playwright show-trace target/playwright-traces/trace-<class>-<test>-<ts>.zip
|
|
```
|
|
|
|
Typical local debug recipe — slow it down enough to follow, capture video for review:
|
|
```
|
|
PW_HEADED=true PW_SLOWMO=500 PW_VIDEO=true mvn verify -P ui-it \
|
|
-pl :openmetadata-integration-tests -Dit.test='SimpleReindexTriggerUIIT'
|
|
```
|
|
|
|
## When in doubt
|
|
|
|
- Read `REINDEX_TEST_PLAN.md` at the module root for the reindex scenario coverage map.
|
|
- Read `SimpleReindexTriggerUIIT` as the canonical reference test.
|