Skip to content

Chapter 10 · Forms and Complex Component Interaction ​

Chapter goals: Master automation methods for dropdowns, checkboxes, file uploads, and native dialogs; distinguish the适用 boundaries of fill vs press_sequentially; learn to handle date pickers — the "hardest professional" of widget automation.

10.1 Dropdown and Selection Controls ​

Native <select> is handled by select_option, supporting value, label, or index targeting, plus multi-select:

python
# Single select: match by value or label
page.get_by_label("Choose a color").select_option("blue")
page.get_by_label("Choose a color").select_option(label="Blue")

# Multi-select: pass a list
page.get_by_label("Choose multiple colors").select_option(["red", "green", "blue"])

# By index
page.get_by_label("Choose a color").select_option(index=2)

Checkboxes and radio buttons use check() / uncheck() / set_checked(bool) uniformly — set_checked works for input[type=checkbox], input[type=radio], and [role=checkbox]:

python
page.get_by_label("I agree to the terms above").check()
expect(page.get_by_label("Subscribe to newsletter")).to_be_checked()
page.get_by_label("XL").set_checked(True)     # Idempotent: no-op if already in that state

10.2 Three Ways to Upload Files ​

Method 1 (most common): set files directly on <input type="file"> — no system dialog needed:

python
# Single file
page.get_by_label("Upload file").set_input_files("myfile.pdf")
# Multiple files
page.get_by_label("Upload files").set_input_files(["file1.txt", "file2.txt"])
# Entire directory
page.get_by_label("Upload directory").set_input_files("mydir")
# Clear selected files
page.get_by_label("Upload file").set_input_files([])

Method 2: upload a Buffer from memory — especially useful for "generate report then upload" flows:

python
page.get_by_label("Upload file").set_input_files(
    files=[{"name": "test.txt", "mimeType": "text/plain", "buffer": b"hello e2e"}]
)

Method 3: the input is dynamically created after a click; catch the system file chooser event with expect_file_chooser:

python
with page.expect_file_chooser() as fc_info:
    page.get_by_text("Choose file").click()
fc_info.value.set_files("myfile.pdf")     # Also accepts a list or Buffer

10.3 Native Dialogs: alert / confirm / prompt ​

Playwright auto-dismisses all dialogs by default. To accept input or click OK, you must register a dialog handler before triggering the action:

python
# Auto-accept all confirms/alerts
page.on("dialog", lambda dialog: dialog.accept())

# prompt requires input text; handle by type separately
def handle(dialog):
    if dialog.type == "prompt":
        dialog.accept("John")
    else:
        dialog.accept()

page.on("dialog", handle)
page.get_by_role("button", name="Delete").click()

Listeners must actually handle the dialog

The official docs explicitly warn: a callback registered with page.on("dialog") that only prints without calling accept/dismiss will permanently deadlock — web dialogs are modal and block page execution. Reading dialog.message without handling is the most common mistake.

10.4 fill, press_sequentially, and Keyboard Operations ​

fill sets the value directly and fires input events (clears existing content first) — it is the default choice for <input>, <textarea>, and [contenteditable]. When you need to simulate real keypresses one by one (autocomplete that counts keydown events, mask inputs), use press_sequentially — it is the successor to the deprecated type() method:

python
page.get_by_role("textbox").fill("Peter")                       # Set value in one shot
page.locator("#area").press_sequentially("Hello!", delay=50)   # Type key by key

# Combo keys and function keys
page.get_by_text("Submit").press("Enter")
page.get_by_role("textbox").press("Control+ArrowRight")

# Hover and drag
page.get_by_text("Item").hover()
source.drag_to(page.get_by_role("listitem", name="done"))      # locator.drag_to

The essential difference: fill is "set value"; press_sequentially is a full keyboard event sequence (keydown/keypress/keyup). Typing into a plain input box key-by-key only wastes test time.

10.5 Date Picker Strategies ​

Date controls fall into two categories with completely different strategies:

  • Native <input type="date">: just fill the standard format string — do not click the calendar panel:
    python
    page.get_by_label("Birth date").fill("2020-02-02")
    page.get_by_label("Appointment time").fill("13:15")            # Time
    page.get_by_label("Local time").fill("2020-03-02T05:15")       # Local datetime
  • JS calendar components (Element UI, Ant Design, etc.): first check if the input accepts typed input (focus + fill); if you must click, locate the year/month toggle buttons and loop-click to the target month, then get_by_role("cell", name=...) to click the specific date. Never hard-code coordinates — they break on any resolution change.

10.6 Chapter Summary ​

  • <select> uses select_option(value/label/index); pass a list for multi-select;
  • File upload prefers set_input_files (single, multiple, directory, Buffer, clear); dynamic inputs use expect_file_chooser;
  • Dialogs are auto-dismissed by default; custom listeners must call accept/dismiss or the page deadlocks;
  • fill sets value; press_sequentially simulates key-by-key typing (replaces deprecated type);
  • Date controls: native → direct fill; componentized → locate and click.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. To select multiple options in a <select> element, you pass?

2. Why does page.on("dialog") require the handler to call accept/dismiss?

3. What is the essential difference between fill() and press_sequentially()?

4. For a native <input type="date">, the correct approach is?

🛠️ Hands-On Exercises ​

  1. Build a local form with a <select multiple>, checkboxes, a file input, and a native <input type="date">; automate filling and submitting each.
  2. Trigger an alert("Delete this?") dialog; register a handler that reads dialog.message then calls dialog.dismiss(); assert the page continues.
  3. For a JS calendar component (e.g., Element UI date picker), write a helper that types the date into the input field via fill(); compare with the click-based approach.

Proceed to Chapter 11: Screenshots, Video, and Trace Viewer.