Custom Forms

Introduction

Customizable forms allows spintop-openhtf developers to include complex form inputs to their tests using simple dictionnary definitions. For example, as shown in the getting started page, the following form shows two input fields, First Name and Last Name:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
FORM_LAYOUT = {
    'schema':{
        'title': "First and Last Name",
        'type': "object",
        'required': ["firstname", "lastname"],
        'properties': {
            'firstname': {
                'type': "string", 
                'title': "First Name"
            },
            'lastname': {
                'type': "string", 
                'title': "Last Name"
            },
        }
    },
    'layout':[
        "firstname",
        "lastname"
    ]
}

Output

Normal Form

This FORM_LAYOUT variable contains two top level attributes which are essential to differentiate: schema and layout. The schema defines the fields while layout defines the ordering and display of these fields.

JSON Schema Forms

The schema part is actually a generic JSON schema vocabulary used to validate JSON documents named JSON Schema.

As quoted from their homepage,

JSON Schema is a vocabulary that allows you to annotate and validate JSON documents.

Example Schema

The format allows you to build the schema of complex JSON structures and afterward validate that a certain JSON document respects or not that structure. Let's take the schema part of our previous example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
    "title": "First and Last Name",
    "type": "object",
    "required": ["firstname", "lastname"],
    "properties": {
        "firstname": {
            "type": "string", 
            "title": "First Name"
        },
        "lastname": {
            "type": "string", 
            "title": "Last Name"
        },
    }
}

In order, this defines that:

  • "title": "First and Last Name": The title of this form is 'First and Last Name'.
  • "type": "object": The type of the top level JSON object is object, which means that it contains other properties.
  • "required": ["firstname", "lastname"]: The properties named firstname and lastname are required.
  • "properties": {: Begins the list of properties in this object. Note that these are unordered.
  • 1
    2
    3
    4
    "firstname": {
        "type": "string", 
        "title": "First Name"
    }
    
    The property named firstname is a string with label 'First Name'

  • And so on.

Note

Of all the keys shown here, only required is specific to JSON Schema Forms. The rest is part of the JSON Schema format. You can use the following playground to experiment with the forms: JSON Schema Form. However, the renderer is not the same that spintop-openhtf internally uses and therefore results may vary. You can use the getting started example in the spintop-openhtf repo for a quick demo with forms.

Example Data

The previous form would then successfully validate the following JSON Data:

1
2
3
4
{
  "firstname": "foo",
  "lastname": "bar"
}

This is the dictionnary that is returned when you call UserInput.prompt_form(...).

Layout

The layout aspect of our previous example is specific to JSON Schema Forms, and, more specifically, to the renderer we use.

Select Choices (Dropdown)

If we re-use the previous form and wish to limit the values allowed for a specific string field, we can use the layout attribute to impose a select field.

In the following snippet, the simple lastname key is replaced by a complex object which identifies the field using the "key": "lastname" attribute. By adding the "type": "select" with the titleMap, we impose specific choices to the user.

This does not make much sense in the case of a last name, but we use the same example for consistency.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
FORM_LAYOUT = {
    'schema':{
        'title': "First and Last Name",
        'type': "object",
        'required': ["firstname", "lastname"],
        'properties': {
            'firstname': {
                'type': "string", 
                'title': "First Name"
            },
            'lastname': {
                'type': "string", 
                'title': "Last Name"
            },
        }
    },
    'layout':[
        "firstname",
        {
            "key": "lastname",
            "type": "select",
            "titleMap": [
                { "value": "Andersson", "name": "Andersson" },
                { "value": "Johansson", "name": "Johansson" },
                { "value": "other", "name": "Something else..."}
            ]
        }
    ]
}

Output

Select Form

Radio Buttons

Same example with lastname:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
FORM_LAYOUT = {
    'schema':{
        'title': "First and Last Name",
        'type': "object",
        'required': ["firstname", "lastname"],
        'properties': {
            'firstname': {
                'type': "string", 
                'title': "First Name"
            },
            'lastname': {
                'type': "string", 
                'title': "Last Name"
            },
        }
    },
    'layout':[
        "firstname",
        {
            "key": "lastname",
            "type": "radiobuttons",
            "titleMap": [
                { "value": "one", "name": "One" },
                { "value": "two", "name": "More..." }
            ]
        }
    ]
}

Output

Radiobuttons Form

Text

Adding text within the form is very useful to guide or otherwise give more information to the user. This can be done using the "type": "help" layout.

v0.5.5

The markdown function was added in spintop-openhtf version 0.5.5. It transforms the text into HTML, which is the only understood format of the helpvalue.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from spintop_openhtf.util.markdown import markdown

FORM_LAYOUT = {
    'schema':{
        'title': "First and Last Name",
        'type': "object",
        'required': ["firstname", "lastname"],
        'properties': {
            'firstname': {
                'type': "string", 
                'title': "First Name"
            },
            'lastname': {
                'type': "string", 
                'title': "Last Name"
            },
        }
    },
    'layout':[
        "firstname",
        {
            "type": "help",
            "helpvalue": markdown("# Well Hello There!")
        },
        "lastname
    ]
}

Output

Text Form

Images

To seamlessly serve one or more image in your custom form or prompt message, the test plan image_url method needs to be used. This will create a temporary url that points to the local file you are targeting and allow browsers to load this image successfully.

Warning

The url returned by image_url is strictly temporary. It represents an in-memory mapping between the url and the filepath you specified. It follows the lifecycle of the TestPlan object, which means that as long as you keep the same test plan object, the url will live.

There are no cleanup mecanisms. However, each image is a simple key: value entry in a dictionnary, which means that its memory footprint is negligible.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from spintop_openhtf.util.markdown import markdown, image_url

plan = TestPlan('examples.getting_started')

helpvalue = markdown("""

# Well Hello There
<img src="%s" width="200px" />

""" % plan.image_url('spinhub-app-icon.png'))


FORM_LAYOUT = {
    'schema':{
        'title': "First and Last Name",
        'type': "object",
        'required': ["firstname", "lastname"],
        'properties': {
            'firstname': {
                'type': "string", 
                'title': "First Name"
            },
            'lastname': {
                'type': "string", 
                'title': "Last Name"
            },
        }
    },
    'layout':[
        "firstname",
        {
            "type": "help",
            "helpvalue": helpvalue
        },
        {
            "key": "lastname",
            "type": "radiobuttons",
            "titleMap": [
                { "value": "one", "name": "One" },
                { "value": "two", "name": "More..." }
            ]
        }
    ]
}

Output

Image Form