Guides
Oracle Forms automation
dolphin_desktop automates Oracle Forms Java clients — the Java Web Start applet, Forms Standalone Launcher (FSAL), or a plain Java main class — through the Java Access Bridge (JAB). The public surface speaks Forms concepts (blocks, items, function keys, LOV, status line) rather than raw Swing components, so tests port cleanly across Forms 10g / 11g / 12c and remain readable.
What works
- Direct JAB API for reads and writes.
type_text()usessetTextContents;text()/value()usegetAccessibleTextRange; buttons and menu items invoke viadoAccessibleActions. Zero mouse cursor movement, zero global keyboard capture — the tests run reliably from background processes, RDP sessions, and CI machines whereSetForegroundWindowis blocked by the OS. - Function keys (F1–F12 + Shift/Ctrl/Alt combinations) delivered
via
PostMessage(WM_KEYDOWN)to the target HWND. Works without foregrounding the window; hits the SwingInputMap/ActionMapchain the way a real user's keystroke would. - Menu bar navigation via ALT+first-letter mnemonic and JAB action invocation on the resolved menu item.
- List of Values (F9) wrapper —
.lov().select("SMITH")opens and completes the LOV popup. - Status line reader — pulls the current text from
AccessibleContext.getAccessibleDescription()(the standard place Oracle Forms publishes dynamic status text).
Quick start
from dolphin_desktop import Desktop, OracleFormsKey
app = Desktop().launch_oracle_forms(
jnlp="http://forms.example.com/forms/frmservlet?config=hr",
)
form = app.form()
form.wait_ready(timeout=30)
# Type into a block item, run the query, save.
app.block("EMPLOYEES").item("EMPNO").type_text("7369")
app.execute_query() # F8
assert "Record 1" in app.status_line()
app.save() # F10 (see caveat below)
# LOV
app.list_of_values() # F9
app.lov().select("JONES")
# Menu bar
app.menu("Query").select("Enter")
Function-key reference
Warning: This table is an all-or-nothing choice
The Forms runtime loads exactly one key-binding resource file and every key comes from it. The values below follow the PC-style family (
fmrpcweb.res). A host running the stockfmrweb.resuses a different family — and there, these bindings mean:
duplicate_record()→ Exit, discarding uncommitted changesinsert_record()→ Clear Recorddelete_record()→ Duplicate Recordclear_form()→ Next Primary KeyPress Ctrl+K ("Show Keys") in the running form to see what your runtime actually loaded. If it does not match, override the whole block — never a single entry, because a borrowed entry silently maps to a different Forms function.
| Attribute | Key | Oracle Forms action |
|---|---|---|
HELP |
F1 | Help topic |
COUNT_QUERY |
Shift+F2 | Count matching records ¹ |
DUPLICATE_ITEM |
F3 | Copy item from previous record ¹ |
DUPLICATE_RECORD |
F4 | Copy the previous record |
CLEAR_RECORD |
Shift+F4 | Clear current record |
CLEAR_BLOCK |
Shift+F5 | Clear all records in block ¹ |
INSERT_RECORD |
F6 | Insert new record |
DELETE_RECORD |
Shift+F6 | Delete current record |
ENTER_QUERY |
F7 | Enter query mode |
CLEAR_FORM |
Shift+F7 | Clear all records in form ¹ |
EXECUTE_QUERY |
F8 | Run the query |
LIST_OF_VALUES |
F9 | Open LOV popup |
SAVE / COMMIT |
F10 | Save changes |
NEXT_ITEM |
Tab | Move to next item |
PREVIOUS_ITEM |
Shift+Tab | Move to previous item |
NEXT_RECORD |
Shift+Down | Move to next record |
PREVIOUS_RECORD |
Shift+Up | Move to previous record |
NEXT_BLOCK |
Shift+Page_Down | Move to next block ¹ |
PREVIOUS_BLOCK |
Shift+Page_Up | Move to previous block ¹ |
CLEAR_ITEM |
Ctrl+U | Clear current item |
EXIT / CANCEL_QUERY |
Ctrl+Q | Exit form / leave query mode |
¹ Consistent with the PC-style family but not confirmed against a published resource file — verify with Ctrl+K before relying on it.
There is no First Record or Last Record function in the Forms key table, so the library exposes no binding for them. On a stock-file host F11 is Enter Query, which is why a "go to first record" helper built on F11 would silently put the block into query mode.
Modifier shortcuts need the foreground. PostMessage cannot set the
target thread's modifier state, so anything with Shift+/Ctrl+ is
delivered by SendInput against the foreground window. Unmodified keys
(F7, F8, F9, F10) work from background runs, RDP sessions and CI hosts;
modifier combos need a usable input desktop.
Send any of these with app.function_key(OracleFormsKey.X) or use the
named shortcuts (app.enter_query(), app.execute_query(), …).
F10 caveat. Swing intercepts F10 for menu-bar activation before
the InputMap fires. In real Forms 12c the Save keybinding still works
because Forms overrides the default; in the mock we ship, F10 opens
the menu bar. If your target Forms build has the same limitation, use
app.menu("Action").select("Save") instead.
Enabling the Java Access Bridge
The JAB is bundled with Adoptium and Oracle JDKs. dolphin's
JavaAccessBridge handles the setup automatically, or run once
by hand:
jabswitch.exe /enable
The JVM the Forms client uses must be launched with
-Djava.accessibility=true (dolphin's launch_oracle_forms adds this
flag by default) so the JVM loads the JAB helper on startup.
Backend selection
# Java Web Start (real Oracle Forms deployment)
app = desktop.launch_oracle_forms(jnlp="http://forms.example.com/frmservlet")
# Forms Standalone Launcher (FSAL) — bundled JAR
app = desktop.launch_oracle_forms(jar=r"C:\forms\frmsal.jar")
# Local Java main class (used by the integration test suite)
app = desktop.launch_oracle_forms(
main_class="OracleFormsMock",
classpath=r"C:\path\to\classes",
)
# Attach to an already-running client — no launch
app = desktop.attach_oracle_forms(title_re=".*HR Payroll Form.*")
Item locator convention
Oracle Forms items are named BLOCK.ITEM in the accessible tree
(.getAccessibleContext().setAccessibleName("EMPLOYEES.EMPNO")).
.block("EMPLOYEES").item("EMPNO") tries the qualified name first, then
falls back to the plain item name.
If your Forms build only publishes plain names, .item("EMPNO") on
the app directly works too.
Testing your own forms
The recommended pattern:
import pytest
from dolphin_desktop import Desktop
@pytest.fixture(scope="module")
def form_app():
app = Desktop().launch_oracle_forms(jnlp=YOUR_JNLP)
app.application.detach() # survive per-test PID reaping
app.form().wait_ready(timeout=30)
yield app
app.close()
def test_query_runs(form_app):
form_app.block("EMPLOYEES").item("EMPNO").type_text("7369")
form_app.execute_query()
assert "Record 1" in form_app.status_line()
The tests/oracle_forms/ directory ships a full
Swing mock and a 14-test integration suite as a reference.
Common failure modes
OracleFormsError: no top-level window found
: The JVM launched but its window is not surfaced to pywinauto. Check
that JAB is enabled (jabswitch /enable) and that
windowsaccessbridge-64.dll is on PATH.
Oracle Forms window did not become ready
: JAB never established a root-context handshake. Increase
startup_delay on launch_oracle_forms, or verify with
JavaAccessBridge.is_enabled().
Item read returns empty string
: The target field does not publish AccessibleText. Some older
Forms builds only publish accessible name. Fall back to
item._jab.description() or use image-based verification.
Function key does not fire
: The target Forms window may filter WM_KEYDOWN. Fall back to
bring_to_foreground() + Keyboard.type("{F8}", escape=False), or bind the same
action to a menu entry and use app.menu("Query").select("Execute").