Appian Locust Library documentation

What is Appian Locust?

Appian Locust is a wrapper library around Locust for load testing Appian. This library is intended to be used as an alternative to tools such as Jmeter and Load Runner.

Appian Locust capabilities

  • Logging in and logging out

  • Form interactions (filling/submitting)

  • Finding and interacting with basic components on a SAIL interface

  • Navigating to records/reports/sites

What is Locust?

It’s an open source python library for doing load testing (think JMeter, but in Python). It is by default HTTP-driven, but can be made to work with other types of interactions. Visit Locust for more information.

Locust has the benefit of relying purely on API requests, which makes it lower overhead than frameworks building on Selenium or browser automation libraries. We have also found python to be common denominator across software and quality engineers, making it a convenient language for extending the framework and defining tests. Using Locust’s model of TaskSets and TaskSequences, it is easy to compose user operations in a maintainable way. Appian-Locust builds on these concepts by defining AppianTaskSet and AppianTaskSequence, which layer on Appian-specific functionality such as login and session management.

SAIL Navigation

Appian interfaces are built with SAIL. It’s a RESTful contract that controls state between the browser/mobile clients and the server.

All SAIL-based interactions require updating a server-side context (or in a stateless mode, passing that context back and forth). These updates are expressed as JSON requests sent back and forth, which are sent as “SaveRequests”, usually to the same endpoint from which the original SAIL form was served. Each SaveRequest, if successful, will return an updated component, or a completely new form if a modal is opened or a button is clicked on a wizard.

Appian Locust abstracts away the specifics of these requests into methods that make it easy to quickly create new workflows for Locust’s virtual users to execute on an Appian instance, For more details on how Appian Locust enables this, check out the How to Write a Locust Test section.

Quick Installation Guide

This is a quick guide to getting up and running with the appian-locust library. You will need Python 3.10 installed on your machine before proceeding.

Setup

  1. Install appian-locust using pip, for more comprehensive projects we recommend using pipenv.

pip install appian-locust

If using pipenv, simply start from the following Pipfile:

[packages]
appian-locust = {version = "*"}

[requires]
python_version = "3.10"

[pipenv]
allow_prereleases = true
  1. Download the sample test example_locustfile.py from the Appian Locust repo and run it.

locust -f example_locustfile.py

If everything is set up correctly, you should see a link to the Locust web interface, which you can use to start test runs and view results.

Build from source

Clone the repository:

git clone -o prod git@gitlab.com:appian-oss/appian-locust.git

Install the library globally:

pip install -e appian-locust

If you’re using a virtualenv or a dependency management tool (e.g. pipenv), you can do the same type of install, but you will want to be in the context of the virtualenv (i.e. source the virtualenv), and you’ll need to pass the path to the repository you cloned.

Note: It’s highly recommended that you use a virtual environment when installing python artifacts. You can follow the instructions here to install virtualenv and pip.

If you have issues installing, make sure you have the proper prerequisites installed for Locust and its dependencies. If you’re having trouble on Windows, check here

Troubleshooting

  • Do not have permissions to clone appian-locust

    • Ensure you have added you ssh key to your profile. See here for how to do this.

  • “locust is not available”

    • Verify that you ran pip install -e appian-locust

  • “Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known”

    • check that host_address is specified correctly in your locust test file.

  • “Login unsuccessful, no multipart cookie found…make sure credentials are correct”

    • check that auth specifies a valid username and password combination for the site you’re testing on in your locust test file.

  • “General request and response debugging”

    • Add self.client.record_mode = True to your HttpUser subclass. Files will be placed in /record_responses where the runner is executed.

How to Write a Locust Test

The majority of the work involved in writing Appian Locust tests is around creating Locust Tasks. Each Task represents a workflow for a virtual Locust user to execute. This section will go over how to get started writing tasks and introduce the two core Appian Locust concepts: the Visitor class and the SailUiForm class.

Sample Workflow

For this workflow, we will use the Employee Record Type found in the Appian documentation. We will implement a Locust Task that will create a new Employee record. The specific workflow will be as follows:

  1. Navigate to the Employee Record List

  2. Click the “New Employee” button

  3. Fill in the First Name, Last Name, Department, Title, and Phone Number fields

  4. Click the “Create” button

Appian Navigation - Visitor

The first step in most workflows is to navigate our user to an interface to interact with. All navigation in Appian Locust can be accomplished via the Visitor. Any kind of Appian interface, including Sites, Records, Reports, Portals and others can be navigated to via the Visitor, and the Visitor will return a SailUiForm which can be used to interact with that interface.

In our case, we want to navigate to a Record Type list. In Appian Locust, that would look like the following:

@task
def create_new_employee(self):
    # Navigate to Employee Record List
    record_list_uiform = self.appian.visitor.visit_record_type(record_type="Employees")

UI Interactions - SailUiForm

Now that we have navigated to the Employee Record List, we need to execute the workflow steps that will create the new Employee. As briefly touched on above, all navigations done via the Visitor return a SailUiForm which is capable of performing interactions with a UI. SailUiForm supports filling in text fields, clicking buttons and more.

In our specific case, because we navigated to a Record list, our visitor returned a subclass of the SailUiForm: the RecordListUiForm. Some types of interfaces in Appian have a specific subclass that will support additional functionality catered to the kind of interface it represents. The RecordListUiForm will enable us to click on the Record List Action which is unique to Record Lists via click_record_list_action, like so:

@task
def create_new_employee(self):
    # Navigate to Employee Record List
    record_list_uiform = self.appian.visitor.visit_record_type(record_type="Employees")

    # Click on "New Employee" Record List Action
    record_list_uiform.click_record_list_action(label="New Employee")

As shown above, in many cases the only thing required to interact with a UI element is the label associated with that element.

At this point in the workflow, the dialog to create a new employee has been launched. Now we can use the various other interactions supported by the base SailUiForm class which are available on all of its subclasses to fill out the new Employee’s information:

@task
def create_new_employee(self):
    # Navigate to Employee Record List
    record_list_uiform = self.appian.visitor.visit_record_type(record_type="Employees")

    # Click on "New Employee" Record List Action
    record_list_uiform.click_record_list_action(label="New Employee")

    # Fill in new Employee information
    record_list_uiform.fill_text_field(label="First Name", value="Sample")
    record_list_uiform.fill_text_field(label="Last Name", value="User")
    record_list_uiform.fill_text_field(label="Department", value="Engineering")
    record_list_uiform.fill_text_field(label="Title", value="Senior Software Engineer")
    record_list_uiform.fill_text_field(label="Phone Number", value="(703) 442-8844")

Now all we need to do is click the “Create” button, and our new Employee will be created!

@task
def create_new_employee(self):
    # Navigate to Employee Record List
    record_list_uiform = self.appian.visitor.visit_record_type(record_type="Employees")

    # Click on "New Employee" Record List Action
    record_list_uiform.click_record_list_action(label="New Employee")

    # Fill in new Employee information
    record_list_uiform.fill_text_field(label="First Name", value="Sample")
    record_list_uiform.fill_text_field(label="Last Name", value="User")
    record_list_uiform.fill_text_field(label="Department", value="Engineering")
    record_list_uiform.fill_text_field(label="Title", value="Senior Software Engineer")
    record_list_uiform.fill_text_field(label="Phone Number", value="(703) 442-8844")

    # Create Employee!
    record_list_uiform.click_button(label="Create")

If you run a locust test with the task above, you should be able to check the Employee record list and see the “Sample User” employees that the virtual Locust user just made! You can see a full version of a locust test including the task we just wrote here.

How to Run Locust

Once you have the library installed, you can simply follow the output from running locust -h to run your test. Note that if you don’t specify a locustfile with -f FILE_NAME, locust will look for a file called locustfile in the current directory by default.

Command Line Flow

If you’re running Locust somewhere where there is no web ui, or you don’t want to bother with the web flow, I tend to run Locust like so:

locust -f examples/example_locustfile.py -u 1 -r 10 -t 3600 --headless

Required Arguments

This is the bare minimum to run a Locust test without the web view. You’ll notice you need to specify:

  • The hatch rate (-r is for rate)

  • The number of users (-u is for users)

  • The time of the test (-t) in seconds

  • The –headless flag

Once you run this command, the test will start immediately, and start logging output. It should run for the duration you run the test, and hitting ctrl+c may orphan some locusts. Some arguments that we use are

  • –csv-full-history -> prints out the different percentile changes every 30 seconds

It’s recommended to capture log file output when running Locust as well, i.e. | tee run.log

Web Flow

If you supply no arguments to locust other than the locustfile, Locust will launch in a web mode. This is not recommended, mainly because you can’t automate running it as it requires manual interaction as well as access to the flask application.

locust -f example_locustfile.py

If you navigate to http://localhost:8089/ you’ll see the following:

Locust web view

These arguments map to the same arguments described in the Required Arguments section.

Once you hit “start swarming”, you’ll see graphs reporting latencies, errors, and other data. These can be useful for visually understanding how a load test is performing.

Debugging

Oftentimes you will encounter errors or not get the appropriate load you expect when running tests.

Things you can use to get more information:

  1. Add print statements to your Locust code or the installed appian-locust library

  2. Inspect the output of the latencies that Locust periodically prints out, to see if certain requests are much slower than you expect

  3. Verify using the browser console that the requests you are attempting to simulate match up with what Locust/appian-locust is sending

  4. Setting the “record_mode” attribute to True on your HttpUser’s client object will create a “recorded_responses” folder which will contain all requests and responses sent during test execution. You can do this in the __init__ method of your HttpUser, like so:

def __init__(self, environment) -> None:
    super().__init__(environment)
    self.client.record_mode = True

Limitations

Disclaimer: This library is continuously evolving. Currently the main focus is supporting essential use-cases. We are happy to accept contributions to further extend functionality, address bug fixes and improve usability. Please see the Contributing section and feel free to reach out.

Currently unsupported Appian Interactions

  • Multiple links with the same name on the same page, use labels to differentiate

  • Legacy forms

Limitations when running Locust

  • On Windows, running locust in distributed mode is not supported

Advanced Appian Locust Usage

Loading Test Settings from Config

These three lines look for a config.json file at the location from which the script is run (not where the locustfile is).

from appian_locust.utilities import loadDriverUtils

utls = loadDriverUtils()
utls.load_config()

This takes the content of the config.json file and places it into a variable as utls.c. This allows us to access configurations required for logging in inside the class that extends HttpUser:

config = utls.c
auth = config['auth']
host = "https://" + config['host_address']

A minimal config.json looks like:

{
    "host_address": "site-name.appiancloud.com",
    "auth": [
        "user.name",
        "password"
    ]
}

Advanced Test Examples

Locust Test Example: Records

An example of a Locust Test showing interaction with Appian Records - example_locust_test_records.py.

This test has 2 locust tasks defined and it will execute all three for each spawned locust user for the duration of the test.

  • Task 1 will visit a random record instance for a random record type and return the SAIL form.

  • Task 2 will visit a random record type list and get the SAIL form for it, then filter the records in the list.

@task takes an optional weight argument that can be used to specify the task’s execution ratio. For example: If there are two tasks with weights 3 and 6 then second task will have twice the chance of being picked as first task.

# Locust tests executing against Appian with a TaskSet should set AppianTaskSet as their base class to have access to various functionality.
# This class handles creation of basic objects like self.appian (appian client) and actions like `login` and `logout`

class RecordsTaskSet(AppianTaskSet):

    def on_start(self):
        super().on_start()
        # could be either view or list:
        if "endpoint_type" not in CONFIG:    # could be either view or list:
            logger.error("endpoint_type not found in config.json")
            sys.exit(1)

        self.endpoint_type = CONFIG["endpoint_type"]

        # view - to view records from opaqueId
        # list - to list all the record types from url_stub
        if "view" not in self.endpoint_type and "list" not in self.endpoint_type:
            logger.error(
                "This behavior is not defined for the provided endpoint type. Supported types are : view and list")
            sys.exit(1)

    def on_stop(self):
        logger.info("logging out")
        super().on_stop()

    @task(X)
    def visit_random_record(self):
        if "view" in self.endpoint_type:
            self.appian.visitor.visit_record_instance()

    @task(X)
    # this task visits a random record type list and return the SAIL form.
    def visit_random_record_type_list(self):
        if "list" in self.endpoint_type:
            record_list = self.appian.records.visit_record_type_and_get_form()
            record_list.filter_records_using_searchbox("Favorite Record Name")
  • By calling super().on_start() inside the on_start() function of the locust test you get access to the appian client which allows you to call self.appian.visitor, self.appian.system_operator etc. These properties allow us to navigate to a specific object or access metadata about available objects.

  • Functions like visit_XYZ() access the actual appian object and return its SAIL form as an instance of uiform. This class contains methods that helps you interact with the UI.

Locust Test Example: Grids

An example of a Locust Test showing interaction with Appian Grids - example_locust_test_grids.py.

This test has a locust task defined that will interact with a read-only paging grid layout that contains an Employee directory. Configuration for this grid layout is inspired by Appian’s Grid Tutorial. The goal of this test is to select all Engineering employees and view the details for one of them.

The first step in this workflow is to navigate our user to a report which is backed by an interface containing the grid:

@task
def interact_with_grid_in_interface(self):
    # Navigate to the interface backed report that contains a grid
    report_uiform = self.appian.visitor.visit_report(report_name="Employee Report with Grid")

The interface will look similar to this:

Report with a grid

Now that we have navigated to the report, we will sort the grid by the Department field in the ascending order to have all Engineering department employees at the top:

@task
def interact_with_grid_in_interface(self):
    # Navigate to the interface backed report that contains a grid
    report_uiform = self.appian.visitor.visit_report(report_name="Employee Report with Grid")

    # Sort the grid rows by the "Department" field name
    report_uiform.sort_paging_grid(label="Employee Directory", field_name="Department", ascending=True)

The interface with the sorted grid will look similar to this:

Grid sorted by Department field

Next, we will select the first five rows on the first page of the grid:

@task
def interact_with_grid_in_interface(self):
    # Navigate to the interface backed report that contains a grid
    report_uiform = self.appian.visitor.visit_report(report_name="Employee Report with Grid")

    # Sort the grid rows by the "Department" field name
    report_uiform.sort_paging_grid(label="Employee Directory", field_name="Department", ascending=True)

    # Select the first five rows on the first page of the grid
    report_uiform.select_rows_in_grid(rows=[0,1,2,3,4], label="Employee Directory")

Because the grid is configured to show the selected rows under Selected Employees, the resultant interface will look similar to this:

Selected rows in first page of grid

During development, this would be a good way to test the selection using the JSON response from the above request. Next, we will move to the second page of the grid and select the first row since it also contains an Engineering employee:

@task
def interact_with_grid_in_interface(self):
    # Navigate to the interface backed report that contains a grid
    report_uiform = self.appian.visitor.visit_report(report_name="Employee Report with Grid")

    # Sort the grid rows by the "Department" field name
    report_uiform.sort_paging_grid(label="Employee Directory", field_name="Department", ascending=True)

    # Select the first five on the first page of the grid
    report_uiform.select_rows_in_grid(rows=[0,1,2,3,4], label="Employee Directory")

    # Move to the second page of the grid
    report_uiform.move_to_right_in_paging_grid(label="Employee Directory")

    # Select the first row on the second page of the grid
    report_uiform.select_rows_in_grid(rows=[0], label="Employee Directory", append_to_existing_selected=True)

The interface will look similar to this:

Selected first row in second page of grid

The grid contains a First Name column which is a link to the employee record. Finally, we will click on the link for an employee William:

@task
def interact_with_grid_in_interface(self):
    # Navigate to the interface backed report that contains a grid
    report_uiform = self.appian.visitor.visit_report(report_name="Employee Report with Grid")

    # Sort the grid rows by the "Department" field name
    report_uiform.sort_paging_grid(label="Employee Directory", field_name="Department", ascending=True)

    # Select the first five on the first page of the grid
    report_uiform.select_rows_in_grid(rows=[0,1,2,3,4], label="Employee Directory")

    # Move to the second page of the grid
    report_uiform.move_to_right_in_paging_grid(label="Employee Directory")

    # Select the first row on the second page of the grid
    report_uiform.select_rows_in_grid(rows=[0], label="Employee Directory", append_to_existing_selected=True)

    # Click on the row with a record link with the given label
    report_uiform.click_record_link(label="William")

The user will be navigated to the employee’s record which will look similar to this:

Record view for William Ross

You can see a full version of this locust test here. There are other useful functions for interacting with grids that can be found in our documentation.

Locust Test Example: Multiple Users

An example of a Locust Test showing interaction with the frontend and admin pages - example_multi_user_locustfile.py.

This test has 2 locust TaskSets defined and 2 HttpUsers defined that simulate different login information.

TaskSets

  • The GetFrontPageTaskSet simply navigates to a basic user landing page

  • The GetAdminPageTaskSet navigates to the admin console

HttpUsers

  • The FrontendUserActor uses the login credentials for the regular frontend user

  • The AdminUserActor uses the login credentials for the admin user

Running

When running this locustfile, it is important to note that the users specified when running are spread evenly across the HttpUsers. If you only specify one user to run when running locust, it will only choose one user actor to start:

locust -f examples/example_multi_user_locustfile.py --headless -r 10 -t 3600 --users 1
...
All users hatched: FrontendUserActor: 1, AdminUserActor: 0 (0 already running)

Make sure to run the locustfile with at least as many users as required by how you have the weights configured for each HttpUser.

For the included sample, the weights are 3 and 1 respectively, meaning you’ll have to spawn 4 users to get the AdminUserActor to start up

locust -f examples/example_multi_user_locustfile.py  --headless -r 10 -t 3600 --users 4
...
All users hatched: FrontendUserActor: 3, AdminUserActor: 1 (0 already running)

Advanced Task Execution

Executing a specific number of tasks

One can also write a test that executes a set number of iterations of your TaskSequence Class and all its tasks, instead of executing the test for X number of seconds/mins/hours. Here’s a snippet showing how to run a test for a set number of iterations.

import json
import os
from locust import HttpUser, task, between, events

from appian_locust import AppianTaskSet

@events.init.add_listener
def on_locust_init(environment, **kw):
    global ENV
    ENV = environment


class OrderedEndToEndTaskSequence(AppianTaskSequence):
    @task
    def nav_to_random_site(self):
        pass

    @task
    def nav_to_specific_site(self):
        pass

    @task
    def increment_iteration_counter(self):
        logger.info(f"Iteration Number: {self.iterations}")
        # Stop the test if 40 iterations of the set have been completed.
        # This would mean approximately 40K requests in total for the test.
        if self.iterations >= CONFIG["num_of_iterations"]:
            logger.info(f"Stopping the Locust runner")
            ENV.runner.quit()
        else:
            logger.info(f"Incrementing the iteration set counter")
            self.iterations += 1

class UserActor(HttpUser):
    tasks = [GetFrontPageTaskSet]
    config_file = "./example_config.json"
    CONFIG = {}
    if os.path.exists(config_file):
        with open(config_file, 'r') as config_file:
            CONFIG = json.load(config_file)
    else:
        raise Exception("No config.json found")
    host = f'https://{CONFIG["host_address"]}'
    auth = CONFIG["auth"]
    wait_time = between(0.500, 0.500)

Note: CONFIG["num_of_iterations"] is retrieved from the test configuration. This should be provided in the example_config.json file.

The way to achieve specific number of tasks in this test is by having a counter in your task, that you increment once in a specific Locust task and then stop the test when you have reached the desired number of iterations.

Waiting until all users are spawned

If you want to wait for your test to spawn all of the Locust users

from gevent.lock import Semaphore

all_locusts_spawned = Semaphore()
all_locusts_spawned.acquire()

@events.spawning_complete.add_listener
def on_spawn_complete(**kw):
    print("All news users can start now!")
    all_locusts_spawned.release()

class WaitingTaskSet(AppianTaskSet):

    def on_start(self):
        """ Executes before any tasks begin."""
        super().on_start()
        all_locusts_spawned.wait()

Latest Release

Version 2.0.0

Appian Locust v2.0 introduces a significant rework of the API to guide a clear and streamlined development experience. Our target was to meet feature parity while simplifying the steps to interact with Appian.

New Paradigm

  • Visitor is the new hub for all SailUiForm navigations. From the client, you can call various methods to retrieve the desired SailUiForm that matches the type of page that the caller has navigated to, which will enable further interaction with the represented UI.

  • SystemOperator is for non UI form interactions at the system level (i.e. get_webapi()).

  • The info module extended from AppianClient provides metadata for News, Tasks, Records, Reports, Actions, and Sites (i.e. appian_locust.appian_client.AppianClient.actions_info).

Breaking Changes

  • Appian Locust now requires Python 3.10. Update your dependencies globally or within your dependency management config file.

  • Fetching SailUIForms from News, Tasks, Records, Reports, Actions, and Sites have been marked as private. Use Visitor to handle all UI navigations.

  • SailUIForms types can be found in the uiform module.

  • Design objects and types have been moved to the objects module.

  • Various helper methods have been moved to the utilities module.

  • loadDriverUtils() does not provide utls anymore. Instead, call loadDriverUtils() to set utls:

    from appian_locust.utilities import loadDriverUtils
    utls = loadDriverUtils()
    

For a more comprehensive list of changes in Appian Locust 2.0, see the Appian Locust 2.0 Migration Guide document.

Appian Locust 2.0 Migration Guide

_actions.py

The _Actions class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

get_actions_interface

Not available anymore. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

get_actions_feed

Not available anymore. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

get_all

We can do the same operation from the actions_info.py method named “get_all_available_actions”.

all_available_actions = self.appian.actions_info.get_all_available_actions(..)

get_action

We can do the same operation from the actions_info.py method named “get_action_info”.

specific_action_info = self.appian.actions_info.get_action_info(..)

visit_and_get_form

We can perform the same operation by the “visit_action” method from visitor.py.

uiform = self.appian.visitor.visit_action(..)

visit

Not available. Call the “visit_action” method from visitor.py instead.

uiform = self.appian.visitor.visit_action(..)

start_action

We can perform the same operation by calling “start_action” in system_operator.py

response = self.system_operator.start_action(...)

_admin.py

The _Admin class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

visit

We can do the same operation by calling “visit_admin” method in visitor.py

uiform = self.appian.visitor.visit_admin()

_design.py

The _Design class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

visit

We can do the same operation by calling “visit_design” method in visitor.py

uiform = self.appian.visitor.visit_design()

visit_object

We can do the same operation by calling “visit_design_object_by_id” method in visitor.py

design_object_uiform = self.appian.visitor.visit_design_object_by_id(..)

visit_app

We can do the same operation by calling “visit_application_by_id” method in visitor.py

application_uiform = self.appian.visitor.visit_application_by_id(..)

create_application

We can do the same operation by calling “create_application” method in design_uiform.py

design_uiform = self.appian.visit_design()

create_record_type

We can do the same operation by calling “create_record_type” method in application_uiform.py

application_uiform = self.appian.visitor.visit_application_by_id(..)

create_report

We can do the same operation by calling “create_recport” method in application_uiform.py

application_uiform = self.appian.visitor.visit_application_by_id(..)

_news.py

The _News class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

get_all

We can do the same operation by calling the “get_all_available_entries” method in news_info.py.

news_dict = self.appian.news_info.get_all_available_entries(..)

get_news

We can do the same operation by calling the “get_news_entry” method in news_info.py.

specific_news_info = self.appian.news_info.get_news_entry(..)

visit

Not available. Call “get_news_entry” method in news_info.py instead.

specific_news_info = self.appian.news_info.get_news_entry(..)

visit_news_entry

Not available. Call “get_news_entry” method in news_info.py instead.

specific_news_info = self.appian.news_info.get_news_entry(..)

search

We can do the same operation by calling the “get_all_available_entries” with a search string argument.

uiform = self.appian.news_info.get_all_available_entries(..)

_records.py

The _Records class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

visit_record_instance_and_get_feed_form

Not available. Call “visit_record_instance” method in visitor.py instead.

record_instance_uiform = self.appian.visitor.visit_record_instance(..)

visit_record_instance_and_get_form

We can do the same operation by calling the “visit_record_instance” method in visitor.py

record_instance_uiform = self.appian.visitor.visit_record_instance(..)

visit_record_type_and_get_form

We can do the same operation by calling the “visit_record_type” method in visitor.py

record_list_uiform = self.appian.visitor.visit_record_type(..)

get_all

We can do the same operation from the record_list_uiform.py method named “get_visible_record_instances”.

record_list_uiform = self.appian.visitor.visit_record_type(..)
all_records_info = record_list_uiform.get_visible_record_instances(..)

get_all_record_types

“get_all_available_record_types” in records_info.py

records_dict = self.appian.records_info.get_all_available_record_types(..)

get_all_records_of_record_type

“get_visible_record_instances” in record_list_uiform.py

record_list_uiform = self.appian.visitor.visit_record_type(..)
all_records_info = record_list_uiform.get_visible_record_instances(..)

get_records_interface

Not available. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

get_records_nav

Not available. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

get_all_records_of_record_type_mobile

To interact with an Appian instance as a mobile client, pass in “is_mobile_client=True” in the AppianTaskSet on_start method.

class SampleTaskSet(AppianTaskSet)
def on_start(self):
super().on_start(is_mobile_client=True)

get_all_mobile

To interact with an Appian instance as a mobile client, pass in “is_mobile_client=True” in the AppianTaskSet on_start method.

class SampleTaskSet(AppianTaskSet)
def on_start(self):
super().on_start(is_mobile_client=True)

fetch_record_instance

Not available. Instead call “visit_record_instance” method in visitor.py

record_instance_uiform = self.appian.visitor.visit_record_instance(..)

fetch_record_type

Not available. Instead call “visit_record_type” method in visitor.py

record_list_uiform = self.appian.visitor.visit_record_type(..)

visit_record_instance

Not available. Instead call “visit_record_instance” method in visitor.py

record_instance_uiform = self.appian.visitor.visit_record_instance(..)

visit_record_type

Not available. Instead call “visit_record_type” method in visitor.py

record_list_uiform = self.appian.visitor.visit_record_type(..)

_reports.py

The _Reports class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

get_reports_interface

Not available anymore. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

get_reports_nav

Not available anymore. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

get_all

We can do the same operation by calling the “get_all_available_reports” in reports_info.py

reports_dict = self.appian.reports_info.get_all_available_reports(..)

get_report

We can do the same operation by calling the “get_report_info” in reports_info.py

report_info = self.appian.reports_info.get_report_info(..)

visit_and_get_form

We can do the same operation by calling the “visit_report” in visitor.py

uiform = self.appian.visitor.visit_report(..)

visit

Not available. Instead call “visit_report” in visitor.py

uiform = self.appian.visitor.visit_report(..)

_tasks.py

The _Tasks class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

get_all

We can do the same operation by calling “get_all_available_tasks” method in tasks_info.py

tasks_dict = self.appian.tasks_info.get_all_available_tasks(..)

get_task_pages

Not available. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

get_next_task_page_uri

Not available. Handled internally by the framework whenever it is necessary, so any calls to this can be removed without replacement.

visit_and_get_form

We can do the same operation by calling “visit_task” method in visitor.py

uiform = self.appian.visitor.visit_task(..)

visit

Not available. Instead call “visit_task” in visitor.py

uiform = self.appian.visitor.visit_task(..)

_sites.py

The _Sites class is no longer available. You may migrate any functionality using this class as follows:

Method in 1.x

Method in 2.x

Example Usage

navigate_to_tab

Not available. Instead call “visit_site” in visitor.py

uiform = self.appian.visitor.visit_site(..)

navigate_to_tab_and_record_if_applicable

Not available. Instead call “visit_site_recordlist_and_get_random_record_form” in visitor.py

record_instance_uiform = self.appian.visitor.visit_site_recordlist_and_get_random_record_form(..)

navigate_to_tab_and_record_get_form

We can do the same operation by calling “visit_site_recordlist_and_get_random_record_form” in visitor.py

record_instance_uiform = self.appian.visitor.visit_site_recordlist_and_get_random_record_form(..)

get_all

We can do the same operation by calling “get_all_available_sites” in sites_info.py

sites_dict = self.appian.sites_info.get_all_available_sites()

get_site_data_by_site_name

We can do the same operation by calling “get_site_info” in sites_info.py

specific_site = self.appian.sites_info.get_site_info(..)

get_page_names_from_ui

Not available. Instead call “get_site_info” in sites_info.py

specific_site = self.appian.sites_info.get_site_info(..)

get_site_page

Not available. You can get page information from the Site object returned by “get_site_info” in sites_info.py

specific_site = self.appian.sites_info.get_site_info(..)

visit_and_get_form

We can do the same operation by calling “visit_site” in visitor.py

uiform = self.appian.visitor.visit_site(..)

_app_importer.py

The _app_importer module is no longer available. You may migrate any functionality using this module as follows:

Method in 1.x

Method in 2.x

Example Usage

import_app

Available on DesignUiForm.py as “import_application”

design_uiform = self.appian.visitor.visit_design()
design_uiform.import_application(..)

uiform.py

The following methods in SailUiForm have removed or modified:

Method in 1.x

Method in 2.x

Example Usage

get_record_header_form

Available on RecordInstanceUiForm.py as “get_header_view”

record_form = self.appian.visitor.visit_record_instance(“record_type”, “record_name”)
record_header_form = record_form.get_header_view()

get_record_view_form

Available on RecordInstanceUiForm.py as “get_summary_view”

record_form = self.appian.visitor.visit_record_instance(“record_type”, “record_name”)
record_header_form = record_form.get_summary_view()

get_response

Not available. Instead use “get_latest_state”

ui_form.get_latest_state()

latest_state

Not available. Instead use “get_latest_state”

ui_form.get_latest_state()

get_latest_form

Not available. This method basically just returned “this”, so it was unnecessary.

click_record_link

This method now returns a new type, a RecordInstanceUiForm, so you must make sure to save the return value into a new variable

record_uiform: RecordInstanceUiform = uiform.click_record_link(..)

_records_helper.py

This class and all its methods are not accessible anymore.

_interactor.py

This class and all its methods are not accessible anymore. There are corresponding methods in other new classes/existing classes.

Import Changes

Class/Module

Import in V1

Import in V2

helper

from appian_locust.helper import *

from appian_locust.utilities.helper import *

Site

from appian_locust._sites import Site

from appian_locust.objects import Site

Page

from appian_locust._sites import Page

from appian_locust.objects import Page

PageType

from appian_locust._sites import PageType

from appian_locust.objects import PageType

utls

from appian_locust.loadDriverUtils import utls

from appian_locust.utilities import loadDriverUtils
utls = loadDriverUtils()

API

class appian_locust.appian_client.AppianClient(session: HttpSession, host: str, base_path_override: str | None = None, portals_mode: bool = False, config_path: str = './config.json', is_mobile_client: bool = False)

Bases: object

property actions_info: ActionsInfo

Navigate to actions and gather information about available actions

get_client_feature_toggles() None
login(auth: list | None = None, check_login: bool = True) Tuple[HttpSession, Response]
logout() None

Logout from Appian

property news_info: NewsInfo

Navigate to news and fetch information on news entries

property records_info: RecordsInfo

Navigate to records and gather information about available records

property reports_info: ReportsInfo

Navigate to reports and gather information about available reports

property sites_info: SitesInfo

Get Site metadata object

property system_operator: SystemOperator

Abstraction used for system operation that do not require a UI

property tasks_info: TasksInfo

Navigate to tasks and gather information about available tasks

property visitor: Visitor

Visitor that can be used to navigate to different types of pages in an Appian instance

appian_locust.appian_client.appian_client_without_locust(host: str, record_mode: bool = False, base_path_override: str | None = None) AppianClient

Returns an AppianClient that can be used without locust to make requests against a host, e.g.

>>> appian_client_without_locust()
>>> client.login(auth=('username', 'password'))
>>> client.get_client_feature_toggles()

This can be used for debugging/ making CLI style requests, instead of load testing You MUST call client.get_client_feature_toggles() to correctly finish initializing the client.

Returns:

an Appian client that can be used

Return type:

AppianClient

class appian_locust.appian_task_set.AppianTaskSequence(parent: SequentialTaskSet)

Bases: SequentialTaskSet, AppianTaskSet

Appian Locust SequentialTaskSet. Provides functionality of Locust’s SequentialTaskSet and Handles creation of basic objects like``self.appian`` and actions like login and logout

tasks: List[TaskSet | Callable] = []

Collection of python callables and/or TaskSet classes that the User(s) will run.

If tasks is a list, the task to be performed will be picked randomly.

If tasks is a (callable,int) list of two-tuples, or a {callable:int} dict, the task to be performed will be picked randomly, but each task will be weighted according to its corresponding int value. So in the following case, ThreadPage will be fifteen times more likely to be picked than write_post:

class ForumPage(TaskSet):
    tasks = {ThreadPage:15, write_post:1}
class appian_locust.appian_task_set.AppianTaskSet(parent: TaskSet)

Bases: TaskSet

property appian: AppianClient

A wrapper around the generated AppianClient

on_start(portals_mode: bool = False, config_path: str = './config.json', is_mobile_client: bool = False) None

Overloaded function of Locust’s default on_start.

It will create object self.appian and logs in to Appian

Parameters:
  • portals_mode (bool) – set to True if connecting to portals site

  • config_path (str) – path to configuration file

  • is_mobile_client (bool) – set to True if client should act as mobile

on_stop() None

Overloaded function of Locust’s default on_stop.

It logs out the client from Appian.

override_default_flags(flags_to_override: List[FeatureFlag]) None

override_default_flags gets the flag mask to set all of the flags to true given a list of flag enums and overrides the current feature flag extended value to set these flags to true.

tasks: List[TaskSet | Callable] = []

Collection of python callables and/or TaskSet classes that the User(s) will run.

If tasks is a list, the task to be performed will be picked randomly.

If tasks is a (callable,int) list of two-tuples, or a {callable:int} dict, the task to be performed will be picked randomly, but each task will be weighted according to its corresponding int value. So in the following case, ThreadPage will be fifteen times more likely to be picked than write_post:

class ForumPage(TaskSet):
    tasks = {ThreadPage:15, write_post:1}
class appian_locust.feature_flag.FeatureFlag(value)

Bases: Enum

An enumeration.

ALL_FEATURES = 1
ALWAYS_ADD_RECORD_TYPE_INFORMATION = 45
BILLBOARD_LAYOUT = 35
BODY_URI_TEMPLATES = 14
BOX_LAYOUT = 20
CARD_LAYOUT = 39
CERTIFIED_SAIL_EXTENSION = 47
COMPACT_URI_TEMPLATES = 10
DATA_EXPORT = 23
DOCUMENT_VIEWER_LAYOUT = 44
EVOLVED_BILLBOARD_LAYOUT = 50
EVOLVED_GRIDFIELD = 52
FILTERS_LAYOUT = 48
FLUSH_RECORD_HEADERS = 57
GAUGE_FIELD = 55
GRID_ROW_SELECTION = 40
ICON_WIDGET = 31
IMAGES_INTERFACES_2 = 27
IMAGE_CROPPING = 21
IMPLICIT_SYSTYPE_NAMESPACE = 5
INLINE_TASK_CONTROLS = 12
IN_APP_BROWSER_AUTH = 59
JUSTIFIED_LABEL_POSITION = 22
LESS_OPAQUE_BILLBOARD_OVERLAYS = 58
MEDIUM_LARGE_RICH_TEXT = 19
MODERN_RECORD_TYPES_LIST = 38
MULTIPLE_SAVE_USER_FILTERS = 60
MULTI_SELECT_RECORD_FILTERS = 17
NESTED_COLUMNS = 16
NEWS_ENTRY_LAYOUT = 43
NEWS_SUBSCRIPTION_SETTINGS = 46
NEW_COLUMN_WIDTHS = 53
NEW_RICH_TEXT_SIZES = 54
NO_FEATURES = 0
OFFLINE = 6
PARTIAL_RENDERING = 7
POSITIVE_NEGATIVE_RICH_TEXT = 18
REACT_CLIENT = 11
RECORD_ACTION_COMPONENT = 61
RECORD_CHROME = 34
RECORD_LIST_FEED_ITEM_DTO = 36
RECORD_NEWS = 4
RECORD_NEWS_FIELD = 37
RELATIVE_URI_TEMPLATES = 13
REST_REDIRECT = 9
RICH_TEXT_ACCENT_STYLE = 32
SAIL_FORMS = 2
SHORT_CIRCUIT_PARTIAL_RENDERING = 8
SIDE_BY_SIDE_LAYOUT = 28
SITE_RECORD_NEWS = 24
SUBMISSION_LOCATION = 51
TAG_FIELD = 56
TASK_FORM_LAYOUT = 33
TASK_PREVIEW = 3
TWO_PART_RECORD_TAG_URI = 15
USE_CLIENT_LOCALE = 41
USE_MULTIPART_RECORD_UIS = 42
WCC_READ_ONLY = 29
WCC_READ_WRITE = 30
class appian_locust.system_operator.SystemOperator(interactor: _Interactor, actions: _Actions)

Bases: object

Class for providing the ability to perform activities that do not require a UI interaction i.e. triggering an action without a startform.

fetch_autosuggestions(payload: Dict[str, Any], locust_request_label: str | None = None) Response

Retrieve suggestions from autosuggest endpoint :param payload: payload containing expression details to retrieve suggestions for :param locust_request_label: the label to be displayed by locust

Returns: Json response of suggestions

fetch_content(opaque_id: str, locust_request_label: str | None) Response

Fetch a content element, such as an image :param opaque_id: The opaque id of the content to download :param locust_request_label: label to associate request with

Returns: Response object containing information of downloaded content

get_webapi(uri: str, headers: Dict[str, Any] | None = None, locust_request_label: str | None = None, query_parameters: Dict[str, Any] = {}) Response

Make a GET request to a web api endpoint :param uri: API URI to be called :param headers: header for the REST API Call :param locust_request_label: the label to be displayed by locust :param query_parameters: Queries/Filters

Returns: Json response of GET operation

To set custom headers

>>> headers = {'Is-Admin': 'true'}
... self.appian.system_operator.get_webapi('/suite/webapi/headers', headers=headers)

To set custom query parameters

>>> params = {'age': 5, 'start-date': '10-05-2020'}
... self.appian.system_operator.get_webapi('/suite/webapi/query', query_parameters=params)
post_webapi(uri: str, headers: Dict[str, Any] | None = None, locust_request_label: str | None = None) Response

Make a GET request to a web api endpoint :param uri: API URI to be called :param headers: header for the REST API Call :param locust_request_label: the label to be displayed by locust

Returns: Json response of GET operation

To set custom headers

>>> headers = {'Is-Admin': 'true'}
... self.appian.system_operator.post_webapi('/suite/webapi/headers', headers=headers)
start_action(action_name: str, skip_design_call: bool = False, exact_match: bool = False) Response

Perform the post operation on action’s API to start specific action. Actions that do not have a UI can be called directly without using “GET” to retrieve the UI. this is controlled by the optional skip_design_call parameter

Parameters:
  • action_name (str) – Name of the action

  • skip_design_call (bool, optional) – to skip the “GET” call for the action’s UI. Default : False

  • exact_match (bool, optional) – Should action name match exactly or to be partial match. Default : False

Returns: requests.models.Response

Example

>>> self.appian.site_helper.start_action("action_name")
class appian_locust.visitor.Visitor(interactor: _Interactor, tasks: _Tasks, reports: _Reports, actions: _Actions, records: _Records, sites: _Sites)

Bases: object

Provides methods to get an interactable SailUiForm from an Appian instance. Each method will return the respected SailUiForm type for which it will allow interactions with the visited page.

visit_action(action_name: str, exact_match: bool = False, locust_request_label: str | None = None) SailUiForm

Gets the action by name and returns the corresponding SailUiForm to interact with

If the action is activity chained, this will attempt to start the process and retrieve the chained SAIL form.

Parameters:
  • action_name (str) – Name of the action to be called. Name of the action will be in the below pattern. “displayLabel::opaquqId”

  • exact_match (bool, optional) – Should action name match exactly or to be partial match. Default : False

  • locust_request_label (str, optional) – label to be used within locust. Default: ‘’ (empty string)

Returns: SailUiForm

Examples

If the full name of the action is known, with the opaque ID,

>>> self.appian.visitor.visit_action("action_name:igB0K7YxC0UQ2Fhx4hicRw...", exact_match=True)

If only the display name is known, or part of the display name

>>> self.appian.visitor.visit_action("action_name")
>>> self.appian.visitor.visit_action("actio")
visit_admin(locust_request_label: str | None = None) SailUiForm

Navigates to /admin :param locust_request_label: label to be used within locust :type locust_request_label: str, optional

Returns: SailUiForm

visit_ai_skill_by_id(opaque_id: str, locust_request_label: str | None = None) AISkillUiForm

Visit an AI Skill by its opaque id :param opaque_id: opaque id of the AI Skill :type opaque_id: str :param locust_request_label: label to be used within locust :type locust_request_label: str, optional

Returns (AISkillUiForm): UiForm representing AI Skill

visit_ai_skill_by_name(ai_skill_name: str, locust_request_label: str | None = None) AISkillUiForm

Visit an AI Skill by its name :param ai_skill_name: The name of the AI Skill :type ai_skill_name: str :param locust_request_label: label to be used within locust :type locust_request_label: str, optional

Returns (AISkillUiForm): UiForm representing AI Skill

visit_application_by_id(application_id: str, locust_request_label: str | None = None) ApplicationUiForm

Visit an application by its opaque id

Parameters:
  • application_id (str) – The opaque id of the application

  • locust_request_label (str, optional) – label to be used within locust

Returns (ApplicationUiForm): UiForm representing design application page

visit_application_by_name(application_name: str, application_prefix: str | None = None, locust_request_label: str | None = None) ApplicationUiForm

Visit an application by name

Parameters:
  • application_name (str) – The name of the application

  • application_prefix (str, optional) – The prefix of the application. Required if the application has a prefix.

  • locust_request_label (str, optional) – label to be used within locust

Returns (ApplicationUiForm): UiForm representing design application page

visit_data_fabric(locust_request_label: str | None = None) SailUiForm

Navigate to Data Fabric :param locust_request_label: label to be used within locust :type locust_request_label: str, optional

Returns (SailUiForm): UiForm representing Data Fabric

visit_design(locust_request_label: str | None = None) DesignUiForm

Navigate to /design :param locust_request_label: label to be used within locust :type locust_request_label: str, optional

Returns (DesignUiForm): UiForm representing /design

visit_design_object_by_id(opaque_id: str, locust_request_label: str | None = None) DesignObjectUiForm

Visit a design object by its opaque id :param opaque_id: opaque id of the design object :type opaque_id: str :param locust_request_label: label to be used within locust :type locust_request_label: str, optional

Returns (DesignObjectUiForm): UiForm representing design object

visit_design_object_by_name(object_name: str, object_type: DesignObjectType, locust_request_label: str | None = None) DesignObjectUiForm

Visit a design object by its name and type :param object_name: The name of the design object :type object_name: str :param object_type: The type of the design object :type object_type: DesignObjectType :param locust_request_label: label to be used within locust :type locust_request_label: str, optional

Returns (DesignObjectUiForm): UiForm representing design object

visit_portal_page(portal_unique_identifier: str, portal_page_unique_identifier: str, locust_request_label: str | None = None) SailUiForm

Navigate to portal’s page by url and returns the corresponding SailUiForm to interact with

Parameters:
  • portal_unique_identifier (str) – portal web address unique identifier

  • portal_page_unique_identifier (str) – web address unique identifier for specific page in portal

  • locust_request_label (str, optional) – label to be used within locust

Returns: SailUiForm

Examples

If we have portal up and running with 2 pages with title “page1” and “page2”, we can visit any portal page with help of this method.

In order to visit “page1” with url (in browser: https://mysite.appian-internal.com/performance-testing/page/page1), we would use

>>> self.appian.visitor.visit_portal_page("performance-testing", "page1")

In order to visit “page2” with url (in browser: https://mysite.appian-internal.com/performance-testing/page/page2), we would use

>>> self.appian.visitor.visit_portal_page("performance-testing", "page2")

Note: sometimes when portal has just 1 page (for example page with title ‘page1’). appian use only “https://mysite.appian-internal.com/performance-testing” (in browser) instead of https://mysite.appian-internal.com/performance-testing/page/page1 . although it still works.

visit_record_instance(record_type: str = '', record_name: str = '', view_url_stub: str = '', exact_match: bool = False, summary_view: bool = True, locust_request_label: str | None = None) RecordInstanceUiForm

Navigate to a specific record and return a RecordUiForm

Parameters:
  • record_type (str) – Record Type Name. If not specified, a random record type will be selected.

  • record_name (str) – Name of the record to be called. If not specified, a random record will be selected.

  • view_url_stub (str, optional) – page/tab to be visited in the record. If not specified, “summary” dashboard will be selected.

  • exact_match (bool, optional) – Should record type and record name matched exactly as it is or partial match.

  • summary_view (bool, optional) – Should the Record UI be returned in Summary View, if false will return Header View

  • locust_request_label (str, optional) – Label locust should associate this request with

Returns (RecordUiForm): The UI for the record instance

visit_record_type(record_type: str = '', locust_request_label: str | None = None) RecordListUiForm

This function calls the API for the specific record type and returns a SAIL form representing the list of records for that record type.

Parameters:
  • record_type (str) – Record Type Name. If not specified, a random record type will be selected.

  • locust_request_label (str, optional) – Label locust should associate this request with

Returns (SailUiForm): UI representing list of records for that record type

visit_report(report_name: str, exact_match: bool = True, locust_request_label: str | None = None) SailUiForm

Navigate to a report and return a SailUiForm for that report’s UI

Parameters:
  • report_name (str) – Name of the report to be called.

  • exact_match (bool, optional) – Should report name match exactly or to be partial match. Default : True

  • locust_request_label (str, optional) – Label locust should associate this request with

Returns (SailUiForm): Response of report’s Get UI call in SailUiForm

visit_site(site_name: str, page_name: str, locust_request_label: str | None = None) SailUiForm

Get a SailUiForm for a Task, Report or Action

Parameters:
  • site_name (str) – Site where the page exists

  • page_name (str) – Page to navigate to

  • locust_request_label (str, optional) – Label locust should associate this request with

Returns: SailUiForm

Example

>>> self.appian.visitor.visit_site("site_name","page_name")
visit_site_recordlist(site_name: str, page_name: str, locust_request_label: str | None = None) RecordListUiForm

Get a RecordListUiForm for a record list page on a site

Parameters:
  • site_name (str) – Site where the page exists

  • page_name (str) – Page to navigate to

  • locust_request_label (str, optional) – label to be used within locust

NOTE: The actual Type of the Site Page MUST be “Record List”, this will not work for sites that are of other page types,

such as an Interface with a record grid.

Returns: SailUiForm

Example

>>> self.appian.visitor.visit_site_recordlist("site_name","page_name")
visit_site_recordlist_and_get_random_record_form(site_name: str, page_name: str, locust_request_label: str | None = None) RecordInstanceUiForm

Navigates to a site page that is a recordlist then clicks on a random record instance on the first page

Parameters:
  • site_name – Site Url stub

  • page_name – Page Url stub

  • locust_request_label (str, optional) – label to be used within locust

NOTE: The actual Type of the Site Page MUST be “Record List”, this will not work for sites that are of other page types,

such as an Interface with a record grid.

Returns: RecordInstanceUiForm

visit_task(task_name: str, exact_match: bool = True, locust_request_label: str | None = None) SailUiForm

Gets the SailUiForm given a task name

Parameters:
  • task_name (str) – Name of the task to search for

  • exact_match (bool, optional) – Whether or not a full match is returned. Defaults to True.

  • locust_request_label (str, optional) – label to be used within locust

Returns:

SAIL form for the task

Return type:

SailUiForm

Exceptions module

exception appian_locust.exceptions.exceptions.BadCredentialsException

Bases: Exception

exception appian_locust.exceptions.exceptions.ChoiceNotFoundException

Bases: Exception

exception appian_locust.exceptions.exceptions.ComponentNotFoundException

Bases: Exception

exception appian_locust.exceptions.exceptions.IncorrectDesignAccessException(object_type: str, correct_access_method: str)

Bases: Exception

exception appian_locust.exceptions.exceptions.InvalidComponentException

Bases: Exception

exception appian_locust.exceptions.exceptions.InvalidDateRangeException(start_date: date, end_date: date)

Bases: Exception

exception appian_locust.exceptions.exceptions.InvalidSiteException

Bases: Exception

exception appian_locust.exceptions.exceptions.MissingConfigurationException(missing_keys: list)

Bases: Exception

exception appian_locust.exceptions.exceptions.MissingCsrfTokenException(found_cookies: dict)

Bases: Exception

exception appian_locust.exceptions.exceptions.PageNotFoundException

Bases: Exception

exception appian_locust.exceptions.exceptions.SiteNotFoundException

Bases: Exception

Info module

class appian_locust.info.actions_info.ActionsInfo(actions: _Actions)

Bases: object

get_action_info(action_name: str, exact_match: bool = False) Dict[str, Any]

Get the information about specific action by name.

Parameters:
  • action_name (str) – Name of the action

  • exact_match (bool) – Should action name match exactly or to be partial match. Default : False

Returns (dict): Specific Action’s info

Raises: In case of action is not found in the system, it throws an “Exception”

Example

If full name of action is known:

>>> action_info.get_action_info("action_name", exact_match=True)

If only the display name is known, or part of the display name:

>>> action_info.get_action_info("actio")
get_all_available_actions(locust_request_label_breadcrumb: str = 'Actions.MainMenu.AvailableActions') Dict[str, Any]

Retrieves all the available “actions” and associated metadata from “Appian-Tempo-Actions”

Parameters:

locust_request_label_breadcrumb (str) – Base label used for each of the multiple requests made by this method

Returns (dict): List of actions and associated metadata

Examples

>>> actions_info.get_all_available_actions()
class appian_locust.info.news_info.NewsInfo(news: _News)

Bases: object

Class which provides metadata about news entries from the Tempo News tab

get_all_available_entries(search_string: str | None = None, locust_request_label: str = 'News.AllAvailableEntries') Dict[str, Any]

Retrieves all the available “news” and associated metadata from “Appian-Tempo-News”

Parameters:
  • search_string (str, optional) – results will be filtered based on the search string.

  • locust_request_label (str) – Label locust should associate with the request

Returns (dict): List of news and associated metadata

Examples

>>> news_info.get_all()
get_news_entry(news_name: str, exact_match: bool = True, search_string: str | None = None) Dict[str, Any]

Get the information about specific news by name.

Parameters:
  • news_name (str) – name of the news entry

  • exact_match (bool, optional) – Should news name match exactly or to be partial match. Default : True

  • search_string (str, optional) – results will be filtered based on the search string.

Returns: Specific News’s info

Raises: In case news is not found in the system, it throws an “Exception”

Example

If full name of action is known,

>>> news_info.get_news("news_name")

If only partial name is known,

>>> news_info.get_news("news_name", exact_match=False)
get_news_entry_record_tags(news_name: str, locust_request_label: str | None = None) Response | None

Get the record tags associated with a news entry :param news_name: News entry ID :type news_name: str :param locust_request_label: Label locust should associate with the request for record tags :type locust_request_label: str, optional

Returns:

get_news_entry_related_records(news_name: str, exact_match: bool = True, search_string: str | None = None, locust_request_label: str | None = None) Response | None

Request related records information for a news entry :param news_name: name of the news entry :type news_name: str :param exact_match: Should news name match exactly or to be partial match. Default : True :type exact_match: bool, optional :param search_string: results will be filtered based on the search string. :type search_string: str, optional :param locust_request_label: Label locust should associate with the request for related records :type locust_request_label: str, optional

Returns: Response object with related records information, if any exists

class appian_locust.info.records_info.RecordsInfo(records: _Records)

Bases: object

Class which provides metadata about available record types from the Tempo Records tab

get_all_available_record_types(locust_request_label: str = 'Records.MainMenu_ViewAll') Dict[str, Any]

Get all metadata for visible record types on Tempo Records.

Returns (dict): List of record types and associated metadata

class appian_locust.info.reports_info.ReportsInfo(reports: _Reports)

Bases: object

Class which provides metadata about available reports from the Tempo Reports tab

get_all_available_reports(search_string: str | None = None, locust_request_label: str | None = None) Dict[str, Any]

Retrieves all the available “reports” and associated metadata from “Appian-Tempo-Reports”

Returns (dict): List of reports and associated metadata

Examples

>>> reports_info.get_all_available_reports()
get_report_info(report_name: str, exact_match: bool = True) Dict[str, Any]

Get the information about specific report by name.

Parameters:
  • report_name (str) – Name of the action

  • exact_match (bool) – Should action name match exactly or to be partial match. Default : True

Returns (dict): Specific Report’s info

Raises: In case of report is not found in the system, it throws an “Exception”

Example

If full name of report is known:

>>> report_info.get_report_info("report_name", exact_match=True)

If only the display name is known, or part of the display name:

>>> report_info.get_report_info("report")
class appian_locust.info.sites_info.SitesInfo(sites: _Sites)

Bases: object

Class which provides metadata about available Sites

get_all_available_sites() Dict[str, Site]

Retrieves all the available “sites” and associated metadata

Returns (dict): List of sites and associated metadata

Examples

>>> sites_info.get_all_available_sites()
get_site_info(site_name: str, exact_match: bool = True) Site

Get the information about specific site by name.

Parameters:

site_name (str) – Name of the action

Returns (Site): Specific site’s info

Raises: In case of site is not found in the system, it throws an “Exception”

Example

If full name of site is known:

>>> site_info.get_site_info("site_name", exact_match=True)

If only the display name is known, or part of the display name:

>>> site_info.get_site_info("site")
class appian_locust.info.tasks_info.TasksInfo(tasks: _Tasks)

Bases: object

Class which provides metadata about available tasks from the Tempo Tasks tab

get_all_available_tasks(locust_request_label: str = 'Tasks.MainMenu.GetAllAvailable') Dict[str, Any]

Retrieves all the available “tasks” and associated metadata from “Appian-Tempo-Tasks”

Parameters:

locust_request_label (str) – Label locust should associate with the request

Returns (dict): List of tasks and associated metadata

Examples

>>> tasks_info.get_all_available_tasks()
get_task_info(task_name: str, exact_match: bool = True) Dict[str, Any]

Get the information about specific task by name.

Parameters:
  • task_name (str) – Name of the action

  • exact_match (bool) – Should action name match exactly or to be partial match. Default : True

Returns (dict): Specific Task’s info

Raises: In case of task is not found in the system, it throws an “Exception”

Example

If full name of task is known:

>>> task_info.get_task_info("task_name", exact_match=True)

If only the display name is known, or part of the display name:

>>> task_info.get_task_info("task")

Objects module

class appian_locust.objects.ai_skill.AiSkill(host_url: str, object_uuid: str)

Bases: object

class appian_locust.objects.ai_skill_type.AISkillObjectType(value)

Bases: Enum

An enumeration.

DOCUMENT_CLASSIFICATION = 2
DOCUMENT_EXTRACTION = 5
EMAIL_CLASSIFICATION = 8
class appian_locust.objects.application.Application(name: str, opaque_id: str)

Bases: object

Class representing an application

class appian_locust.objects.design_object.DesignObject(name: str, opaque_id: str)

Bases: object

Class representing an Design Object

class appian_locust.objects.design_object.DesignObjectType(value)

Bases: Enum

An enumeration.

DATA_TYPE = 'Data Type'
DECISION = 'Decision'
EXPRESSION_RULE = 'Expression Rule'
INTEGRATION = 'Integration'
INTERFACE = 'Interface'
RECORD_TYPE = 'Record Type'
SITE = 'Site'
TRANSLATION_SET = 'Translation Set'
WEB_API = 'Web API'
class appian_locust.objects.page.Page(page_name: str, page_type: PageType, group_name: str | None = None)

Bases: object

Class representing a single Page within a site

class appian_locust.objects.page.PageType(value)

Bases: Enum

An enumeration.

ACTION: str = 'action'
INTERFACE: str = 'interface'
RECORD: str = 'recordType'
REPORT: str = 'report'
class appian_locust.objects.site.Site(name: str, display_name: str, pages: Dict[str, Page])

Bases: object

Class representing a single site, as well as its pages

Uiform module

class appian_locust.uiform.ai_skill_uiform.AISkillUiForm(rdo_interactor: _RDOInteractor, rdo_state: Dict[str, Any], ai_skill_id: str, breadcrumb: str = 'AISkillUi')

Bases: SailUiForm

save_ai_skill_changes(locust_request_label: str | None = None) AISkillUiForm
Saves an AI Skill Object. This is done in two parts:
  1. Clicking on the Save Changes button which creates an LCP request.

  2. Persisting the model to the RDO Server

Parameters:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (AISkillUiForm): The latest state of the AI Skill UiForm

Example

>>> form.save_ai_skill_changes()
upload_documents_to_multiple_file_upload_field(label: str, file_paths: List[str], locust_request_label: str | None = None) AISkillUiForm

Uploads multiple documents to an MLAS upload field.

Parameters:
  • label (str) – Currently Unused

  • file_paths (list) – List of document file paths in string form

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (AISkillUiForm): The latest state of the AI Skill UiForm

Example

>>> form.upload_document_to_multiple_file_upload_field(["/usr/local/appian/File1.pdf", "/usr/local/appian/File2.pdf"])
class appian_locust.uiform.application_uiform.ApplicationUiForm(interactor: _Interactor, state: Dict[str, Any], breadcrumb: str = 'ApplicationUi')

Bases: SailUiForm

click_ai_skill(ai_skill_name: str, locust_request_label: str | None = None) AISkillUiForm

Click on an AI Skill in the design object grid. The current view of the grid must contain the skill you wish to click. :param ai_skill_name: The name of the AI Skill to click on

Returns (AISkillUiForm): UiForm representing UI of AI Skill

click_design_object(design_object_name: str, locust_request_label: str | None = None) DesignObjectUiForm

Click on a design object in the design object grid. The current view of the grid must contain the object you wish to click. :param design_object_name: The name of the design object to click on

Returns (DesignObjectUiForm): UiForm representing UI of design object

create_ai_skill_object(ai_skill_name: str, ai_skill_type: AISkillObjectType) ApplicationUiForm

Creates an AI Skill with the given name

Returns: The SAIL UI Form after the record type is created

create_record_type(record_type_name: str) ApplicationUiForm

Creates a record type with the given name

Returns: The SAIL UI Form after the record type is created

create_report(report_name: str) ApplicationUiForm

Creates a report with the given name

Returns: The SAIL UI Form after the report is created

filter_design_objects(design_object_types: list[appian_locust.objects.design_object.DesignObjectType]) ApplicationUiForm

Filter the design object list in an Application, must be on page with design object list :param design_object_types: List of the types of objects you wish to filter on :type design_object_types: DesignObjectType

Returns (ApplicationUiForm): ApplicationUiForm with filtered list of design objects

get_available_design_objects() Dict[str, DesignObject]

Retrieve all available design objects in the application, must be on page with design object list

Returns (dict): Dictionary mapping design object names to DesignObject

search_objects(search_str: str, locust_label: str | None = None) ApplicationUiForm

Search the design object list in an Application, must be on page with design object list :param search_str: The string to search :type search_str: str :param locust_label: Label to associate request with :type locust_label: str

Returns (ApplicationUiForm): A UiForm with updated state after the search is complete

class appian_locust.uiform.design_object_uiform.DesignObjectUiForm(interactor: _Interactor, state: Dict[str, Any], breadcrumb: str = 'DesignObjectUi')

Bases: SailUiForm

click_record_type_application_navigation_tab(navigation_tab_label: str, locust_request_label: str | None = None) DesignObjectUiForm

Interacts with an ApplicationNavigationLayout component. This interaction involves clicking the Section Items in the navigation sidebar tab of Record Type e.g. Actions, Views, etc.

Parameters:

navigation_tab_label (str) – The label or identifier for the navigation component.

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics.

Returns (DesignObjectUiForm): The latest state of the UiForm.

Examples

>>> form.click_record_type_application_navigation_tab(navigation_page_label="Actions")
launch_query_editor() DesignObjectUiForm

Calls the post operation to click on the LaunchVQD button in the toolbar for the ExpressionEditorWidget. This will launch the query editor with the expression currently in the expression editor.

Returns (DesignObjectUiForm): UiForm updated with state representing launched query editor

class appian_locust.uiform.design_uiform.DesignUiForm(interactor: _Interactor, state: Dict[str, Any], breadcrumb: str = 'DesignUi')

Bases: SailUiForm

click_application(application_name: str, locust_request_label: str | None = None) ApplicationUiForm

Click on an application in the /design application grid. Must be on the current page to be clicked.

Parameters:
  • application_name (str) – The name of the application to click on

  • locust_request_label (str, optional) – label to be used within locust

Returns (ApplicationUiForm): The latest state of the UiForm, representing the application clicked on

create_application(application_name: str) ApplicationUiForm

Creates an application and returns a form within representing the app contents

Returns: The SAIL UI Form

filter_design_objects(design_object_types: list[appian_locust.objects.design_object.DesignObjectType]) DesignUiForm

Filter the design object list in /design, must be on page with design object list :param design_object_types: List of the types of objects you wish to filter on :type design_object_types: DesignObjectType

Returns (DesignUiForm): DesignUiForm with filtered list of design objects

get_available_applications() Dict[str, Application]

Retrieve all available applications in /design. Must be on page with application list

Returns (dict): Dictionary mapping application names to Application

get_available_design_objects() Dict[str, DesignObject]

Retrieve all available design objects in the application. Must be on page with design object list

Returns (dict): Dictionary mapping design object names to DesignObject

import_application(app_file_path: str, customization_file_path: str | None = None, inspect_and_import: bool = False) None

Import an application into the Appian instance. :param app_file_path: Local path to the application zip file :param customization_file_path: Local path to customization file :param inspect_and_import: Set to true if Appian Locust should “Inspect” before importing

Returns: None

search_applications(search_str: str, locust_label: str | None = None) DesignUiForm

Search the application list in /design, must be on page with application list :param search_str: The string to search :type search_str: str :param locust_label: Label to associate request with :type locust_label: str

Returns (DesignUiForm): A UiForm with updated state after the search is complete

search_objects(search_str: str, locust_label: str | None = None) DesignUiForm

Search the design object list in /design, must be on page with design object list :param search_str: The string to search :type search_str: str :param locust_label: Label to associate request with :type locust_label: str

Returns (DesignUiForm): A UiForm with updated state after the search is complete

class appian_locust.uiform.record_list_uiform.RecordListUiForm(interactor: _Interactor, state: Dict[str, Any], breadcrumb: str = 'RecordListUi')

Bases: SailUiForm

UiForm representing a Record List from Tempo Records

clear_records_search_filters() RecordListUiForm

Clear any search filters on the records list

Returns: Unfiltered RecordsListUiForm

click_record_list_action(label: str, locust_request_label: str | None = None) RecordListUiForm

Click on an action in a record list :param label: The label of the record list action to click :param locust_request_label: The label locust should associate with this request

Returns: UiForm with action clicked

This method allows you to Filter the Record Type List (displaying record instance for a specific record type) which makes the same request when typing something in the search box and reloading the page. More interactions (with the filtered list) can be performed on the returned SailUiForm Object.

Note: This function does not require unfocusing from the search box as you would in the UI.

Parameters:
  • search_term (str, optional) – Term to filter records list to

  • locust_label (str, optional) – label to associate request with

Examples

>>> form.filter_records_using_searchbox('Donuts')

Returns (RecordListUiForm): The record type list UiForm with the filtered results.

get_visible_record_instances(column_index: int | None = None) Dict[str, Any]

Retrieve information about all visible records on the page. :param column_index: Which column to retrieve record information for. If no column is selected, every record link in the UI will be retrieved.

Returns: Dictionary with record instance information

class appian_locust.uiform.record_uiform.RecordInstanceUiForm(interactor: _Interactor, state: Dict[str, Any], summary_view: bool = True, breadcrumb: str = 'RecordUi')

Bases: SailUiForm

UiForm representing a Record Instance UI. Supports both summary and header record views. Defaults to summary view

get_header_view() RecordInstanceUiForm

Get the header view of the record instance Returns (RecordInstanceUiForm): UiForm updated to header view

get_summary_view() RecordInstanceUiForm

Get the summary view of the record instance Returns (RecordInstanceUiForm): UiForm updated to summary view

class appian_locust.uiform.uiform.SailUiForm(interactor: _Interactor, state: Dict[str, Any], breadcrumb: str = 'SailUi')

Bases: object

assert_no_validations_present() SailUiForm

Raises an exception if there are validations present on the form, otherwise, returns the form as is

Returns (SailUiForm): Form as it was when validations were asserted

check_checkbox_by_label(label: str, indices: List[int], locust_request_label: str = '') SailUiForm

Checks a checkbox by its label Indices are positions to be checked

Parameters:
  • label (str) – Value for label of the checkbox

  • indices (str) – Indices of the checkbox to check. Pass None or empty to uncheck all

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.check_checkbox_by_label('myLabel', [1])  # checks the first item
>>> form.check_checkbox_by_label('myLabel', None) # unchecks
check_checkbox_by_test_label(test_label: str, indices: List[int], locust_request_label: str = '') SailUiForm

Checks a checkbox by its testLabel attribute Indices are positions to be checked

Parameters:
  • test_label (str) – Value for the testLabel attribute of the checkbox

  • indices (str) – Indices of the checkbox to check. Pass None or empty to uncheck all

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.check_checkbox_by_test_label('myTestLabel', [1])  # checks the first item
>>> form.check_checkbox_by_test_label('myTestLabel', None) # unchecks
click(label: str, is_test_label: bool = False, locust_request_label: str = '', index: int = 1) SailUiForm

Clicks on a component on the form, if there is one present with the following label (case sensitive) Otherwise throws a NotFoundException

Can also be called as ‘click_link’ or ‘click_button’ to convey intent

This can also click StartProcessLinks or ProcessTaskLinks

Parameters:
  • label (str) – Label of the component to click

  • is_test_label (bool) – If you are clicking a button or link via a test label instead of a label, set this boolean to true

Keyword Arguments:
  • locust_request_label (str) – Label used to identify the request for locust statistics

  • index (int) – Index of the component to click if more than one match the label criteria (default: 1)

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click('Submit')
>>> form.click('SampleTestLabel', is_test_label = True)
click_button(label: str, is_test_label: bool = False, locust_request_label: str = '', index: int = 1) SailUiForm

Clicks on a component on the form, if there is one present with the following label (case sensitive) Otherwise throws a NotFoundException

This can also click StartProcessLinks or ProcessTaskLinks

Parameters:
  • label (str) – Label of the component to click

  • is_test_label (bool) – If you are clicking a button via a test label instead of a label, set this boolean to true

Keyword Arguments:
  • locust_request_label (str) – Label used to identify the request for locust statistics

  • index (int) – Index of the component to click if more than one match the label criteria (default: 1)

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click_button('Save')
>>> form.click_link('Update')
click_card_layout_by_index(index: int, locust_request_label: str = '') SailUiForm

Clicks a card layout link by index. This method will find the CardLayout component on the UI by index and then perform the click behavior on its Link component.

Parameters:

index (int) – Index of the card layout on which to click. (first found card layout , or second etc.) (Indices start from 1)

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

This finds link field on the 2nd card layout on the page and clicks it. It handles StartProcessLink as well, so no need to call click_start_process_link() when it is on a CardLayout.

>>> form.click_card_layout_by_index(2)

Click on a link in a grid with plaintext values

Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • column_name (str) – The name of the column the link is in

  • row_index (int) – The row in the column to click on, 0 indexed

  • grid_label (str) – The label of the grid, if index is not supplied

  • grid_index (str) – the index of the grid, if label is not supplied

  • locust_request_label (str, optional) – The label locust should associate this request with

Returns (SailUiForm): The latest state of the UiForm

Click on a Record link in a grid with plaintext values

Either a label or an index is required, indices are useful if there is no title for the grid

NOTE: This method returns a NEW RecordInstanceUiForm object, so you must save its return value into a new variable, like so:

>>> record_uiform = other_uiform.click_grid_plaintext_record_link(...)
Parameters:
  • column_name (str) – The name of the column the link is in

  • row_index (int) – The row in the column to click on, 0 indexed

  • grid_label (str) – The label of the grid, if index is not supplied

  • grid_index (str) – the index of the grid, if label is not supplied

  • locust_request_label (str, optional) – The label locust should associate this request with

Returns (SailUiForm): The latest state of the UiForm

Click on a link in a grid with RichText values

Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • column_name (str) – The name of the column the link is in

  • row_index (int) – The row in the column to click on, 0 indexed

  • grid_label (str) – The label of the grid, if index is not supplied

  • grid_index (str) – the index of the grid, if label is not supplied

  • locust_request_label (str, optional) – The label locust should associate this request with

Returns (SailUiForm): The latest state of the UiForm

Click on a Record link in a grid with RichText values

Either a label or an index is required, indices are useful if there is no title for the grid

NOTE: This method returns a NEW RecordInstanceUiForm object, so you must save its return value into a new variable, like so:

>>> record_uiform = other_uiform.click_grid_rich_text_record_link(...)
Parameters:
  • column_name (str) – The name of the column the link is in

  • row_index (int) – The row in the column to click on, 0 indexed

  • grid_label (str) – The label of the grid, if index is not supplied

  • grid_index (str) – the index of the grid, if label is not supplied

  • locust_request_label (str, optional) – The label locust should associate this request with

Returns (SailUiForm): The latest state of the UiForm

Clicks on a component on the form, if there is one present with the following label (case sensitive) Otherwise throws a NotFoundException

This can also click StartProcessLinks or ProcessTaskLinks

Parameters:
  • label (str) – Label of the component to click

  • is_test_label (bool) – If you are clicking a link via a test label instead of a label, set this boolean to true

Keyword Arguments:
  • locust_request_label (str) – Label used to identify the request for locust statistics

  • index (int) – Index of the component to click if more than one match the label criteria (default: 1)

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click_link('Update')
click_menu_item_by_choice_index(label: str, choice_index: int, is_test_label: bool = False, locust_request_label: str = '') SailUiForm

Clicks an item in a MenuLayout provided the index of the chosen MenuItem ValueError is thrown if component found is NOT a MenuLayout, IndexError is thrown if the provided choice index is out of bounds for the available menu items

Parameters:
  • label (str) – Label of the MenuLayout

  • choice_index (str) – Index of the MenuItem to select

  • is_test_label (bool) – True if you are finding a MenuLayout by test label instead of a label, False o.w.

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click_menu_item_by_choice_index("changeScopeMenu", 0, True, locust_request_label="Create Scope")
click_menu_item_by_name(label: str, choice_name: str, is_test_label: bool = False, locust_request_label: str = '') SailUiForm

Clicks an item in a MenuLayout provided the primaryText of the chosen MenuItem ValueError is thrown if component found is NOT a MenuLayout, OR if the MenuLayout doesn’t contain specified choice

Parameters:
  • label (str) – Label of the MenuLayout

  • choice_name (str) – PrimaryText of the MenuItem to select

  • is_test_label (bool) – True if you are finding a MenuLayout by test label instead of a label, False o.w.

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click_menu_item_by_name("changeScopeMenu", "Scope 2", True, locust_request_label="Change Scope")

Click a record link on the form if there is one present with the following label (case sensitive) Otherwise throws a ComponentNotFoundException

Parameters:
  • label (str) – Label of the record link to click

  • is_test_label (bool) – If you are clicking a record link via a test label instead of a label, set this boolean to true

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

NOTE: This method returns a NEW RecordInstanceUiForm object, so you must save its return value into a new variable, like so:

>>> record_uiform = other_uiform.click_record_link(...)

Returns (RecordUiForm): The record form (feed) for the linked record.

Click the index’th record link on the form if there is one present with an attribute matching attribute_value If no attribute is provided, the index’th record link is selected from all record links in the form Otherwise throws a ComponentNotFoundException

NOTE: This method returns a NEW RecordInstanceUiForm object, so you must save its return value into a new variable, like so:

>>> record_uiform = other_uiform.click_record_link_by_attribute_and_index(...)
Keyword Arguments:
  • attribute (str) – Attribute to check for ‘attribute_value’ (default: “”)

  • attribute_value (str) – Attribute value of record link to click (default: “”)

  • index (int) – Index of record link to click (default: 1)

  • locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The record form (feed) for the linked record.

Click the index’th record link on the form Otherwise throws a ComponentNotFoundException

Parameters:

index (int) – Index of the record link to click (1-based)

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The record form (feed) for the linked record.

click_record_search_button_by_index(index: int = 1, locust_request_label: str = '') SailUiForm

Clicks the Search button of a record grid.

Parameters:

index (int) – Index of the record search button on the form

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click_record_search_button_by_index(1)

Click a record view link on the form if there is one present with the following label (case sensitive) Otherwise throws a ComponentNotFoundException

Parameters:

label (str) – Label of the record link to click

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

NOTE: This method returns a NEW RecordInstanceUiForm object, so you must save its return value into a new variable, like so:

>>> record_uiform = other_uiform.click_record_view_link(...)

Returns (SailUiForm): The record form (feed) for the linked record.

Clicks a related action (either a related action button or link) on the form by label If no link is found, throws a ComponentNotFoundException

Parameters:

label (str) – Label of the related action

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

How to use click_related_action():

Use records function - visit_record_instance_and_get_feed_form() to get Record Instance SailUiForm, then get the header response and finally click on the related action by label.

>>> feed_form = records.visit_record_instance_and_get_feed_form()

We need to get the header response or view response depending on if the related action is under the related actions dashboard or if it is a related action link on the summary view UI (which opens in a dialog).

>>> header_form = feed_form.get_record_header_form() or feed_form.get_record_view_form()
>>> header_form.click_related_action('Request upgrade')

Clicks a start process link on the form by label If no link is found, throws a ComponentNotFoundException

Parameters:

label (str) – Label of the link

Keyword Arguments:
  • is_mobile (bool) – Boolean to use the mobile form of the request

  • locust_request_label (str) – Label used to identify the request for locust statistics

  • is_test_label (bool) – Boolean indicating if label is a test label

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click_start_process_link('Request upgrade')

Clicks a start process link on the form by label (for Mobile) If no link is found, throws a ComponentNotFoundException

Parameters:
  • label (str) – Label of the link

  • site_name (str) – Name of the site (i.e. the Sites feature)

  • page_name (str) – Name of the page within the site

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.click_start_process_link_on_mobile('Open Issue')
click_tab_by_label(tab_label: str, tab_group_test_label: str, locust_request_label: str = '') SailUiForm

Selects a Tab by its label and its tab group’s testLabel

Parameters:
  • tab_label (str) – Label of the tab to select

  • tab_group_test_label (str) – Test Label of the tab group (tab is part of a tab group)

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples:

fill_cascading_pickerfield(label: str, selections: List[str], format_test_label: bool = True, locust_request_label: str = '') SailUiForm

Select a choice for a cascading pickerfield, one where multiple choices can be chained together

Parameters:
  • label (str) – Label of the field to fill out

  • selections (str) – The series of options to select through

Keyword Arguments:
  • format_test_label (bool) – If you don’t want to prepend a “test-” to the testLabel, set this to False

  • locust_request_label (str) – Label to associate in locust statistics with selecting the picker choice

fill_date_field(label: str, date_input: date, locust_request_label: str = '') SailUiForm

Fills a date field with the specified date

Parameters:
  • label (str) – Label of the date field

  • date_input (date) – Date used to fill the field

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_date_field('Today', datetime.date.today())
>>> form.fill_date_field('Date of Birth', datetime.date(1992, 12, 30))
fill_datetime_field(label: str, datetime_input: datetime, locust_request_label: str = '') SailUiForm

Fills a datetime field with the specified datetime

NOTE: this does one api call for both the date and time, whereas filling the elements on screen requires two separate evaluations, one to fill the date field and one to fill the time field. This is the way the request would look if one of the fields were already filled.

Parameters:
  • label (str) – Label of the datetime field

  • datetime_input (date) – Date time used to fill the field

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_datetime_field('Now', datetime.datetime.now())
>>> form.fill_datetime_field('Date and Time of Birth', datetime.datetime(1992, 12, 30, 12, 30, 5))
fill_field_by_any_attribute(attribute: str, value_for_attribute: str, text_to_fill: str, locust_request_label: str = '', index: int = 1) SailUiForm

Selects a Field by “attribute” and its value provided “value_for_attribute” and fills it with text “text_to_fill”

Parameters:
  • attribute (str) – Name of the component to fill

  • value_for_attribute (str) – Value for the attribute passed in to this function

  • text_to_fill (str) – Value to fill the field with

Keyword Arguments:
  • locust_request_label (str) – Label used to identify the request for locust statistics

  • index (int) – Index of the field to fill if more than one match the label criteria (default: 1)

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_field_by_any_attribute("placeholder", "Write a comment", "Hello, Testing")
# selects the Component with the "placeholder" attribute having "Write a comment" value
# and fills it with "Hello, Testing"
fill_field_by_attribute_and_index(attribute: str, attribute_value: str, fill_value: str, index: int = 1, locust_request_label: str = '') SailUiForm

Selects a Field by “attribute” and its value provided “attribute_value” and an index if more than one Field is found and fills it with text “fill_value”

Parameters:
  • attribute (str) – Name of the field to fill

  • attribute_value (str) – Value for the attribute passed in to this function

  • fill_value (str) – Value to fill in the field

Keyword Arguments:
  • index (int) – Index of the field to fill if more than one match the attribute and attribute_value criteria (default: 1)

  • locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_field_by_attribute_and_index("label", "Write a comment", "Hello, Testing")
# selects the first Component with the "label" attribute having "Write a comment" value
# and fills it with "Hello, Testing"
>>> form.fill_field_by_attribute_and_index("label", "Write a comment", "Hello, Testing", 2)
# selects the second Component with the "label" attribute having "Write a comment" value
# and fills it with "Hello, Testing"
fill_field_by_index(type_of_component: str, index: int, text_to_fill: str, locust_request_label: str = '') SailUiForm

Selects a Field by its index and fills it with a text value

Parameters:
  • type_of_component (str) – Name of the component to fill

  • index (int) – Index of the field on the page (is it the first one found, or second etc.)

  • value (int) – Value to fill in the field of type ‘type_of_component’

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_field_by_index("ParagraphField", 1, "Hello, Testing")
# selects the first ParagraphField with the value "Hello, Testing"
fill_paragraph_field(label: str, value: str, is_test_label: bool = False, locust_request_label: str = '', index: int = 1) SailUiForm

Fills a field on the form, if there is one present with the following label (case sensitive) Otherwise throws a NotFoundException

Parameters:
  • label (str) – Label of the field to fill out

  • value (str) – Value to fill the field with

Keyword Arguments:
  • is_test_label (bool) – If you are filling a text field via a test label instead of a label, set this boolean to true

  • locust_request_label (str) – Label used to identify the request for locust statistics

  • index (int) – Index of the field to fill if more than one match the label criteria (default: 1)

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_text_field('Title','My New Novel')
fill_picker_field(label: str, value: str, identifier: str = 'id', format_test_label: bool = True, fill_request_label: str = '', pick_request_label: str = '') SailUiForm

Enters the value in the picker widget and selects one of the suggested items if the widget is present with the following label (case sensitive)

If there is more than one suggestion, this method will select a random one out of the list

Otherwise this throws a NotFoundException

The mechanism it uses to find a pickerWidget is prefixing the picker field label with test- and looking for a testLabel

Parameters:
  • label (str) – Label of the field to fill out

  • value (str) – Value to update the label to

Keyword Arguments:
  • identifier (str) – Key to select the field to filter on, defaults to ‘id’

  • format_test_label (bool) – If you don’t want to prepend a “test-” to the testLabel, set this to False

  • fill_request_label (str) – Label to associate in locust statistics with filling the picker field

  • pick_request_label (str) – Label to associate in locust statistics with selecting the picker suggestion

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_picker_field('Title','My New Novel')
>>> form.fill_picker_field('People','Jeff George')
>>> form.fill_picker_field('Customer', 'GAC Guyana', identifier='code')
fill_text_field(label: str, value: str, is_test_label: bool = False, locust_request_label: str = '', index: int = 1) SailUiForm

Fills a field on the form, if there is one present with the following label (case sensitive) Otherwise throws a NotFoundException

Parameters:
  • label (str) – Label of the field to fill out

  • value (str) – Value to fill the field with

Keyword Arguments:
  • is_test_label (bool) – If you are filling a text field via a test label instead of a label, set this boolean to true

  • locust_request_label (str) – Label used to identify the request for locust statistics

  • index (int) – Index of the field to fill if more than one match the label criteria (default: 1)

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.fill_text_field('Title','My New Novel')
get_dropdown_items(label: str, is_test_label: bool = False) List[str]

Gets all dropdown items for the dropdown label provided on the form If no dropdown found, throws a NotFoundException

Parameters:
  • label (str) – Label of the dropdown

  • is_test_label (bool) – If you are interacting with a dropdown via a test label instead of a label, set this boolean to true. User filters on a record instance list use test labels.

Returns (List): A list of all the choices in the dropdown

Examples

>>> form.get_dropdown_items('MyDropdown')
get_latest_state() Dict[str, Any]

Provides a deep copy of latest state of UI form.

Returns (dict): deep copy of last recorded response.

go_to_next_record_grid_page(locust_request_label: str = '') SailUiForm
move_to_beginning_of_paging_grid(label: str | None = None, index: int | None = None, locust_request_label: str = '') SailUiForm

Moves to the beginning of a paging grid, if possible Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • label (str) – Label of the grid

  • index (int) – Index of the grid

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.move_to_beginning_of_paging_grid(label='my nice grid')
move_to_end_of_paging_grid(label: str | None = None, index: int | None = None, locust_request_label: str = '') SailUiForm

Moves to the end of a paging grid, if possible Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • label (str) – Label of the grid

  • index (int) – Index of the grid

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.move_to_end_of_paging_grid(label='my nice grid')
move_to_left_in_paging_grid(label: str | None = None, index: int | None = None, locust_request_label: str = '') SailUiForm

Moves to the left in a paging grid, if possible It might require getting the state of the grid if you’ve moved to the end/ previous part of the grid Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • label (str) – Label of the grid

  • index (int) – Index of the grid

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.move_to_left_in_paging_grid(label='my nice grid')
move_to_right_in_paging_grid(label: str | None = None, index: int | None = None, locust_request_label: str = '') SailUiForm

Moves to the right in a paging grid, if possible It might require getting the state of the grid if you’ve moved within the grid Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • label (str) – Label of the grid

  • index (int) – Index of the grid

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.move_to_right_in_paging_grid(index=0) # move to right in first grid on the page
refresh_after_record_action(label: str, is_test_label: bool = False, locust_request_label: str = '') SailUiForm

Refreshes a form after the completion of a record action.

Parameters:
  • label (str) – Label of the record action that has just been completed

  • is_test_label (bool) – If you are referencing a record action via a test label instead of a label, set this boolean to true

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> initial_form = copy(form)
>>> form.click('Request upgrade')
>>> ...
>>> form.click('Submit')
>>> initial_form.refresh_after_record_action('Request upgrade')
select_card_choice_field_by_label(label: str, index: int, locust_request_label: str = '') SailUiForm

Select a card by its label Index is position to be selected

Parameters:
  • label (str) – Label of the card choice field

  • index (int) – Index of the card to select

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_card_choice_field_by_label('myLabel', 1)  # selects the first item
select_date_range_user_filter(filter_label: str, filter_start_date: date, filter_end_date: date, locust_request_label: str | None = None) SailUiForm

Select a value on a date range user filter

Parameters:
  • filter_label (str) – the testLabel of the filter to select a value for

  • filter_start_date (date) – the start date for the range

  • filter_end_date (date) – the end date for the range

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_date_range_user_filter(filter_label="userFilter", filter_start_date=datetime.date(2023, 10, 18), filter_end_date=datetime.date(2023,10,19))
select_dropdown_item(label: str, choice_label: str, locust_request_label: str = '', is_test_label: bool = False) SailUiForm

Selects a dropdown item on the form If no dropdown found, throws a NotFoundException If no element found, throws a ChoiceNotFoundException

Parameters:
  • label (str) – Label of the dropdown

  • choice_label (str) – Label of the dropdown item to select

  • is_test_label (bool) – If you are interacting with a dropdown via a test label instead of a label, set this boolean to true. User filters on a record instance list use test labels.

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_dropdown_item('MyDropdown', 'My First Choice')
select_dropdown_item_by_index(index: int, choice_label: str, locust_request_label: str = '') SailUiForm

Selects a dropdown item on the form by index (1-based) If no dropdown found, throws a NotFoundException If no element found, throws a ChoiceNotFoundException

Parameters:
  • index (int) – index(int): Index of the dropdown to select

  • choice_label (str) – Label of the dropdown item to select

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_dropdown_item_by_index(1, 'My First Choice')
select_multi_dropdown_item(label: str, choice_label: List[str], locust_request_label: str = '', is_test_label: bool = False) SailUiForm

Selects a multiple dropdown item on the form If no multiple dropdown found, throws a NotFoundException If no element found, throws a ChoiceNotFoundException

Parameters:
  • label (str) – Label of the dropdown

  • choice_label ([str]) – Label(s) of the multiple dropdown item to select

  • is_test_label (bool) – If you are interacting with a multiple dropdown via a test label instead of a label, set this boolean to true. User filters on a record instance list use test labels.

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_multi_dropdown_item('MyMultiDropdown', ['My First Choice','My Second Choice'])
select_multi_dropdown_item_by_index(index: int, choice_label: List[str], locust_request_label: str = '') SailUiForm

Selects a multiple dropdown item on the form by index (1-based) If no multiple dropdown found, throws a NotFoundException If no element found, throws a ChoiceNotFoundException

Parameters:
  • index (int) – Index of the multiple dropdown to select

  • choice_label ([str]) – Label(s) of the multiple dropdown item to select

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_multi_dropdown_item_by_index(2, ['My First Choice','My Second Choice'])
select_nav_card_by_index(nav_group_label: str, index: int, is_test_label: bool = False, locust_request_label: str = '') SailUiForm

Selects an element of a navigation card group by its index

Parameters:
  • nav_group_label (str) – Label of the navigation card group

  • index (int) – Index of the element

Keyword Arguments:
  • is_test_label (bool) – If this label is a test label

  • locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

select_radio_button_by_index(field_index: int, index: int, locust_request_label: str = '') SailUiForm

Selects a radio button by its field index Index is position to be selected

Parameters:
  • field_index (int) – Index of the radio button field on the page

  • index (int) – Index of the radio button to select

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_radio_button_by_index(1, 1)  # selects the first item in the first radio button field
select_radio_button_by_label(label: str, index: int, locust_request_label: str = '') SailUiForm

Selects a radio button by its label Index is position to be selected

Parameters:
  • label (str) – Label of the radio button field

  • index (int) – Index of the radio button to select

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_radio_button_by_label('myLabel', 1)  # selects the first item
select_radio_button_by_test_label(test_label: str, index: int, locust_request_label: str = '') SailUiForm

Selects a radio button by its test label Index is position to be selected

Parameters:
  • test_label (str) – Label of the radio button field

  • index (int) – Index of the radio button to select

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_radio_button_by_test_label('myTestLabel', 1)  # selects the first item
select_rows_in_grid(rows: List[int], label: str | None = None, index: int | None = None, append_to_existing_selected: bool = False, locust_request_label: str = '') SailUiForm

Selects rows in a grid Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • rows (List[int]) – The rows to select

  • label (str) – Label of the grid

  • index (int) – Index of the grid

  • append_to_existing_selected (bool) – Flag to control appending row selections

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.select_rows_in_grid(rows=[1], label='my nice grid')
sort_paging_grid(label: str | None = None, index: int | None = None, field_name: str = '', ascending: bool = False, locust_request_label: str = '') SailUiForm

Sorts a paging grid by the field name, which is not necessarily the same as the label of the column And might require inspecting the JSON to determine what the sort field is

Sorts by ascending = False by default, override to set it to True

Either a label or an index is required, indices are useful if there is no title for the grid

Parameters:
  • label (str) – Label of the grid

  • index (int) – Index of the grid

  • field_name (str) – Field to sort on (not necessarily the same as the displayed one)

  • ascending (bool) – Whether to sort ascending, default is false

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.sort_paging_grid(index=0,field_name='Total',ascending=True)
upload_document_to_upload_field(label: str, file_path: str, locust_request_label: str = '') SailUiForm

Uploads a document to a named upload field There are two steps to this which can fail, one is the document upload, the other is finding the component and applying the update.

Parameters:
  • label (str) – Label of the upload field

  • file_path (str) – File path to the document

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Examples

>>> form.upload_document_to_upload_field('Upload File', "/usr/local/appian/File.zip")
>>> form.upload_document_to_upload_field('Upload Properties', "/usr/local/appian/File.properties")
upload_documents_to_multiple_file_upload_field(label: str, file_paths: List[str], locust_request_label: str = '') SailUiForm

Uploads multiple documents to a named upload field There are two steps to this which can fail, one is the document uploads, the other is finding the component and applying the update.

Parameters:
  • label (str) – Label of the upload field

  • file_paths (list) – List of document file paths in string form

Keyword Arguments:

locust_request_label (str) – Label used to identify the request for locust statistics

Returns (SailUiForm): The latest state of the UiForm

Example

>>> form.multi_upload_document_to_upload_field('Upload Files', ["/usr/local/appian/File1.zip", "/usr/local/appian/File2.zip"])

Utilities module

appian_locust.utilities.credentials.procedurally_generate_credentials(CONFIG: dict) None

Helper method that can be used to procedurally generate a set of Appian user credentials

Note: This class must be called in the UserActor class of your Locust test in order to create the credentials before any Locust users begin to pick them up.

Parameters:
  • CONFIG – full locust config dictionary, AKA the utls.c variable in locust tests Make sure the following keys are present.

  • procedural_credentials_prefix – Base string for each generated username

  • procedural_credentials_count – Appended to prefix, will create 1 -> Count+1 users

  • procedural_credentials_password – String which will serve as the password for all users

Returns:

None

appian_locust.utilities.credentials.setup_distributed_creds(CONFIG: dict) dict

Helper method to distribute Appian credentials across separate load drivers when running Locust in distributed mode. Credential pairs will be passed out in Round Robin fashion to each load driver.

Note: This class must be called in the UserActor class of your Locust test to ensure that the “credentials” key is prepared before tests begin.

Note: If fewer credential pairs are provided than workers, credentials will be distributed to workers in a Modulo fashion.

Parameters:

CONFIG – full locust config dictionary, AKA the utls.c variable in locust tests Make sure the following keys are present.

Returns:

same as input but with credentials key updated to just the subset of credentials required for given load driver.

Return type:

CONFIG

appian_locust.utilities.helper.extract_all_by_label(obj: dict | list, label: str) list

Recursively search for all fields with a matching label in JSON tree. :param obj: The json tree to search for fields in :param label: The label used to identify elements we want to return

Returns (list): A list of all elements in obj that match label

appian_locust.utilities.helper.extract_values(obj: Dict[str, Any], key: str, val: Any) List[Dict[str, Any]]

Pull all values of specified key from nested JSON.

Parameters:
  • obj (dict) – Dictionary to be searched

  • key (str) – tuple of key and value.

  • val (any) – value, which can be any type

Returns:

list of matched key-value pairs

appian_locust.utilities.helper.extract_values_multiple_key_values(obj: Dict[str, Any], key: str, vals: List[Any]) List[Dict[str, Any]]

Pull all values where the key value matches an entry in vals from nested JSON.

Parameters:
  • obj (dict) – Dictionary to be searched

  • key (str) – a key in the dictionary

  • vals (List[any]) – A list of values corresponding to the key, which can be any type

Returns:

list of matched key-value pairs

appian_locust.utilities.helper.find_component_by_attribute_and_index_in_dict(attribute: str, value: str, index: int, component_tree: Dict[str, Any]) Any

Find a UI component by the given attribute (label for example) in a dictionary It returns the index’th match in a depth first search of the json tree It returns the dictionary that contains the given attribute with the given value or throws an error when none is found

Parameters:
  • attribute – an attribute to search (‘label’ for example)

  • value – the value of the attribute (‘Submit’ for example)

  • index – the index of the component to find if multiple components are found with the same ‘value’ for ‘attribute’ (1 for example)

  • component_tree – the json response.

Returns:

The json object of the component

Raises:

ComponentNotFoundException if the component cannot be found.

Example

>>> find_component_by_attribute_and_index_in_dict('label', 'Submit', 1, self.json_response)

will search the json response to find the first component that has ‘Submit’ as the label

appian_locust.utilities.helper.find_component_by_attribute_in_dict(attribute: str, value: str, component_tree: Dict[str, Any], raise_error: bool = True, throw_attribute_exception: bool = False) Any

Find a UI component by the given attribute (label for example) in a dictionary It only returns the first match in a depth first search of the json tree It returns the dictionary that contains the given attribute with the given value or throws an error when none is found.

Parameters:
  • attribute – an attribute to search (‘label’ for example)

  • value – the value of the attribute (‘Submit’ for example)

  • component_tree – the json response.

  • raise_error – If set to False, will return None instead of raising an error. (Default: True)

  • throw_attribute_exception – If set to False then if the component is not found an exception is thrown using the attribute and value in the exception.

Returns:

The json object of the component

Raises:

ComponentNotFoundException if the component cannot be found.

Example

>>> find_component_by_attribute_in_dict('label', 'Submit', self.json_response)

will search the json response to find a component that has ‘Submit’ as the label

appian_locust.utilities.helper.find_component_by_index_in_dict(component_type: str, index: int, component_tree: Dict[str, Any]) Any

Find a UI component by the index of a given type of component (“RadioButtonField” for example) in a dictionary Performs a depth first search and counts quantity of the component, so the 1st is the first one It returns the dictionary that contains the given attribute with the requested index or throws an error when none is found.

Parameters:
  • component_type – type of the component(#t in the JSON response, ‘RadioButtonField’ for example)

  • index – the index of the component with the component_type (‘1’ for example - Indices start from 1)

  • component_tree – the json response

Returns:

The json object of the component

Raises:

ComponentNotFoundException if the component cannot be found.

Example

>>> find_component_by_index_in_dict('RadioButtonField', 1, self.json_response)

will search the json response to find the first component that has ‘RadioButtonField’ as the type

appian_locust.utilities.helper.find_component_by_label_and_type_dict(attribute: str, value: str, type: str, component_tree: Dict[str, Any], raise_error: bool = True) Any

Find a UI component by the given attribute (like label) in a dictionary, and the type of the component as well. (#t should match the type value passed in) It only returns the first match in a depth first search of the json tree. It returns the dictionary that contains the given attribute with the given label and type or throws an error when none is found if the raise_error value is True. Otherwise it will return None if the component cannot be found.

Parameters:
  • label – label of the component to search

  • value – the value of the label

  • type – Type of the component (TextField, StartProcessLink etc.)

  • component_tree – the json response.

  • raise_error – If set to False, will return None instead of raising an error. (Default: True)

Returns:

The json object of the component or None if the component cannot be found.

Raises:

ComponentNotFoundException if the component cannot be found.

Example

>>> find_component_by_label_and_type_dict('label', 'MyLabel', 'StartProcessLink', self.json_response)
appian_locust.utilities.helper.find_component_by_type_and_attribute_and_index_in_dict(component_tree: Dict[str, Any], type: str = '', attribute: str = '', value: str = '', index: int = 1, raise_error: bool = True) Any

Find a UI component by the given type and/or attribute with ‘value’ in a dictionary Returns the index’th match in a depth first search of the json tree Returns the dictionary that contains the given attribute with the given value or throws an error when none is found Note: Both type and attribute matching are optional, which will cause this function to return the index’th component in the tree

Parameters:

component_tree – the json response

Keyword Arguments:
  • type (str) – the component type to match against (default: ‘’)

  • attribute (str) – an attribute to search (default: ‘’)

  • value (str) – the value of the attribute (default: ‘’)

  • index (int) – the index of the component to find if multiple components match the above criteria, 1-indexed (default: 1)

  • raise_error (bool) – if this is set to false, it will return None instead of raising an error.

Returns:

The json object of the component or None if ‘raise_error’ is set to false.

Raises:
  • ComponentNotFoundException if the attribute or type checks fail.

  • Exception if the component is found but at an incorrect index.

Example

>>> find_component_by_attribute_and_index_in_dict('label', 'Submit', 1, self.json_response)

will search the json response to find the first component that has ‘Submit’ as the label

appian_locust.utilities.helper.format_label(label: str, delimiter: str | None = None, index: int = 0) str

Simply formats the string by replacing a space with underscores

Parameters:
  • label – string to be formatted

  • delimiter – If provided, string will be split by it

  • index – used with delimiter parameter, which item will be used in the “split”ed list.

Returns:

formatted string

appian_locust.utilities.helper.get_random_item(list_of_items: List[Any], exclude: List[Any] = []) Any

Gets a random item from the given list excluding the items if any provided

Parameters:
  • list_of_items – list of items of any data type

  • exclude – if any items needs to be excluded in random pick

Returns:

Randomly picked Item

Raises:

In case of no item to pick, Exception will be raised

appian_locust.utilities.helper.get_username(auth: list) str

Returns the username from an auth list :param auth: Appian Locust authorization list

Returns (str): Username from auth list

appian_locust.utilities.helper.list_filter(list_var: List[str], filter_string: str, exact_match: bool = False) List[str]

from the given list, return the list with filtered values.

Parameters:
  • list_var (list) – list of strings

  • filter_string (str) – string which will be used to filter

  • exact_match (bool, optional) – filter should be based on exact match or partial match. default is partial.

Returns:

List with filtered values

appian_locust.utilities.helper.remove_type_info(sail_dict: Dict[str, Any]) Dict[str, Any]

Returns a flattened dictionary with SAIL type info removed :param sail_dict: SAIL Dictionary to remove type information from

Returns (dict): Flattened dictionary

appian_locust.utilities.helper.repeat(num_times: int = 2, wait_time: float = 0.0) Callable

This function allows an arbitrary function to be executed an arbitrary number of times The intended use is as a decorator:

>>> @repeat(2)
... def arbitrary_function():
...     print("Hello World")
... arbitrary_function()
Hello World
Hello World
Parameters:

num_times (int) – an integer

Implicit Args:

arbitrary_function (Callable): a python function

Returns:

A reference to a function which performs the decorated function num_times times

class appian_locust.utilities.loadDriverUtils.loadDriverUtils

Bases: object

load_config(config_file: str = './config.json') dict

Load a json configuration file into a dictionary :param config_file: Location where config file can be found

Returns (dict): Dictionary containing configuration. Will also be stored in

loadDriverUtils.c

appian_locust.utilities.logger.getLogger(name: str | None = None) Logger
Parameters:

name (str, optional) – Name of the logger. it is common practice to use file name here but it can be anything.

Returns: logger object

Note

By contributing to this Open Source project, you provide Appian Corporation a non-exclusive, perpetual, royalty-free license to use your contribution for any purpose.

Contributing

  1. Fork the appian-locust repository

  2. Make any desired changes

  3. Commit changes and push to your fork

  4. Make a merge request to the upstream fork and project maintainers will review it

New Development

As new development is done to Appian Locust, the core principle of user navigation and resulting interaction should be kept in mind. Is your feature adding interaction capabilities to an existing type of page? If so, you likely want to add a new method to an existing SailUiForm type. Otherwise, you might need to create a new extention of SailUiForm and ensure that the Visitor class has the capabilities to visit the new page type. Lastly, functionality that doesn’t involve user interaction should be included in the SiteHelper class.

If you think that your development falls outside of the above criteria, you should submit an issue for the maintainers of this project to discuss your use case.

To Test Your Changes

In any test-implementation repo where you use appian-locust, change the following (assuming you’re using a Pipfile)

appian-locust = {path="../appian-locust", editable=true}

NOTE The path above assumes appian-locust is checked out locally, hence we can use a relative directory path.

And run pipenv install --skip-lock to allow you to use a local version of appian-locust without recreating the lock file. However, remember to use a lock file in your test-implementation repo.

Now you can test your changes as you normally would.

Internal Classes

Apart from our exposed API, we provide internal classes for more granular control when developing or testing.

class appian_locust._actions._Actions(interactor: _Interactor)

Bases: _Base

clear_actions_cache() None
fetch_action_json(action_name: str, exact_match: bool = False, label: str = '') Dict[str, Any]

This function calls the API for the specific action to get its “form” data

Parameters:
  • action_name (str) – Name of the action to be called. Name of the action will be in the below pattern. “displayLabel::opaquqId”

  • exact_match (bool, optional) – Should action name match exactly or to be partial match. Default : False

Returns (dict): Response of actions’s Get UI call in dictionary

Examples

If the full name of the action is known, with the opaque ID,

>>> self.appian.action.fetch_action_json("action_name:igB0K7YxC0UQ2Fhx4hicRw...", exact_match=True)

If only the display name is known, or part of the display name

>>> self.appian.action.fetch_action_json("action_name")
>>> self.appian.action.fetch_action_json("actio")
get_action(action_name: str, exact_match: bool = False) Dict[str, Any]

Get the information about specific action by name.

Parameters:
  • action_name (str) – Name of the action

  • exact_match (bool) – Should action name match exactly or to be partial match. Default : False

Returns (dict): Specific Action’s info

Raises: In case of action is not found in the system, it throws an “Exception”

Example

If full name of action is known, with the opaque ID,

>>> self.appian.action.get_action("action_name:igB0K7YxC0UQ2Fhx4hicRw...", exact_match=True)

If only the display name is known, or part of the display name

>>> self.appian.action.get_action("action_name")
>>> self.appian.action.get_action("actio")
get_actions_feed(locust_request_label: str = 'Actions') Dict[str, Any]
get_actions_interface(locust_request_label: str = 'Actions') Dict[str, Any]
get_actions_nav(locust_request_label: str = 'Actions') Dict[str, Any]
get_all(search_string: str | None = None, locust_request_label: str = 'Actions.MainMenu.AvailableActions') Dict[str, Any]

Retrieves all the available “actions” and associated metadata from “Appian-Tempo-Actions”

Note: All the retrieved data about actions is stored in the private variable self._actions

Returns (dict): List of actions and associated metadata

Examples

>>> self.appian.action.get_all()
get_errors_count() int
start_action(action_name: str, skip_design_call: bool = False, exact_match: bool = False) Response

Perform the post operation on action’s API to start specific action. Actions that do not have a UI can be called directly without using “GET” to retrieve the UI. this is controlled by the optional skip_design_call parameter

Parameters:
  • action_name (str) – Name of the action

  • skip_design_call (bool, optional) – to skip the “GET” call for the action’s UI. Default : False

  • exact_match (bool, optional) – Should action name match exactly or to be partial match. Default : False

Returns: NONE

Example

>>> self.appian.action.start_action("action_name")
class appian_locust._admin._Admin(interactor: _Interactor)

Bases: object

fetch_admin_json(locust_request_label: str | None = None) Dict[str, Any]

Navigates to /admin

Returns: The response of /admin

class appian_locust._base._Base

Bases: object

Base class for classes _Actions, _News, _Records, _Reports, _Tasks, Sites

get(items_in_dict: dict, item_name: str, exact_match: bool = True, ignore_retry: bool = False, search_string: str | None = None) tuple

Common Get function to get the specific component from dictionary of items. If item is not found, it calls get_all function to update itself and retry.

Warning: Internal function, should never be called directly.

Parameters:
  • items_in_dict (dict) – Dictionary of component (for ex, dictionary of actions if called from actions class)

  • item_name (str) – Component name (for ex, action name if called from actions class)

  • exact_match (bool, optional) – Should item name match exactly or to be partial match. Default : True

  • ignore_retry (bool, optional) – Whether to retry or not if item is not found in first try.

  • search_string (str, optional) – String to filter the results of get_all function. Currently supported only for News

Returns: Tuple of item name and full properties of item If item found, otherwise tuple of Nones

get_all(search_string: str | None = None, locust_request_label: str = '') Any

Common Get All function prototype that is overwritten by subclasses. Created only to conform to Mypy validation.

class appian_locust._data_fabric._DataFabric(interactor: _Interactor)

Bases: object

fetch_data_fabric_json(locust_request_label: str | None = None) Dict[str, Any]
class appian_locust._design._Design(interactor: _Interactor)

Bases: object

click_expression_editor_toolbar_button(button_action: str, post_url: str, state: Dict[str, Any], context: Dict[str, Any], uuid: str, label: str | None = None) Dict[str, Any]
create_ai_skill_object(ui_form: SailUiForm, ai_skill_name: str, ai_skill_type: AISkillObjectType) SailUiForm
create_object(ui_form: SailUiForm, link_name: str, object_name: str) SailUiForm
extract_ai_skill_info(object_json: Dict[str, Any]) AiSkill
fetch_application_json(app_id: str, locust_request_label: str | None = None) Dict[str, Any]

Fetches the JSON of the UI for a specific application within the /design environment

Returns: JSON Dictionary

Example

>>> design.fetch_application_json("AADZeglVgAAgfpsAAJsAAAAAAdA")
fetch_design_json(locust_request_label: str | None = None) Dict[str, Any]

Fetches the JSON of /design UI

Returns: JSON Dictionary

Example

>>> design.fetch_design_json()
fetch_design_object_json(opaque_id: str, locust_request_label: str | None = None) Dict[str, Any]

Fetches the JSON of the UI for a specific object within the /design environment

Returns: JSON Dictionary

Example

>>> design.fetch_design_object_json("lABD1iTIu_lxy_3T_90Is2fs63uh52xESYi6-fun7FBWshlrBQ0EptlFUdGyIRzSSluPyVdvOhvGgL6aBlnjlkWfQlALYR2aRZ_AIliJ4lc3g")
find_design_grid(state: Dict[str, Any]) Dict[str, Any]
find_design_object_opaque_id_in_grid(design_object_name: str, current_state: Dict[str, Any]) str
find_design_object_type_indices(current_state: Dict[str, Any], design_object_types: list[str]) list[int]
search_design_grid(search_str: str, reeval_url: str, state: Dict[str, Any], context: Dict[str, Any], uuid: str, locust_label: str = 'Design.Search') Dict[str, Any]

Search a grid in /design :param search_str: string to search :type search_str: str :param reeval_url: url to send request to :type reeval_url: str :param state: current state of UI, which contains the search bar :type state: str :param context: current context :type context: str :param uuid: UUID of search component :type uuid: str :param locust_label: label to associate request with :type locust_label: str

Returns:

appian_locust._design.get_available_design_objects(state: Dict[str, Any]) Dict[str, DesignObject]
appian_locust._design.validate_design_object_access_method(design_object_json: Dict[str, Any], object_type_to_method_dict: Dict[str, Any]) None
appian_locust._feature_toggle_helper.__get_javascript_feature_flag_regex() Generator[str, None, None]
appian_locust._feature_toggle_helper.__get_javascript_uri_regex() Generator[str, None, None]
appian_locust._feature_toggle_helper.__get_mobile_feature_flag_overrides() Generator[FeatureFlag, None, None]
appian_locust._feature_toggle_helper._create_override_flag_mask(flags_to_override: Callable[[], Generator[FeatureFlag, None, None]]) int

Given a list of flag enums from FeatureFlag, this will set that flag to 1 to override the default feature set. returns flag mask reflecting all the flags combined.

appian_locust._feature_toggle_helper._get_feature_flags_from_regex_match(flag_str: str) Tuple[str, str]

Coerce flags into proper format for sending as headers

appian_locust._feature_toggle_helper._get_javascript_and_find_feature_flag(client: HttpSession, script_uri: str, headers: Dict[str, Any] | None = None) Any

Read through minified javascript for feature flags

appian_locust._feature_toggle_helper._get_javascript_uri(interactor: _Interactor, headers: Dict[str, Any] | None = None) Any

Gets the URI for the javascript file that contains the Feature Toggles

appian_locust._feature_toggle_helper._to_hex_str(flagVal: int) str
appian_locust._feature_toggle_helper._truncate_flag_extended(flag_extended: int) int
appian_locust._feature_toggle_helper.get_client_feature_toggles(interactor: _Interactor, session: HttpSession) Tuple[str, str]

Given an authenticated user, recover the feature toggles from the minified javascript

Returns: Returns feature flag if found, otherwise empty string

appian_locust._feature_toggle_helper.override_default_feature_flags(interactor: _Interactor, flags_to_override: Callable[[], Generator[FeatureFlag, None, None]]) None

Given a list of flag enums from FeatureFlag, override_default_flags gets the flag mask to set all of the flags to true, and it overrides the current feature flag extended value to set these flags to true.

appian_locust._feature_toggle_helper.set_mobile_feature_flags(interactor: _Interactor) None

This overrides the feature flags to tell the service that the request is coming from a mobile device.

class appian_locust._grid_interactor.GridInteractor

Bases: object

Set of utility methods for interacting with grids, i.e. finding them, and manipulating them

_get_grid_data(paging_grid: Dict[str, Any]) Dict[str, Any]
_get_sort_info(field_name: str, ascending: bool) List[Dict[str, Any]]
_to_save_data(grid_data: Dict[str, Any], paging_grid: Dict[str, Any]) Dict[str, Any]
_validate_grid_field_or_label(field_name: str, paging_grid: Dict[str, Any]) str
find_grid_by_index(index: int, form: Dict[str, Any]) Dict[str, Any]
find_grid_by_label(label: str, form: Dict[str, Any]) Dict[str, Any]
find_grid_by_label_or_index(form: Dict[str, Any], label: str | None = None, index: int | None = None) Dict[str, Any]
format_grid_display_label(grid: Dict[str, Any]) str
move_to_first_page(paging_grid: Dict[str, Any]) Dict[str, Any]
move_to_last_page(paging_grid: Dict[str, Any]) Dict[str, Any]
move_to_the_left(paging_grid: Dict[str, Any]) Dict[str, Any]
move_to_the_right(paging_grid: Dict[str, Any]) Dict[str, Any]
select_rows(paging_grid: Dict[str, Any], rows: List[int], append_to_existing_selected: bool = False) Dict[str, Any]
sort_grid(field_name: str, paging_grid: Dict[str, Any], ascending: bool = False) Dict[str, Any]
validate_sort(field_name: str, paging_grid: Dict[str, Any]) None
class appian_locust._interactor.DataTypeCache

Bases: object

cache(response_in_json: Dict[str, Any]) None

From the given json response, finds and caches the data type :param response_in_json: response of the API request

clear() None

Clears the data type cache

get() str

Concatenates all cached data types and returns a string

Returns: concatenated cached data type string

class appian_locust._interactor._Interactor(session: HttpSession, host: str, portals_mode: bool = False, request_timeout: int = 300)

Bases: object

static _clean_filename(label: str) str
_make_file_metadata(doc_info: Dict[str, Any]) dict

Produces a file metadata object to use for multifile upload fields

Parameters:

id (int) – Document id of the object

Returns:

Dictionary of the multifile upload data

Return type:

dict

check_login(resp: ResponseContextManager) None

Given a response, checks to see if it’s possible that we are not logged in and then performs a login if we are not

Parameters:

resp – Response to check

Returns: None

check_post_response_for_valid_auth(resp: ResponseContextManager) None

Given a POST response, checks to see if we are correctly authenticated :param resp: POST request response to examine

Returns: None

click_button(post_url: str, component: Dict[str, Any], context: Dict[str, Any], uuid: str, label: str | None = None, headers: Dict[str, Any] | None = None, client_mode: str | None = None) Dict[str, Any]

Calls the post operation to click certain SAIL components such as Buttons and Dynamic Links

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • component – the JSON code for the desired component

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • label – the label to be displayed by locust for this action

  • headers – header for the REST API call

Returns: the response of post operation as json

click_component(post_url: str, component: Dict[str, Any], context: Dict[str, Any], uuid: str, label: str | None = None, headers: Dict[str, Any] | None = None, client_mode: str | None = None) Dict[str, Any]

Calls the post operation to click certain SAIL components such as Buttons and Dynamic Links

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • component – the JSON code for the desired component

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • label – the label to be displayed by locust for this action

  • headers – header for the REST API call

Returns: the response of post operation as json

click_generic_element(post_url: str, component: Dict[str, Any], context: Dict[str, Any], uuid: str, new_value: Dict[str, Any], label: str | None = None) Dict[str, Any]

Calls the post operation to click on a generic element

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • component – the JSON code for the component

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • new_value – value for the payload

  • label – the label to be displayed by locust for this action

Returns: the response of post operation as json

Calls the post operation to click certain SAIL components such as Buttons and Dynamic Links

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • component – the JSON code for the desired component

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • label – the label to be displayed by locust for this action

  • headers – header for the REST API call

Returns: the response of post operation as json

Use this function to interact specifically with record links, which represent links to new sail forms. :param get_url: the url (not including the host and domain) to navigate to :param component: the JSON code for the RecordLink :param context: the Sail context parsed from the json response :param label: the label to be displayed by locust for this action :param headers: header for the REST API call

Returns: the response of get RecordLink operation as json

click_record_list_action(component: Dict[str, Any], process_model_uuid: str, cache_key: str, locust_request_label: str | None = None) Dict[str, Any]
click_record_search_button(post_url: str, component: Dict[str, Any], context: Dict[str, Any], uuid: str, label: str | None = None) Dict[str, Any]

Calls the post operation to click a record search button

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • component – the JSON code for the desired SearchBoxWidget component

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • label – the label to be displayed by locust for this action

Returns: the response of post operation as json

Use this function to interact with related action links, which start a process and return the start form. This can handle both relation actions and related action links that open in a dialog.

Parameters:
  • component – the JSON representing the Related Action Link

  • record_type_stub – record type stub for the record

  • opaque_record_id – opaque id for the record

  • opaque_related_action_id – opaque id for the related action

  • locust_request_label – label to be used within locust

  • open_in_a_dialog – Does this link open in a dialog

Returns: the start form for the related action

click_selected_tab(post_url: str, tab_group_component: Dict[str, Any], tab_label: str, context: Dict[str, Any], uuid: str) Dict[str, Any]

Calls the post operation to send an update to a tabgroup to select a tab

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • tab_group_component – the JSON representing the desired TabButtonGroup SAIL component

  • tab_label – Label of the tab to select

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • label – the label of the tab to select. It is one of the tabs inside TabButtonGroup

Returns: the response of post operation as json

Use this function to interact with start process links, which start a process and return the start form. :param component: the JSON representing the Start Process Link :param process_model_opaque_id: opaque id for the process model of the Start Process Link :param cache_key: cachekey for the start process link :param site_name: name of site for link in starting process model. :param page_name: name of page for link in starting process model. :param group_name: name of group for link in starting process model, if there is one. :param is_mobile: indicates if it should hit the mobile endpoint. :param locust_request_label: label to be used within locust

Returns: the response of get Start Process Link operation as json

construct_and_send_dropdown_update(component: Any, choice_label: str, context: Dict[str, Any], state: Dict[str, Any], uuid: str, context_label: str, exception_label: str, reeval_url: str, identifier: Dict[str, Any] | None = None) Dict[str, Any]

Calls the post operation to send an update to a dropdown

Parameters:
  • component – the dropdown component to update

  • choice_label – Label of the dropdown

  • context – the Sail context parsed from the json response

  • state – the Sail state parsed from the json response

  • uuid – the uuid parsed from the json response

  • context_label – the label to be displayed by locust for this action

  • exception_label – information about the dropdown component to be displayed if there is an exception

  • reeval_url – URL for “rel”=”update”, which is used to do other interactions on the form

  • identifier – the Record List Identifier, if made on a Record List

Returns: the response of post operation as json

construct_and_send_multiple_dropdown_update(component: Any, choice_label: List[str], context: Dict[str, Any], state: Dict[str, Any], uuid: str, context_label: str, exception_label: str, reeval_url: str, identifier: Dict[str, Any] | None = None) Dict[str, Any]

Calls the post operation to send an update to a multiple dropdown

Parameters:
  • component – the multiple dropdown component to update

  • choice_label – Label(s) of the multiple dropdown item to select

  • context – the Sail context parsed from the json response

  • state – the Sail state parsed from the json response

  • uuid – the uuid parsed from the json response

  • context_label – the label to be displayed by locust for this action

  • exception_label – information about the multiple dropdown component to be displayed if there is an exception

  • reeval_url – URL for “rel”=”update”, which is used to do other interactions on the form

  • identifier – the Record List Identifier, if made on a Record List

Returns: the response of post operation as json

fetch_new_cascading_pickerfield_selection(pickfield_payload: List, locust_request_label: str = 'SelectCascadingPickerField') Dict[str, Any]
fill_cascading_pickerfield_request(request_payload: List, choice: Dict[str, Any]) List
fill_pickerfield_text(post_url: str, picker_field: Dict[str, Any], text: str, context: Dict[str, Any], uuid: str, label: str | None = None) Dict[str, Any]

Fill a Picker field with the given text and randomly select one of the suggested item :param post_url: the url (not including the host and domain) to post to :param picker_field: the picker field component returned from find_component_by_attribute_in_dict :param text: the text to fill into the picker field :param context: the SAIL context parsed from the json response :param uuid: the uuid parsed from the json response :param label: the label to be displayed by locust for this action

Returns: the response of post operation as json

fill_textfield(post_url: str, text_field: Dict[str, Any], text: str, context: Dict[str, Any], uuid: str, label: str | None = None) Dict[str, Any]

Fill a TextField with the given text :param post_url: the url (not including the host and domain) to post to :param text_field: the text field component returned from find_component_by_attribute_in_dict :param text: the text to fill into the text field :param context: the Sail context parsed from the json response :param uuid: the uuid parsed from the json response :param label: the label to be displayed by locust for this action

Returns: the response of post operation as json

find_selection_from_choices(selection: str, choices: List) Dict[str, Any] | None
get_interaction_host() str
get_page(uri: str, headers: Dict[str, Any] | None = None, label: str | None = None, check_login: bool = True) Response

Given a uri, executes GET request and returns response

Parameters:
  • uri – API URI to be called

  • headers – header for the REST API Call

  • label – the label to be displayed by locust

  • check_login – optional boolean to bypass checking if we are logged in

Returns: Json response of GET operation

get_primary_button_payload(page_content_in_json: Dict[str, Any]) Dict[str, Any]

Finds the primary button from the page content response and creates the payload which can be used to simulate a click

Parameters:

page_content_in_json – full page content that has a primary button

Returns: payload of the primary button

initialize_cascading_pickerfield_request(pickerfield_component: Dict[str, Any]) List
interact_with_record_grid(post_url: str, grid_component: Dict[str, Any], context: Dict[str, Any], uuid: str, identifier: Dict[str, Any] | None = None, context_label: str | None = None) Dict[str, Any]

Calls the post operation to send a record grid update

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • grid_component – the JSON dict representing the grid to update

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • identifier – the Record List Identifier, if made on a Record List

  • context_label – the label to be displayed by locust for this action

Returns: the response of post operation as json

login(auth: list | None = None, retry: bool = True, check_login: bool = True) Tuple[HttpSession, Response]
post_page(uri: str, payload: Any = {}, headers: Dict[str, Any] | None = None, label: str | None = None, files: dict | None = None, raise_error: bool = True, check_login: bool = True) Response

Given a uri, executes POST request and returns response

Parameters:
  • uri – API URI to be called

  • payload – Body of the API request. Can be either JSON or text input to allow for different payload types.

  • headers – header for the REST API Call

  • label – the label to be displayed by locust

Returns: Json response of post operation

refresh_after_record_action(post_url: str, record_action_component: Dict[str, Any], record_action_trigger_component: Dict[str, Any], context: Dict[str, Any], uuid: str, label: str | None = None) Dict[str, Any]

Calls the post operation to refresh a form after completion of a record action

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • record_action_component – the JSON representing the relevant record action component

  • record_action_trigger_component – the JSON representing the form’s record action trigger component

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

Returns: the response of post operation as json

replace_base_path_if_appropriate(uri: str) str
select_checkbox_item(post_url: str, checkbox: Dict[str, Any], context: Dict[str, Any], uuid: str, indices: list, context_label: str | None = None) Dict[str, Any]

Calls the post operation to send an update to a checkbox to check all appropriate boxes

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • checkbox – the JSON representing the desired checkbox

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • indices – indices of the checkbox

  • label – the label to be displayed by locust for this action

  • headers – header for the REST API call

Returns: the response of post operation as json

select_pickerfield_suggestion(post_url: str, picker_field: Dict[str, Any], selection: Dict[str, Any], context: Dict[str, Any], uuid: str, label: str | None = None) Dict[str, Any]

Select a Picker field from available selections :param post_url: the url (not including the host and domain) to post to :param picker_field: the text field component returned from find_component_by_attribute_in_dict :param selection: the suggested item to select for the picker field :param context: the SAIL context parsed from the json response :param uuid: the uuid parsed from the json response :param label: the label to be displayed by locust for this action

Returns: the response of post operation as json

select_radio_button(post_url: str, buttons: Dict[str, Any], context: Dict[str, Any], uuid: str, index: int, context_label: str | None = None) Dict[str, Any]

Calls the post operation to send an update to a radio button to select the appropriate button

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • buttons – the JSON representing the desired radio button field

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • index – index of the button to be selected

  • label – the label to be displayed by locust for this action

  • headers – header for the REST API call

Returns: the response of post operation as json

send_dropdown_update(post_url: str, dropdown: Dict[str, Any], context: Dict[str, Any], uuid: str, index: int, identifier: Dict[str, Any] | None = None, label: str | None = None) Dict[str, Any]

Calls the post operation to send an update to a dropdown

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • dropdown – the JSON code for the desired dropdown

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • index – location of the dropdown value

  • identifier – the Record List Identifier, if made on a Record List

  • label – the label to be displayed by locust for this action

Returns: the response of post operation as json

send_multiple_dropdown_update(post_url: str, multi_dropdown: Dict[str, Any], context: Dict[str, Any], uuid: str, index: List[int], identifier: Dict[str, Any] | None = None, label: str | None = None) Dict[str, Any]

Calls the post operation to send an update to a multiple dropdown

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • dropdown – the JSON code for the desired dropdown

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • index – locations of the multiple dropdown value identifier: the Record List Identifier, if made on a Record List

  • label – the label to be displayed by locust for this action

Returns: the response of post operation as json

set_user_agent_to_desktop() None
set_user_agent_to_mobile() None
setup_content_headers() dict
setup_feed_headers() dict
setup_request_headers(uri: str | None = None) dict

Generates standard headers for session

Parameters:

uri (str) – URI to be assigned as the Referer

Returns (dict): headers

Examples

>>> self.appian._interactor.setup_request_headers()
setup_sail_headers() dict
update_date_field(post_url: str, date_field_component: Dict[str, Any], date_input: date, context: Dict[str, Any], uuid: str, locust_label: str | None = None) Dict[str, Any]

Calls the post operation to update a date field

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • date_field_component – the JSON representing the date field component

  • date_input – date field to convert to proper text format

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

Returns: the response of post operation as json

update_datetime_field(post_url: str, datetime_field: Dict[str, Any], datetime_input: datetime, context: Dict[str, Any], uuid: str, locust_label: str | None = None) Dict[str, Any]

Calls the post operation to update a date field

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • datetime_field – the JSON representing the datetime field to edit

  • datetime_input – datetime field to convert to the proper text format

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

Returns: the response of post operation as json

update_grid_from_sail_form(post_url: str, grid_component: Dict[str, Any], new_grid_save_value: Dict[str, Any], context: Dict[str, Any], uuid: str, context_label: str | None = None) Dict[str, Any]

Calls the post operation to send a grid update

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • grid_component – the JSON dict representing the grid to update

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • uuid – indices of the checkbox

  • context_label – the label to be displayed by locust for this action

Returns: the response of post operation as jso

upload_document_to_field(post_url: str, upload_field: Dict[str, Any], context: Dict[str, Any], uuid: str, doc_info: Dict[str, Any] | List[Dict[str, Any]], locust_label: str | None = None, client_mode: str = 'DESIGN') Dict[str, Any]

Calls the post operation to send an update to a upload_field to upload a document or list thereof. Requires a previously uploaded document id or ids

Parameters:
  • post_url – the url (not including the host and domain) to post to

  • upload_field – the JSON representing the desired checkbox

  • context – the Sail context parsed from the json response

  • uuid – the uuid parsed from the json response

  • doc_id – document id or list of document ids for the upload

  • context_label – the label to be displayed by locust for this action

  • client_mode – where this is being uploaded to, defaults to DESIGN

Returns: the response of post operation as json

upload_document_to_server(file_path: str, validate_extensions: bool = False, is_encrypted: bool = False) Dict[str, Any]

Uploads a document to the server, so that it can be used in upload fields :param uri: API URI to be called :param validate_extensions: if extensions should be validated :param file_path: Path to the file to be uploaded

Returns: Document Id that can be used for upload fields

write_response_to_lib_folder(label: str | None, response: Response) None

Used for internal testing, to grab the response and put it in a file of type JSON

Parameters:
  • label (Optional[str]) – Optional label, used to name the file

  • response (Response) – Response object to write to a file

Writes to a recorded_responses folder from wherever you run locust

appian_locust._locust_error_handler._format_http_error(resp: Response, uri: str, username: str) str
Taken from Response.raise_for_status. Formats the http error message,

additionally adding the username

Parameters:
  • resp (Response) – Response to generate an http error message from

  • uri (str) – URI accessed as part of the request

  • username (str) – Username of the calling user

Returns:

the http error message to use

Return type:

str

appian_locust._locust_error_handler.test_response_for_error(resp: ResponseContextManager, uri: str = 'No URI Specified', raise_error: bool = True, username: str = '', name: str = '') None

Locust relies on errors to be logged to the global_stats attribute for error handling. This function is used to notify Locust that its instances are failing and that it should fail too.

Parameters:
  • resp (Response) – a python response object from a client.get() or client.post() call in Locust tests.

  • uri (Str) – URI in the request that caused the above response.

  • username (Str) – identifies the current user when we use multiple different users for locust test)

Returns:

None

Example (Returns a HTTP 500 error):

username = 'admin'
uri = 'https://httpbin.org/status/500'
with self.client.get(uri) as resp:
  test_response_for_error(resp, uri, username)
class appian_locust._news._News(interactor: _Interactor)

Bases: _Base

_visit_internal(news_name: str, exact_match: bool = True, search_string: str | None = None) Tuple
fetch_news_entry_record_tags(news_entry_id: str, locust_request_label: str) Response
get_all(search_string: str | None = None, locust_request_label: str = '') Dict[str, Any]

Retrieves all the available “news” and associated metadata from “Appian-Tempo-News”

Note: All the retrieved data about news is stored in the private variable self._news

Parameters:

search_string (str, optional) – results will be filtered based on the search string.

Returns (dict): List of news and associated metadata

Examples

>>> self.appian.action.get_all()
get_news(news_name: str, exact_match: bool = True, search_string: str | None = None) Dict[str, Any]

Get the information about specific news by name.

Parameters:
  • news_name (str) – name of the news entry

  • exact_match (bool, optional) – Should news name match exactly or to be partial match. Default : True

  • search_string (str, optional) – results will be filtered based on the search string.

Returns: Specific News’s info

Raises: In case news is not found in the system, it throws an “Exception”

Example

If full name of action is known,

>>> self.appian.news.get("news_name")

If only partial name is known,

>>> self.appian.news.get("news_name", exact_match=False)
search(search_string: str = '*') Dict[str, Any]
visit(news_name: str, exact_match: bool = True, search_string: str | None = None) None

This function calls the nav API for the specific news item and its related records if any

Parameters:
  • news_name (str) – Name of the news entry to be called

  • exact_match (bool, optional) – Should news name match exactly or to be partial match. Default : True

  • search_string (str, optional) – results will be filtered based on the search string.

Returns: None

Examples

If full name of news is known,

>>> self.appian.news.visit("news_name")

If only partial name is known,

>>> self.appian.news.visit("news_name", exact_match=False)
visit_news_entry(news_name: str, exact_match: bool = True, search_string: str | None = None) Tuple

This function simulates navigating to a single entry in the ui. There are two parts to navigating to a news entry: navigating to the view and getting the news entry’s feed.

Parameters:
  • news_name (str) – Name of the news entry to be called

  • exact_match (bool, optional) – Should news name match exactly or to be partial match. Default : True

  • search_string (str, optional) – results will be filtered based on the search string.

Returns (Tuple): Response codes for the view navigation and getting the feed entry

Examples

If full name of news is known,

>>> self.appian.news.visit("news_name")

If only partial name is known,

>>> self.appian.news.visit("news_name", exact_match=False)
class appian_locust._portals._Portals(interactor: _Interactor)

Bases: object

fetch_page_json(portal_unique_identifier: str, portal_page__unique_identifier: str, locust_request_label: str | None = None) Dict[str, Any]

Navigates to specific portal’s page

Returns: The response of portal’s page

static get_full_url(portal_unique_identifier: str, portal_page__unique_identifier: str) str
class appian_locust._rdo_interactor._RDOInteractor(interactor: _Interactor, rdo_host: str)

Bases: _Interactor

_make_mlas_file_metadata(id: int, doc_size: int, position: int, file_name: str) dict

Produces a file metadata object to use for multifile upload fields

ai_skill_creation_payload(jwt_token: str, app_prefix: str) dict
ai_skill_creation_save_payload(state: Dict[str, Any], object_uuid: str) dict
fetch_ai_skill_creation_dialog_json(app_prefix: str, locust_request_label: str = 'AISkill.CreateDialog') Dict[str, Any]
fetch_ai_skill_creation_save_dialog_json(state: Dict[str, Any], rdo_state: Dict[str, Any], locust_request_label: str = 'AISkill.CreationSaveDialog') Dict[str, Any]
fetch_ai_skill_designer_json(ai_skill_id: str, locust_request_label: str | None = None) Dict[str, Any]
fetch_jwt_token() Tuple
get_interaction_host() str
get_presigned_url(ai_skill_id: str, model_id: str) dict
patch_page(uri: str, payload: Any = {}, headers: Dict[str, Any] | None = None, label: str | None = None, files: dict | None = None, raise_error: bool = True) Response
persist_ai_skill_changes_to_rdo(ai_skill_id: str, state: Dict[str, Any], locust_request_label: str | None = None) Dict[str, Any]
post_page(uri: str, payload: Any = {}, headers: Dict[str, Any] | None = None, label: str | None = None, files: dict | None = None, raise_error: bool = True, check_login: bool = True) Response

Given a uri, executes POST request and returns response

Parameters:
  • uri – API URI to be called

  • payload – Body of the API request. Can be either JSON or text input to allow for different payload types.

  • headers – header for the REST API Call

  • label – the label to be displayed by locust

Returns: Json response of post operation

put_page(uri: str, payload: Any = {}, headers: Dict[str, Any] | None = None, label: str | None = None, files: dict | None = None, raise_error: bool = True) Response
save_ai_skill_ui_request(component: Dict[str, Any], context: Dict[str, Any], uuid: str, value: Dict[str, Any], locust_request_label: str | None = None) Dict[str, Any]
setup_mlas_file_upload_headers(kms_id: str) dict
setup_rdo_ui_request_headers() dict
upload_document_to_ai_skill_server(file_path: str, ai_skill_id: str, model_id: str, locust_request_label: str | None = None) Tuple[Any, int]
upload_document_to_mlas_field(upload_field: Dict[str, Any], context: Dict[str, Any], uuid: str, file_infos: List[Dict[str, Any]], locust_label: str | None = None) Dict[str, Any]
v1_post_request(jwt_token: str, rdo_csrf_token: str) Any
class appian_locust._records._Records(interactor: _Interactor, is_mobile_client: bool = False)

Bases: _Base

_get_mobile_records_uri(record_type_url_stub: str, search_string: str | None = None) str
_get_random_record_instance(record_type: str = '') Tuple[str, str]
_get_random_record_type() str
_is_response_good(response_text: str) bool
_record_type_list_request(record_type: str, is_mobile: bool = False, search_string: str | None = None, locust_request_label: str | None = None) Dict[str, Any]
fetch_all_records_json(locust_request_label: str = 'Records') Dict[str, Any]
fetch_record_instance(record_type: str, record_name: str, exact_match: bool = True) Dict[str, Any]

Get the information about a specific record by specifying its name and its record type.

Parameters:
  • record_type (str) – Name of the record type.

  • record_name (str) – Name of the record which should be available in the given record type

  • exact_match (bool) – Should record name match exactly or to be partial match. Default : True

Returns (dict): Specific record’s info

Raises: In case of record is not found in the system, it throws an “Exception”

Example

If full name of record type and record is known,

>>> self.appian.records.get("record_type_name", "record_name")

If only partial name is known,

>>> self.appian.records.get("record_type_name", "record_name", exact_match=False)
fetch_record_type(record_type: str, exact_match: bool = True) Dict[str, Any]

Fetch information about record type from the cache. Raises Exception if record type does not exist in cache.

Parameters:

record_type (str) – Name of the record type.

Returns (dict): Specific record type’s info

Raises: In case the record type is not found in the system, it throws an “Exception”

Example

>>> self.appian.records.get_record_type("record_type_name")
fetch_record_type_json(record_type_url_stub: str, is_mobile: bool = False, search_string: str | None = None, label: str | None = None) Dict[str, Any]
get_all(search_string: str | None = None, locust_request_label: str = 'Records') Dict[str, Any]

Retrieves all available “records types” and “records” and associated metadata from “Appian-Tempo-Records”

Note: All the retrieved data about record types and records is stored in the private variables self._record_types and self._records respectively

Returns (dict): List of records and associated metadata

get_all_record_types(locust_request_label: str = 'Records') Dict[str, Any]

Navigate to Tempo Records Tab and load all metadata for associated list of record types into cache.

Returns (dict): List of record types and associated metadata

get_all_records_of_record_type(record_type: str, column_index: int | None = None, search_string: str | None = None) Dict[str, Any]

Navigate to the desired record type and load all metadata for the associated list of record views into cache.

Parameters:
  • record_type (str) – Name of record type for which we want to enumerate the record instances.

  • column_index (int, optional) – Column index to only fetch record links from a specific column, starts at 0.

Returns (dict): List of records and associated metadata

Examples

>>> self.appian.records.get_all_records_of_record_type("record_type_name")
get_record(record_type: str, record_name: str, exact_match: bool = True) Dict[str, Any]

Get the information about a specific record by specifying its name and its record type.

Parameters:
  • record_type (str) – Name of the record type.

  • record_name (str) – Name of the record which should be available in the given record type

  • exact_match (bool) – Should record name match exactly or to be partial match. Default : True

Returns (dict): Specific record’s info

Raises: In case of record is not found in the system, it throws an “Exception”

Example

If full name of record type and record is known,

>>> self.appian.records.get("record_type_name", "record_name")

If only partial name is known,

>>> self.appian.records.get("record_type_name", "record_name", exact_match=False)
get_records_interface(locust_request_label: str | None = 'Records') Dict[str, Any]
get_records_nav(locust_request_label: str | None = 'Records') Dict[str, Any]
visit(record_type: str = '', record_name: str = '', view_url_stub: str = '', exact_match: bool = True, locust_request_label: str | None = None) Dict[str, Any]

This function calls the API for the specific record view/instance to get its response data.

Note: This function is meant to only traverse to Record forms, not to interact with them. For that, use visit_record_instance_and_get_form()

Parameters:
  • record_type (str) – Record Type Name. If not specified, a random record type will be selected.

  • record_name (str) – Name of the record to be called. If not specified, a random record will be selected.

  • view_url_stub (str, optional) – page/tab to be visited in the record. If not specified, “summary” dashboard will be selected.

  • exact_match (bool, optional) – Should record type and record name matched exactly as it is or partial match.

  • locust_request_label (str,optional) – Label used to identify the request for locust statistics

Returns (dict): Responses of Record’s Get UI call in a dictionary

Examples

If full name of record type and record is known,

>>> self.appian.records.visit_record_instance("record_type_name", "record_name", "summary")

If only partial name is known,

>>> self.appian.records.visit_record_instance("record_type_name", "record_name", "summary", exact_match=False)

If a random record is desired,

>>> self.appian.records.visit_record_instance()

If random record of a specific record type is desired,

>>> self.appian.records.visit_record_instance("record_type_name")
visit_record_instance(record_type: str = '', record_name: str = '', view_url_stub: str = '', exact_match: bool = True, locust_request_label: str | None = None) Dict[str, Any]

This function calls the API for the specific record view/instance to get its response data.

Note: This function is meant to only traverse to Record forms, not to interact with them. For that, use visit_record_instance_and_get_form()

Parameters:
  • record_type (str) – Record Type Name. If not specified, a random record type will be selected.

  • record_name (str) – Name of the record to be called. If not specified, a random record will be selected.

  • view_url_stub (str, optional) – page/tab to be visited in the record. If not specified, “summary” dashboard will be selected.

  • exact_match (bool, optional) – Should record type and record name matched exactly as it is or partial match.

  • locust_request_label (str,optional) – Label used to identify the request for locust statistics

Returns (dict): Responses of Record’s Get UI call in a dictionary

Examples

If full name of record type and record is known,

>>> self.appian.records.visit_record_instance("record_type_name", "record_name", "summary")

If only partial name is known,

>>> self.appian.records.visit_record_instance("record_type_name", "record_name", "summary", exact_match=False)

If a random record is desired,

>>> self.appian.records.visit_record_instance()

If random record of a specific record type is desired,

>>> self.appian.records.visit_record_instance("record_type_name")
visit_record_type(record_type: str = '', locust_request_label: str | None = None) Dict[str, Any]

Navigate into desired record type and retrieve all metadata for associated list of record views.

Returns (dict): List of records and associated metadata

Examples

>>> self.appian.records.visit_record_type("record_type_name")
appian_locust._records_helper._get_feedItemLayout_label(item: Dict[str, Any]) str
appian_locust._records_helper._get_linkedItem_label(item: Dict[str, Any]) str
appian_locust._records_helper._is_grid(res_dict_var: Dict[str, Any]) bool
appian_locust._records_helper.get_all_record_types_from_json(json_response: Dict[str, Any]) Dict[str, Any]
appian_locust._records_helper.get_all_records_from_json(json_response: Dict[str, Any]) Tuple[Dict[str, Any], int]
appian_locust._records_helper.get_record_header_response(form_json: Dict[str, Any]) Dict[str, Any]

This returns the contents of “x-embedded-header” from Record Instance’s Feed response. Header response is needed in cases like clicking on a related action.

appian_locust._records_helper.get_record_summary_view_response(form_json: Dict[str, Any]) Dict[str, Any]

This returns the contents of “x-embedded-summary” from Record Instance’s Feed response

appian_locust._records_helper.get_records_from_json_by_column(json_response: Dict[str, Any], column_index: int) Tuple[Dict[str, Any], int]
appian_locust._records_helper.get_url_stub_from_record_list_post_request_url(post_url: str | None) str | None

Given post_url, returns the URL stub IF the url matches the url for a record list. If not, returns None.

Parameters:

post_url – the post request url (not including the host and domain) to post to

Returns: The url stub if post_url matches a record instance list url, otherwise None

appian_locust._records_helper.get_url_stub_from_record_list_url_path(url: str | None) str | None

Attempts to parse the url stub the url of a record list. It should only be able to parse the url stub if the page is a record list. If the url stub cannot be parsed, returns None.

Parameters:

url – url path to attempt to parse the record list URL stub from

Returns: The url stub if post_url matches a record list url, otherwise None

class appian_locust._reports._Reports(interactor: _Interactor)

Bases: _Base

fetch_report_json(report_name: str, exact_match: bool = True, locust_request_label: str | None = None) Dict[str, Any]

This function calls the API for the specific report to get its “form” data

Parameters:
  • report_name (str) – Name of the report to be called.

  • exact_match (bool, optional) – Should report name match exactly or to be partial match. Default : True

  • locust_request_label (str, optional) – Label locust should associate this request with

Returns (dict): Response of report’s Get UI call in dictionary

Examples

If full name of report is known,

>>> self.appian.reports.fetch_report_json("report_name")

If only partial name is known,

>>> self.appian.reports.fetch_report_json("report_name", exact_match=False)
get_all(search_string: str | None = None, locust_request_label: str = 'Reports.Feed') Dict[str, Any]

Retrieves all the available “reports” and associated metadata from “Appian-Tempo-Reports”

Note: All the retrieved data about reports is stored in the private variable self._reports

Returns (dict): List of reports and associated metadata

Examples

>>> self.appian.reports.get_all()
get_report(report_name: str, exact_match: bool = True) Dict[str, Any]

Get the information about specific report by name.

Parameters:
  • report_name (str) – Name of the report

  • exact_match (bool) – Should report name match exactly or to be partial match. Default : True

Returns (dict): Specific Report’s info

Raises: In case of report is not found in the system, it throws an “Exception”

Example

If full name of report is known,

>>> self.appian.reports.get("report_name")

If only partial name is known,

>>> self.appian.reports.get("report_name", exact_match=False)
get_report_form_uri(report_name: str, exact_match: bool = True) str
get_reports_interface(locust_request_label: str = 'Reports') Dict[str, Any]
get_reports_nav(locust_request_label: str = 'Reports') Dict[str, Any]
class appian_locust._save_request_builder._SaveRequestBuilder

Bases: object

Builds a save request, that can be used to trigger saves on the UI

build() Dict[str, Any]
component(component: Dict[str, Any]) _SaveRequestBuilder
context(context: Dict[str, Any]) _SaveRequestBuilder
identifier(identifier: Dict[str, Any] | None) _SaveRequestBuilder
uuid(uuid: str) _SaveRequestBuilder
value(value: dict | list) _SaveRequestBuilder
appian_locust._save_request_builder.save_builder() _SaveRequestBuilder
class appian_locust._sites._Sites(interactor: _Interactor)

Bases: _Base

BROWSER_ACCEPT_HEADER = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3'
_get_and_memoize_site_data_from_ui(initial_nav_json: Dict[str, Any], site_name: str, display_name: str) Site
_get_page_from_json(site_name: str, page_info_json: Dict[str, Any]) Page | None
_setup_headers_with_accept() dict
_setup_headers_with_sail_json() dict
fetch_site_page_metadata(site_name: str, page_name: str, group_name: str | None = None) Page | None

Gets site page from the site url stub and page url stub

Parameters:
  • site_name – Site url stub

  • page_name – Page url stub

  • group_name – Group url stub, if there is one

Returns: Page object, representing an individual page of a site

fetch_site_tab_json(site_name: str, page_name: str, locust_request_label: str | None = None) Dict[str, Any]

Navigates to a site page, either a record, action or report.

Parameters:
  • site_name – Site Url stub

  • page_name – Page Url stub

  • locust_request_label (str, optional) – Label locust should associate this request with

Returns: Response of report/action/record

fetch_site_tab_record_json(site_name: str, page_name: str, locust_request_label: str | None = None) Dict[str, Any]

Navigate to a recordList page on a site, then grab a random page from that site

Note: Any record available in the record list as a recordLink will be hit using this function. There is no guarantee that this record will be of any specific type and may not point to a record view.

Parameters:
  • site_name – Site Url stub

  • page_name – Page Url stub

  • locust_request_label (str, optional) – Label locust should associate this request with

Returns: Response of report/action, or in the case of a record, response of record object

get_all(search_string: str | None = None, locust_request_label: str | None = None) Dict[str, Any]

Gets and stores data for all sites, including all of their url stubs

get_site_data_by_site_name(site_name: str) Site

Gets site data from just the site url stub

Parameters:

site_name – Site url stub

Returns: Site object, containing the site name and pages

get_site_page(site_name: str, page_name: str) Page
get_site_page_type(site_name: str, page_name: str) PageType
class appian_locust._task_opener._TaskOpener(interactor: _Interactor)

Bases: object

accept_a_task(payload: str, task_id: str, headers: Dict[str, Any] | None = None, task_title: str = '', locust_request_label: str = '') Dict[str, Any]

Accept a task if necessary

Parameters:
  • payload (str) – string to send as part of accepting a task

  • task_id (str) – task identifier

  • headers (Dict[str, Any], optional) – Headers to send. Defaults to {}.

  • task_title (str, optional) – Task title used to describe the interaction. Defaults to “”.

  • locust_request_label (str, optional) – label to be used within locust

Returns:

Response from accepting

Return type:

Dict[str, Any]

visit_by_task_id(task_title: str, task_id: str, extra_headers: Dict[str, Any] | None = None, locust_request_label: str = '') Dict[str, Any]

Vist a task page and the corresponding json using the task_id

Parameters:
  • task_title (str) – Title to identify the task

  • task_id (str) – Id used to navigate to the task

  • extra_headers (Dict[str, Any], optional) – Extra headers, used for sites requests. Defaults to None.

  • locust_request_label (str, optional) – label to be used within locust

Returns:

State returned by visiting the task

Return type:

Dict[str, Any]

class appian_locust._tasks._Tasks(interactor: _Interactor)

Bases: _Base

INITIAL_FEED_URI = '/suite/api/feed/tempo?m=menu-tasks&t=t&s=pt&defaultFacets=%255Bstatus-open%255D'
get_all(search_string: str | None = None, locust_request_label: str = 'Tasks') Dict[str, Any]

Retrieves all the available “tasks” and associated metadata from “Appian-Tempo-Tasks”

Note: All the retrieved data about tasks is stored in the private variable self._tasks

Returns (dict): List of tasks and associated metadata

Examples

>>> self.appian.task.get_all()
get_next_task_page_uri(get_default: bool = True) str | None

Retrieves the next URI in the sequence of Task pages being fetched using self.get_task_pages().

If the previous call to self.get_task_pages() reached the end of the available pages then this method will return either a value of None or the default initial page URI depending on the get_default argument.

Returns (str): The URI for the next page of Tasks (or the first page if the previous page fetches

reached the end).

get_task(task_name: str, exact_match: bool = True) Dict[str, Any]

Get the information about specific task by name.

Parameters:
  • task_name (str) – Name of the task

  • exact_match (bool) – Should task name match exactly or to be partial match. Default : True

Returns (dict): Specific task’s info

Raises: In case of task is not found in the system, it throws an “Exception”

Example

If full name of task is known,

>>> self.appian.task.get("task_name")

If only partial name is known,

>>> self.appian.task.get("task_name", exact_match=False)
get_task_form_json(task_name: str, locust_request_label: str = '', exact_match: bool = True) Dict[str, Any]

This function calls the API for the specific task to get its “form” data

Parameters:
  • task_name (str) – Name of the task to be called.

  • exact_match (bool, optional) – Should task name match exactly or to be partial match. Default : True

Returns (dict): Response of task’s Get UI call in dictionary

Examples

If full name of task is known,

>>> self.appian.task.get_task_form_json("task_name")

If only partial name is known,

>>> self.appian.task.get_task_form_json("task_name", exact_match=False)
get_task_pages(locust_request_label: str = 'Tasks', next_uri: str | None = '/suite/api/feed/tempo?m=menu-tasks&t=t&s=pt&defaultFacets=%255Bstatus-open%255D', pages_requested: int = -1) Dict[str, Any]

Retrieves all the available “tasks” and associated metadata from “Appian-Tempo-Tasks”

If the next_uri argument is specified then the calls to fetch tasks will begin at that URI. If omitted the fetching starts at the first page of Tasks. This can be useful for fetching a subset of pages one call at a time. To control the number of pages fetched use the page_count argument. The default of -1 means fetch all pages (starting from the given URI.

Note: If the page_count is used and is less than the total number of pages available then the URI of the _next_ page in the sequence will be stored in self._next_uri and can be fetched with self.get_next_task_page_uri()

Note: All the retrieved data about tasks is stored in the private variable self._tasks

Returns (dict): List of tasks and associated metadata

Examples:

Start at the first page and get all content from that point forward:

>>> self.appian.task.get_task_pages()

Start at the next page (from the previous call to get_task_pages) and fetch the next three pages of Tasks:

>>> self.appian.task.get_task_pages(next_uri=self.get_next_task_page_uri(), pages_requested=3)
class appian_locust._ui_reconciler.UiReconciler

Bases: object

CID_KEY = '_cId'

Reconciles the SAIL UI, based on the different responses passed

COMPONENT_DELTA_TYPE = 'UiComponentsDelta'
MODIFIED_COMPONENTS_KEY = 'modifiedComponents'
_traverse_and_update_state(state: Any, cid_to_component: dict) None

Moves through a dict recursively, swapping out any components that have been modified with new ones

reconcile_ui(old_state: dict, new_state: dict) dict
In the case where components are simply modified:

Makes a copy of the old_state, and applies whichever changes are necessary from the new_state

In the case where a completely new UI is returned:

Replaces the old state with the new state

Indices and tables