Skip to content

Get the last matching element ​

Select the last element in a list using index selectors and JavaScript.

In a flow, targeting a specific view is usually straightforward using indexes. For example, if you want the third Add to Basket button, you simply use index: 2 :

yaml
- tapOn:
    text: Add to Basket
    index: 2   # 3rd item. Indexes start at 0

But in a list whose size changes, like a shopping cart, a message thread, or a photo gallery, you do not know the number of the last item in advance. Use a reusable subflow that finds the index of the last item, then taps it.

The workflow ​

Astra Scout has no index: last, so you find the last index by trying each index in turn. The logic is a loop:

  1. Start at index: 0.
  2. Check if the element exists at the current index.
  3. If it exists, increment the counter and check the next index.
  4. When an index is not visible, the index right before it is the last one.

1. Reusable discovery Flow ​

Create a utility subflow (e.g., utils/find_last_element.yaml) that holds the whole loop. It finds the last index and returns it to your main flow.

yaml
# utils/find_last_element.yaml
# Usage: Pass SELECTOR_TYPE ("id" or "text") and SELECTOR_VALUE
# Result: The index is stored in ${output.lastElementIndex}

# Step 1: First, it's necessary to initialize the search state
- evalScript: ${output.lastElementIndex = -1}
- evalScript: ${output._findLastElement_complete = false}

# Step 2: Validate the input type to avoid failures
# Only "id" and "text" selector are supported in this example
- runFlow:
    when:
      true: ${SELECTOR_TYPE != "id" && SELECTOR_TYPE != "text"}
    commands:
      - evalScript: |
          throw new Error("Invalid SELECTOR_TYPE: " + SELECTOR_TYPE + ". Must be 'id' or 'text'");

# Step 3: The Discovery Loop
# Repeat this until you hit an index that doesn't exist.
- repeat:
    while:
      true: ${!output._findLastElement_complete}
    commands:
      # Check if element exists at current index (id selector)
      - runFlow:
          when:
            true: ${SELECTOR_TYPE == "id"}
          commands:
            - runFlow:
                when:
                  visible:
                    id: "${SELECTOR_VALUE}"
                    index: ${output.lastElementIndex + 1}
                commands:
                   # Element found, update index and continue
                  - evalScript: ${output.lastElementIndex = output.lastElementIndex + 1}
            - runFlow:
                when:
                  notVisible:
                    id: "${SELECTOR_VALUE}"
                    index: ${output.lastElementIndex + 1}
                commands:
                  # Element not found. The search is done
                  - evalScript: ${output._findLastElement_complete = true}

      # Check if element exists at current index (text selector)
      - runFlow:
          when:
            true: ${SELECTOR_TYPE == "text"}
          commands:
            - runFlow:
                when:
                  visible:
                    text: "${SELECTOR_VALUE}"
                    index: ${output.lastElementIndex + 1}
                commands:
                  # Element found, update index and continue
                  - evalScript: ${output.lastElementIndex = output.lastElementIndex + 1}
            - runFlow:
                when:
                  notVisible:
                    text: "${SELECTOR_VALUE}"
                    index: ${output.lastElementIndex + 1}
                commands:
                  # Element not found. The search is done
                  - evalScript: ${output._findLastElement_complete = true}

# Step 4: Final Validation
- runFlow:
    when:
      true: ${output.lastElementIndex == -1}
    commands:
      - evalScript: |
          throw new Error("No elements found matching: " + SELECTOR_VALUE);

In essence, this subflow tries each index in turn until none is left (a linear search), written in flow YAML. The key points:

  • It uses an output variable as a counter. A value set with evalScript is kept while the flow runs, so the next loop iteration continues from the increased index.
  • repeat with a while condition based on a boolean flag (_findLastElement_complete, false at first) loops until the list ends, so the list can be any length.
  • If the loop ends and the index is still -1 (no element was found), the subflow reports a clear error and stops, instead of continuing with wrong data.

2. Implementation ​

Call the subflow with runFlow, then use ${output.lastElementIndex} in the next command. Pass the selector type (id or text) and its value.

yaml
# main_test.yaml
# Find the last "Remove" button in a shopping cart and tap it
- runFlow:
    file: ../utils/find_last_element.yaml
    env:
      SELECTOR_TYPE: "text"
      SELECTOR_VALUE: "Remove"

- tapOn:
    text: "Remove"
    index: ${output.lastElementIndex}

Pages about the building blocks used here:

  • Loops: Learn the different ways to iterate in a flow, including while loops and count-based loops.
  • JavaScript overview: Discover how to use JavaScript to manage complex state and logic within your YAML files.
  • How to use Selectors: Review the fundamentals of how Astra Scout identifies and differentiates between similar UI elements.