Skip to content
Phillip Markert edited this page Jun 26, 2018 · 9 revisions

Response Action Reference

and

Used to logically combine an array of actions with a common on_success/on_failure or for visual/logical clarity when nesting with other boolean operators. In order for the and action to succeed, all nested actions must succeed. Short-circuit evaluation is tracked and nested actions are processed sequentially, as soon as one action fails, subsequent actions will not be processed.

- and:
  - action1
  - action2
  ...
  - actionN

body

Captures the body(text) of the HTTP response into the session using the provided session key. Useful for saving the contents for output or later processing/manipulation. Since the body action operates against the response of an HTTP request, it is only valid when nested within a request's response property.

- request:
  # ...
  response:
    - body: session_key

# Now print out the body
- print: <% session_key %> 

compare

Aliases: equal, equals, greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to, not_equal, not_equals

Compares each element in an array to the following element using the specified operator.

- compare:
   array: [ item1, item2, ... itemN ]
   operator: "=" # or <, >, <=, >=, !=

If no operator is specified, the default operator is "=".

Example:

defaults

Alias for merge with override: false

The most common use case for defaults is to set default values in your script that can be overridden by passing in different values on the command-line or from a data file.

- defaults:
   user: John
   category: books
- print: <% username %> likes <% category %>

Running this with normally will print: John likes books but if the --qs category=cars cli option is passed in, it will print: John likes cars

As defaults is an alias for merge, the above would be normalized to the following:

- merge:
   data:
     user: John
     category: books
   override: false

delay

Causes the script to sleep for the specified number of milliseconds. The value should be a number or a numeric string value.

# Sleep for 300 milliseconds
- delay: 300

NOTE: delay can be combined with the random interpolation pipe to select a random length of time to delay (useful for simulating user think-times).

- delay: <% '3000-5000' | random %> # Random delay between 3000 and 5000 milliseconds
- delay: <% '400' | random %> # Random delay between 1-400 milliseconds

emit

Alias for print

equal

equals

Alias for compare with operator: =

- equals: [ item1, item2, item3 ]

will be normalized into

- compare: 
   array: [ item1, item2, item3 ]
   operator: ">="

fail

The fail action causes an error to be thrown with a specific message. This is most useful when combined with if/on_success/on_failure or other conditional branching statements.

- fail: Your request failed with error message: <% error %>

NOTE: a literal boolean "false" value for an action will be normalized (converted) into a fail action with a default message. This may be of limited use, but the hyperpotamus unit tests for the boolean operators make use of this for validation.

- false # Will be converted to "- fail: Explicit false"

function

The function action allows you to specify a custom javascript function to be executed. The custom function can capture data, run complex validations, or modify other aspects of the current script flow.

The action can either be a direct javascript function value, or must have a function property which is a javascript function. Javascript functions can be specified in YAML using the !!js/function syntax.

NOTE: JSON is incapable of representing literal functions, so in this case, YAML must be used.

The first parameter passed to your function is always the hyperpotamus context. The hyperpotamus context exposes information about the current request, response, session, and some other utility methods. (TODO - document the context object and available features.)

If the function has a single parameter, then it will be invoked synchronously. If the function returns void/null, the hyperpotamus engine will consider your invocation successful. Otherwise, any returned object will be considered an error which will be displayed to the user along with other diagnostic information.

If the function takes a second parameter, then your function is executed asynchronously. The additional parameter is the callback function that you need to invoke to signify completion of your function. In typical "node.js tradition", the first parameter to the callback is an error object. Leave it null to indicate successful execution.

Ideally error objects returned from custom functions should include a .message property for display to the user. Your error object may also specify a goto property which is either the name of the next request to execute or one of the special jump_keys (SELF, END, NEXT). As a special case if a string value is returned as the error, it will be treated as the message.

The javascript context this for the custom function will be bound to the action script element itself, so any additional properties on the action can be accessed from inside the function. This is a useful way to pass interpolated values or other "configuration" to your function.

External modules can be imported and used by your function using the optional imports property. imports should either be a string, array of strings, or an object with string property values. Each of the string values will be passed to a node.js require statement and the corresponding value in the imports property will be replaced with the imported module.

-  !!js/function >
   function(context) {
     if(context.response.status > 200)
       return "Unexpected status code";
   }
- value_to_hash: <% session_value %>
  imports:
    crypto: crypto
  function: !!js/function >
    function(context, callback) {
      var md5 = this.imports.crypto.createHash("MD5"); // from the imports property above
      md5.update(this.value_to_hash); // the 'this' context is bound to the action element itself.
      context.session["hash"] = md5.digest("hex");
      callback();
    }
}

NOTE: A common pitfall that I find when defining javascript functions in YAML is that tab-indentation is technically not allowed. I have put some workarounds in hyperpotamus to replace initial tabs with spaces, but YMMV. If you are getting strange errors from the YAML parser, this may be your problem.

NOTE: The function action is considered "unsafe" and so hyperpotamus must be set to allow unsafe actions.

goto

Causes the script execution to jump to the specified named step or one of the special jump targets. Any step in a hyperpotamus script can be named (using a name property). Names can help document what the steps in your script are doing, but more importantly, they also serve as the labels for goto actions to target. Additionally the following special jump targets can also be the target of a goto:

  • SELF - Repeats the current step in the script
  • NEXT - The following step in the script (skips past any remaining actions in the current step)
  • END - Stop processing the script and exit

goto can be used explicitly like so:

- actions: 
   goto: named_request

- actions:
   fail: "This should never get executed"

- name: named_request
  actions: noop

but it is also used as part of iterate, on_success, on_failure options. Any action can have an on_success or on_failure property. This property can be a single action or an array of actions that are processed if the parent action succeeds or fails. As a shortcut, if the on_success or on_failure property values are a string, then this will be normalized to a goto statement with the string value as the target.

- fail: Fail for some reason 
  on_failure: END
# is equivalent to 
- fail: Fail for some reason
  on_failure:
   goto: END

greater_than

Alias for compare with operator: >

- greater_than: [ 3, 2, 1 ]

will be normalized into

- compare: 
   array: [ 3, 2, 1 ]
   operator: ">"

greater_than_or_equal_to

Alias for compare with operator: >=

- greater_than_or_equal_to: [ 3, 2, 2, 1 ]

will be normalized into

- compare: 
   array: [ 3, 2, 2, 1 ]
   operator: ">="

headers

The headers action allows you to validate or capture content in HTTP response headers.

To match a regex (or capture named groups from a regex)

- headers:
   header-name1: !!js/regexp /regex to match or (:<named_capture>capture)/igm
# is equivalent to
   header-name1:
     regex:
       pattern: "regex to match or (:<named_capture>capture)"
       options: igm # ignore-case, global-match, multi-line

To capture the full header value into a variable

- headers:
   header-name1: variable_name
# is equivalent to
   header-name1: 
     capture: variable_name

To make sure the header value matches some exact text

- headers:
   header-name1: 
     text: Matching text

if

The if action allows for a specific action to be executed and if the action succeeds, the then actions will be executed. If the action fails, the optional else actions will be executed.

The if action to be tested can be a simple action which test for success or failure or it can be a boolean combination of other actions using and, or, or not.

- if:
   compare: [ <% username %>, "phillip" ]
  then:
   emit: "Welcome back!"
  else:
   - emit: "Not authorized."
   - goto: END

The above is actually equivalent (but perhaps a little bit clearer) to

- compare: [ <% username %>, "phillip" ]
  on_success:
   emit: "Welcome back!"
  on_failure:
   - emit: "Not authorized."
   - goto: END

iterate

The iterate action takes the name of an array variable (or an array of array variable names) and increments the current tracking index for each of the arrays. If all of the arrays were successfully iterated (meaning they had at least one more element in them), then the next value is used as the target of a goto statement. If any of the arrays were exhausted (or empty), then processing will continue with the next action/step in the script.

The default value for next is SELF, which will cause the current step to be repeated after advancing the array indices.

- iterate: users
- iterate: [ users, passwords ]
  next: step_to_repeat

NOTE: The current index of an array being iterated is kept in a session variable called [array_name].index. Before the array is iterated, this value most likely does not exist. A non-existant value for the .index property is treated as a 0-value (meaning the first element in the array). Each time the array is iterated, this value is incremented until the end of the array is reached. When the array has been exhausted (no more elements), the .index variable is removed from the session. At that point, another iterate action for the same array would start over at the beginning.

Iteration of an array is particularly useful when combined with the array access format specifier to print out the "current" item in an array.

- actions:
   set:
    product_id: [ '123', '456' ]

- request: 
   url : http://www.somesite.com/product_description?product_id=<% product_id | current | urlencode %>
  response: 
   iterate: "product_id"

This would execute the second step once for each of the product_id values.

jquery

The jquery action will execute a JQuery selector against the page contents, and for each matching element, capture one or more session items. The keys of the "capture" property-map are used to indicate to which session key you are capturing. The value of the capture keys can be either "@attribute", "text", "innerHTML", and "outerHTML". "html" is an alias for "outerHTML". If the value is a single element in an array, then all matched values will be captured into an array. If the value is not enclosed in an array, then only the last matching item on the page is captured.

{ jquery: "table.main td:nth-of-type(2) a", capture: { href : [ "@href" ], text : [ "text" ], html : "outerHTML" };

If this action were processed against a page that had three matches, the resulting href and text properties would have 3 elements, but the "html" element would only have the last matching element (because it is not surrounded with the '[ ]').

json

The json action can be used to capture or check for the existence of an element in a JSON response. The json action uses JSONPath to identify the target elements in the JSON.

To capture values or arrays of values, the json property should be a key/value collection. If the value is wrapped in array markers "[,]", then all instances of the value will be captured. Otherwise, only the first instance will be captured. Multiple key/value mappings can be captured in a single json action instance.

- json:
   keys: [ "$.nodes.*.key" ] # Captures an array of "key" properties
   first_key: "$.nodes.*.key" # Captures only the first "key"

Or alternatively, a single expression can be passed as an assertion or test.

- json: "$.nodes.success[?(@=true)]"

By default, if the value does not exist in the JSON, an error is thrown. The key can be prefixed with a "?" to make the capture value optional and return blank if the value could not be found.

less_than

Alias for compare with operator: <

- less_than: [ 1, 2, 3 ]

will be normalized into

- compare: 
   array: [ 1, 2, 3 ]
   operator: "<"

less_than_or_equal_to

Alias for compare with operator: <=

- less_than_or_equal_to: [ 1, 2, 2, 3 ]

will be normalized into

- compare: 
   array: [ 1, 2, 2, 3 ]
   operator: "<="
- body: captured_body
- md5: <% captured_body %>

merge

Aliases: set, defaults

merge will merge (assign) the values of the .source object into the specified .target object preserving or overwriting existing values according to the .override property. Nested objects and arrays will be merged recursively. If no value or reference is specified for the .target property, then the root-level session scope is targeted. .override defaults to false.

- merge:
    source:
      id: 12345
      user: jsmith
      pizza:
        toppings: [ cheese, pepperoni ]
        crust: deepdish
    override: true
    target: user.order

In this example, the properties under .source would be assigned to the user.order object in the session, overwriting any existing values that overlap.

NOTE: There is currently a limitation that the target object must already exist in the session, even as an empty object.

The simplest (and most common) use-cases for merge are through the use of the set and defaults aliases to assign session variables at the top-level session scope.

The defaults and set aliases set the .override flag accordingly and always target the root session scope.

The optional .target property may be an <%! object_reference %> or the path to an object within the session context.

noop

This action doesn't do anything. Seriously. :) Occasionally that becomes useful. Sometimes it is useful just as a placeholder, sometimes it is useful when combining with other logical operators. An action of literal true is equivalent to a noop.

- true
# is equivalent to
- noop: "anything" # or anything that is not "falsy" in javascript

not

Used to reverse the success/failure of a nested action(s).

- not:
    status: 500

not_equal

not_equals

Alias for compare with operator: !=

- not_equals: [ true, false ]

will be normalized into

- compare: 
   array: [ true, false ]
   operator: "!="

or

Succeeds if any of the nested actions are successful. Short-circuit evaluation is tracked, so as the nested actions are processed in sequence, as soon as any action succeeds, the rest of the actions will not be processed.

- or: 
  - status: 302
  - text: redirect

print

The print action sends text content to the output stream. All output is sent to the default output stream unless a .channel is specified (see below). emit is an alias for print.

- print: Hello world!
- emit: Hello <% name %>, today is <% day_of_week %>.
- emit: <% name %>,<% date %>,<% count %>

By default, the hyperpotamus command-line application echoes emit statements to stdout. If the --out command-line parameter is used to specify a filename, then the emit statements will be written to the specified file instead.

NOTE: Custom applications that use hyperpotamus as a library can listen for emit events on the processor and collect the data. There is a separate "channel" property on emit events that can optionally be specified in the action. When the emit callback event is passed, the value of the channel is passed as an additional parameter. This can allow for separate output streams for different purposes (i.e. errors, messages, and report values).

prompt

safe: false

Prompts the user for input values to be added to the session. Blocks the script waiting for user-input.

- prompt:
    username: "Please enter your username"
    password:
      message: "Enter your password"
      hidden: true

Unless required: true is specified, the user will not be prompted for a value if the target context session variable is already set. This can be useful for writing interactive scripts where users are prompted for any needed values, but if the user already knows the variables, they can be passed in. There are quite a few options for the prompt configuration.

  {
    description: 'Enter your password',     // Prompt displayed to the user. If not supplied name will be used.
    type: 'string',                 // Specify the type of input to expect.
    pattern: /^\w+$/,                  // Regular expression that input must be valid against.
    message: 'Password must be letters', // Warning message to display if validation fails.
    hidden: true,                        // If true, characters entered will either not be output to console or will be outputed using the `replace` string.
    replace: '*',                        // If `hidden` is set it will replace each hidden character with the specified string.
    default: 'lamepassword',             // Default value to use if no value is entered.
    required: true                        // If true, value entered must be non-empty.
    before: function(value) { return 'v' + value; } // Runs before node-prompt callbacks. It modifies user's input
  }

For more information, see the documentation for the prompt module

regex

See named-regex for an example. The regex action will either validate that an HTTP response matches a given pattern or, if the pattern contains named groups, will also capture values into the session with keys named after the groups. If the /g (global) regex option is specified, the results will be captured as an array.

- regex: "/Welcome, (:<name>\w+)\./i"

NOTE: As a shortcut, a RegExp object can also be passed directly instead of an object. If the action is a literal string that starts and ends with "/" characters, it will also be interpreted as a regex.

- regex: !!js/regexp /Welcome, (:<name>\w+)\./

request

The request action sends an HTTP/HTTPS request and executes any actions in the .response property once a response is received. For full details, see the Requests page.

The syntax for the request action is:

- request: {request_options} 
  response:
    - {response_action1}
    ...
    - {response_actionN}

The {request_options} can be a simple string (which will be interpreted as the URL), or it can be the full set of options as supported by the request module.

save

!! NOTE: If you are expecting to download/save binary files, please see this tip.

Causes the downloaded contents for the HTTP response body to be saved to the specified file. Any parent paths will automatically be created. Currently there are no safety checks on the filenames, so it his STRONGLY ENCOURAGED to perform some type of encoding on the filenames to prevent special characters. This also does not prevent users from putting in bad paths such as ".." so be careful which inputs you trust for this.

- save: "output/<% customer_name | urlencode %>.pdf" 

set

Alias for merge with override: true

Set values in the context session.

- set:
   user: John
   category: books
- print: <% username %> likes <% category %>

As set is an alias for merge, the above would be normalized to the following:

- merge:
   data:
     user: John
     category: books
   override: false

Because set is an alias for merge, one perhaps unexpected behavior for new users is that if the .source has a sub-object and the .target has a sub-object with the same name, any existing, but non-overlapping values on the target sub-object will remain. i.e. The entire sub-object is not replaced, but is merged with values from the .source

status

Verifies that the HTTP status code matches the expected value or one of the values in an array.

- status : 200

or

- status: [ 200, 204 ]

tally

text

Ensures that the specified text shows up in the HTTP response.

- text: "Hello, <% name %>"

xpath

Executes an XPath expression against the HTTP response (if the returned format was valid XML) and stores the results in the session key. The key for any properties is the session key where the results will be saved. The right-hand side should be an XPath expression that will be evaluated against the response document. If the right-hand-side is wrapped in an array, all matches will be returned as an array, otherwise, only the first match is returned as a scalar.

- xpath:
   username: "/user/@name"
   order_ids: [ "/user/orders/@id" ]
Clone this wiki locally