├── .gitignore ├── BUILD.rst ├── CalculatorLibrary.py ├── README.rst ├── calculator.py ├── data_driven.robot ├── docs ├── CalculatorLibrary.html ├── extra.css ├── index.html ├── log.html ├── report.html └── style.css ├── gherkin.robot ├── keyword_driven.robot ├── requirements.txt └── tasks.py /.gitignore: -------------------------------------------------------------------------------- 1 | .pydevproject 2 | .project 3 | *.pyc 4 | .DS_Store 5 | log.html 6 | output.xml 7 | report.html 8 | -------------------------------------------------------------------------------- /BUILD.rst: -------------------------------------------------------------------------------- 1 | Instructions to create releases 2 | =============================== 3 | 4 | Preconditions 5 | ------------- 6 | 7 | Operating system and Python requirements 8 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | 10 | Generating releases has only been tested on Linux, but it ought to work the 11 | same way also on OSX and other unixes. Creating releases is only supported 12 | with Python 3.6 or newer. 13 | 14 | The ``pip`` and ``invoke`` commands below are also expected to run on Python 15 | 3.6+. Alternatively, it's possible to use the ``python3.6 -m pip`` approach 16 | to run these commands. 17 | 18 | Python dependencies 19 | ~~~~~~~~~~~~~~~~~~~ 20 | 21 | Many steps are automated using the generic `Invoke `_ 22 | tool with a help by our `rellu `_ 23 | utilities, but also other tools and modules are needed. A pre-condition is 24 | installing all these, and that's easiest done using `pip 25 | `_ and the provided ``_ file:: 26 | 27 | pip install -r requirements.txt 28 | 29 | Using Invoke 30 | ~~~~~~~~~~~~ 31 | 32 | Invoke tasks are defined in the ``_ file and they are executed from 33 | the command line like:: 34 | 35 | inv[oke] task [options] 36 | 37 | Run ``invoke`` without arguments for help. All tasks can be listed using 38 | ``invoke --list`` and each task's usage with ``invoke --help task``. 39 | 40 | Creating release 41 | ---------------- 42 | 43 | 1. Test that everything works with Python 2 and Python 3. Exactly one test 44 | ought to fail:: 45 | 46 | python2 -m robot . 47 | python3 -m robot . 48 | 49 | 2. Regenerate log and report if needed using the command documented in wiki:: 50 | 51 | robot --name Robot --loglevel DEBUG keyword_driven.robot data_driven.robot gherkin.robot 52 | 53 | 3. Regenerate also library docs if needed:: 54 | 55 | inv kw-docs 56 | 57 | 4. Move regenerated log, report and library doc to docs:: 58 | 59 | inv move-docs 60 | 61 | 5. If README.rst has changed, generate project documentation based on it:: 62 | 63 | inv project-docs 64 | -------------------------------------------------------------------------------- /CalculatorLibrary.py: -------------------------------------------------------------------------------- 1 | from calculator import Calculator, CalculationError 2 | 3 | 4 | class CalculatorLibrary(object): 5 | """Test library for testing *Calculator* business logic. 6 | 7 | Interacts with the calculator directly using its ``push`` method. 8 | """ 9 | 10 | def __init__(self): 11 | self._calc = Calculator() 12 | self._result = '' 13 | 14 | def push_button(self, button): 15 | """Pushes the specified ``button``. 16 | 17 | The given value is passed to the calculator directly. Valid buttons 18 | are everything that the calculator accepts. 19 | 20 | Examples: 21 | | Push Button | 1 | 22 | | Push Button | C | 23 | 24 | Use `Push Buttons` if you need to input longer expressions. 25 | """ 26 | self._result = self._calc.push(button) 27 | 28 | def push_buttons(self, buttons): 29 | """Pushes the specified ``buttons``. 30 | 31 | Uses `Push Button` to push all the buttons that must be given as 32 | a single string. Possible spaces are ignored. 33 | 34 | Example: 35 | | Push Buttons | 1 + 2 = | 36 | """ 37 | for button in buttons.replace(' ', ''): 38 | self.push_button(button) 39 | 40 | def result_should_be(self, expected): 41 | """Verifies that the current result is ``expected``. 42 | 43 | Example: 44 | | Push Buttons | 1 + 2 = | 45 | | Result Should Be | 3 | 46 | """ 47 | if self._result != expected: 48 | raise AssertionError('%s != %s' % (self._result, expected)) 49 | 50 | def should_cause_error(self, expression): 51 | """Verifies that calculating the given ``expression`` causes an error. 52 | 53 | The error message is returned and can be verified using, for example, 54 | `Should Be Equal` or other keywords in `BuiltIn` library. 55 | 56 | Examples: 57 | | Should Cause Error | invalid | | 58 | | ${error} = | Should Cause Error | 1 / 0 | 59 | | Should Be Equal | ${error} | Division by zero. | 60 | """ 61 | try: 62 | self.push_buttons(expression) 63 | except CalculationError as err: 64 | return str(err) 65 | else: 66 | raise AssertionError("'%s' should have caused an error." 67 | % expression) 68 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Robot Framework Demo 3 | ==================== 4 | 5 | `Robot Framework`_ is a generic open source test automation framework. 6 | In addition to introducing Robot Framework test data syntax, this demo 7 | shows how to execute test cases, how generated reports and logs 8 | look like, and how to extend the framework with custom test libraries. 9 | 10 | .. contents:: **Contents:** 11 | :depth: 1 12 | :local: 13 | 14 | Downloading demo package 15 | ======================== 16 | 17 | To get the demo, you can either `download`_ and extract the latest 18 | package from the GitHub or checkout the `source code`_ directly. 19 | As a result you get ``RobotDemo`` directory with several files. 20 | 21 | Example `test cases`_, `test library`_ used by them, and `generated results`_ 22 | are available also online. Therefore, you do not need to download the demo if 23 | you are not interested in `running it`__ yourself. 24 | 25 | __ `running demo`_ 26 | 27 | Demo application 28 | ================ 29 | 30 | The demo application is a very simple calculator implemented with Python 31 | (`calculator.py`_). It contains only business logic and no user interface. 32 | You cannot really run the calculator manually. 33 | 34 | Test cases 35 | ========== 36 | 37 | The demo contains three different test case files illustrating three different 38 | approaches for creating test cases with Robot Framework. Click file names below 39 | to see the latest versions online. 40 | 41 | `keyword_driven.robot`_ 42 | Example test cases using the *keyword-driven* testing approach. 43 | 44 | All tests contain a workflow constructed from keywords in 45 | `CalculatorLibrary.py`_. Creating new tests or editing 46 | existing is easy even for people without programming skills. 47 | 48 | The *keyword-driven* approach works well for normal test 49 | automation, but the *gherkin* style might be even better 50 | if also business people need to understand tests. If the 51 | same workflow needs to repeated multiple times, it is best 52 | to use to the *data-driven* approach. 53 | 54 | `data_driven.robot`_ 55 | Example test cases using the *data-driven* testing approach. 56 | 57 | The *data-driven* style works well when you need to repeat 58 | the same workflow multiple times. 59 | 60 | Tests use ``Calculate`` keyword created in this file, that in 61 | turn uses keywords in `CalculatorLibrary.py`_. An exception 62 | is the last test that has a custom *template keyword*. 63 | 64 | `gherkin.robot`_ 65 | Example test case using the *gherkin* syntax. 66 | 67 | This test has a workflow similar to the *keyword-driven* 68 | examples. The difference is that the keywords use higher 69 | abstraction level and their arguments are embedded into 70 | the keyword names. 71 | 72 | This kind of *gherkin* syntax has been made popular by Cucumber_. 73 | It works well especially when tests act as examples that need to 74 | be easily understood also by the business people. 75 | 76 | As you can see, creating test cases with Robot Framework is very easy. 77 | See `Robot Framework User Guide`_ for details about the test data syntax. 78 | 79 | Test library 80 | ============ 81 | 82 | All test cases interact with the calculator using a custom test library named 83 | `CalculatorLibrary.py`_. In practice the library is just a Python class 84 | with methods that create the keywords used by the test cases. 85 | 86 | Generated library documentation makes it easy to see what keywords the 87 | library provides. This documentation is created with Libdoc_ tool integrated 88 | with the framework: 89 | 90 | - `CalculatorLibrary.html`_ 91 | 92 | As you can see, Robot Framework's test library API is very simple. 93 | See `Robot Framework User Guide`_ for more information about creating test 94 | libraries, using Libdoc, and so on. 95 | 96 | Generated results 97 | ================= 98 | 99 | After `running tests`_, you will get report and log in HTML format. Example 100 | files are also visible online in case you are not interested in running 101 | the demo yourself. Notice that one of the test has failed on purpose to 102 | show how failures look like. 103 | 104 | - `report.html`_ 105 | - `log.html`_ 106 | 107 | Running demo 108 | ============ 109 | 110 | Preconditions 111 | ------------- 112 | 113 | A precondition for running the tests is having `Robot Framework`_ installed. 114 | It is most commonly used on Python_ but it works also with Jython_ (JVM) 115 | and IronPython_ (.NET). Robot Framework `installation instructions`_ 116 | cover installation procedure in detail. People already familiar with 117 | installing Python packages and having `pip`_ package manager installed, can 118 | simply run the following command:: 119 | 120 | pip install robotframework 121 | 122 | Robot Framework 3.0 and newer support Python 3 in addition to Python 2. Also 123 | this demo project is nowadays Python 3 compatible. 124 | 125 | Running tests 126 | ------------- 127 | 128 | Test cases are executed with the ``robot`` command:: 129 | 130 | robot keyword_driven.robot 131 | 132 | .. note:: If you are using Robot Framework 2.9 or earlier, you need to 133 | use Python interpreter specific command ``pybot``, ``jybot`` or 134 | ``ipybot`` instead. 135 | 136 | To execute all test case files in a directory recursively, just give the 137 | directory as an argument. You can also give multiple files or directories in 138 | one go and use various command line options supported by Robot Framework. 139 | The results `available online`__ were created using the following command:: 140 | 141 | robot --name Robot --loglevel DEBUG keyword_driven.robot data_driven.robot gherkin.robot 142 | 143 | Run ``robot --help`` for more information about the command line usage and see 144 | `Robot Framework User Guide`_ for more details about test execution in general. 145 | 146 | __ `Generated results`_ 147 | 148 | 149 | .. _Robot Framework: http://robotframework.org 150 | .. _download: https://github.com/robotframework/RobotDemo/archive/master.zip 151 | .. _source code: https://github.com/robotframework/RobotDemo.git 152 | .. _calculator.py: https://github.com/robotframework/RobotDemo/blob/master/calculator.py 153 | .. _keyword_driven.robot: https://github.com/robotframework/RobotDemo/blob/master/keyword_driven.robot 154 | .. _CalculatorLibrary.py: https://github.com/robotframework/RobotDemo/blob/master/CalculatorLibrary.py 155 | .. _data_driven.robot: https://github.com/robotframework/RobotDemo/blob/master/data_driven.robot 156 | .. _gherkin.robot: https://github.com/robotframework/RobotDemo/blob/master/gherkin.robot 157 | .. _Cucumber: https://cucumber.io/ 158 | .. _Robot Framework User Guide: http://robotframework.org/robotframework/#user-guide 159 | .. _Python: http://python.org 160 | .. _Jython: http://jython.org 161 | .. _IronPython: http://ironpython.net 162 | .. _pip: http://pip-installer.org 163 | .. _installation instructions: https://github.com/robotframework/robotframework/blob/master/INSTALL.rst 164 | .. _Libdoc: http://robotframework.org/robotframework/#built-in-tools 165 | .. _CalculatorLibrary.html: http://robotframework.org/RobotDemo/CalculatorLibrary.html 166 | .. _report.html: http://robotframework.org/RobotDemo/report.html 167 | .. _log.html: http://robotframework.org/RobotDemo/log.html 168 | -------------------------------------------------------------------------------- /calculator.py: -------------------------------------------------------------------------------- 1 | class Calculator(object): 2 | BUTTONS = '1234567890+-*/C=' 3 | 4 | def __init__(self): 5 | self._expression = '' 6 | 7 | def push(self, button): 8 | if button not in self.BUTTONS: 9 | raise CalculationError("Invalid button '%s'." % button) 10 | if button == '=': 11 | self._expression = self._calculate(self._expression) 12 | elif button == 'C': 13 | self._expression = '' 14 | elif button == '/': 15 | self._expression += '//' # Integer division also in Python 3 16 | else: 17 | self._expression += button 18 | return self._expression 19 | 20 | def _calculate(self, expression): 21 | try: 22 | return str(eval(expression)) 23 | except SyntaxError: 24 | raise CalculationError('Invalid expression.') 25 | except ZeroDivisionError: 26 | raise CalculationError('Division by zero.') 27 | 28 | 29 | class CalculationError(Exception): 30 | pass 31 | -------------------------------------------------------------------------------- /data_driven.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Example test cases using the data-driven testing approach. 3 | ... 4 | ... The _data-driven_ style works well when you need to repeat 5 | ... the same workflow multiple times. 6 | ... 7 | ... Tests use ``Calculate`` keyword created in this file, that in 8 | ... turn uses keywords in ``CalculatorLibrary.py``. An exception 9 | ... is the last test that has a custom _template keyword_. 10 | ... 11 | ... Notice that one of these tests fails on purpose to show how 12 | ... failures look like. 13 | Test Template Calculate 14 | Library CalculatorLibrary.py 15 | 16 | *** Test Cases *** Expression Expected 17 | Addition 12 + 2 + 2 16 18 | 2 + -3 -1 19 | 20 | Subtraction 12 - 2 - 2 8 21 | 2 - -3 5 22 | 23 | Multiplication 12 * 2 * 2 48 24 | 2 * -3 -6 25 | 26 | Division 12 / 2 / 2 3 27 | 2 / -3 -1 28 | 29 | Failing 1 + 1 3 30 | 31 | Calculation error [Template] Calculation should fail 32 | kekkonen Invalid button 'k'. 33 | ${EMPTY} Invalid expression. 34 | 1 / 0 Division by zero. 35 | 36 | *** Keywords *** 37 | Calculate 38 | [Arguments] ${expression} ${expected} 39 | Push buttons C${expression}= 40 | Result should be ${expected} 41 | 42 | Calculation should fail 43 | [Arguments] ${expression} ${expected} 44 | ${error} = Should cause error C${expression}= 45 | Should be equal ${expected} ${error} # Using `BuiltIn` keyword 46 | -------------------------------------------------------------------------------- /docs/CalculatorLibrary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 160 | 230 | 243 | 266 | 317 | 520 | 524 | 536 | 549 | 552 | 553 | 554 | 555 | 556 |
557 |

Opening library documentation failed

558 |
    559 |
  • Verify that you have JavaScript enabled in your browser.
  • 560 |
  • Make sure you are using a modern enough browser. If using Internet Explorer, version 8 or newer is required.
  • 561 |
  • Check are there messages in your browser's JavaScript error log. Please report the problem if you suspect you have encountered a bug.
  • 562 |
563 |
564 | 565 | 569 | 570 | 770 | 771 | 816 | 817 | 836 | 837 | 848 | 849 | 860 | 861 | 877 | 878 | 900 | 901 | 902 | 910 | 911 | 912 | 913 | -------------------------------------------------------------------------------- /docs/extra.css: -------------------------------------------------------------------------------- 1 | /* Additional styles to use with style.css. */ 2 | 3 | .contents li p, table p { 4 | margin: 0; 5 | } 6 | th, td { 7 | padding: 0.2em 0.4em; 8 | border-width: 1px; 9 | border-style: solid; 10 | vertical-align: top; 11 | } 12 | th { 13 | white-space: nowrap; 14 | vertical-align: middle; 15 | } 16 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Robot Framework Demo 7 | 8 | 9 | 10 | 11 |
12 |

Robot Framework Demo

13 | 14 |

Robot Framework is a generic open source test automation framework. 15 | In addition to introducing Robot Framework test data syntax, this demo 16 | shows how to execute test cases, how generated reports and logs 17 | look like, and how to extend the framework with custom test libraries.

18 |
19 |

Contents:

20 | 28 |
29 |
30 |

Downloading demo package

31 |

To get the demo, you can either download and extract the latest 32 | package from the GitHub or checkout the source code directly. 33 | As a result you get RobotDemo directory with several files.

34 |

Example test cases, test library used by them, and generated results 35 | are available also online. Therefore, you do not need to download the demo if 36 | you are not interested in running it yourself.

37 |
38 |
39 |

Demo application

40 |

The demo application is a very simple calculator implemented with Python 41 | (calculator.py). It contains only business logic and no user interface. 42 | You cannot really run the calculator manually.

43 |
44 |
45 |

Test cases

46 |

The demo contains three different test case files illustrating three different 47 | approaches for creating test cases with Robot Framework. Click file names below 48 | to see the latest versions online.

49 |
50 |
keyword_driven.robot
51 |

Example test cases using the keyword-driven testing approach.

52 |

All tests contain a workflow constructed from keywords in 53 | CalculatorLibrary.py. Creating new tests or editing 54 | existing is easy even for people without programming skills.

55 |

The keyword-driven approach works well for normal test 56 | automation, but the gherkin style might be even better 57 | if also business people need to understand tests. If the 58 | same workflow needs to repeated multiple times, it is best 59 | to use to the data-driven approach.

60 |
61 |
data_driven.robot
62 |

Example test cases using the data-driven testing approach.

63 |

The data-driven style works well when you need to repeat 64 | the same workflow multiple times.

65 |

Tests use Calculate keyword created in this file, that in 66 | turn uses keywords in CalculatorLibrary.py. An exception 67 | is the last test that has a custom template keyword.

68 |
69 |
gherkin.robot
70 |

Example test case using the gherkin syntax.

71 |

This test has a workflow similar to the keyword-driven 72 | examples. The difference is that the keywords use higher 73 | abstraction level and their arguments are embedded into 74 | the keyword names.

75 |

This kind of gherkin syntax has been made popular by Cucumber. 76 | It works well especially when tests act as examples that need to 77 | be easily understood also by the business people.

78 |
79 |
80 |

As you can see, creating test cases with Robot Framework is very easy. 81 | See Robot Framework User Guide for details about the test data syntax.

82 |
83 |
84 |

Test library

85 |

All test cases interact with the calculator using a custom test library named 86 | CalculatorLibrary.py. In practice the library is just a Python class 87 | with methods that create the keywords used by the test cases.

88 |

Generated library documentation makes it easy to see what keywords the 89 | library provides. This documentation is created with Libdoc tool integrated 90 | with the framework:

91 | 94 |

As you can see, Robot Framework's test library API is very simple. 95 | See Robot Framework User Guide for more information about creating test 96 | libraries, using Libdoc, and so on.

97 |
98 |
99 |

Generated results

100 |

After running tests, you will get report and log in HTML format. Example 101 | files are also visible online in case you are not interested in running 102 | the demo yourself. Notice that one of the test has failed on purpose to 103 | show how failures look like.

104 | 108 |
109 |
110 |

Running demo

111 |
112 |

Preconditions

113 |

A precondition for running the tests is having Robot Framework installed. 114 | It is most commonly used on Python but it works also with Jython (JVM) 115 | and IronPython (.NET). Robot Framework installation instructions 116 | cover installation procedure in detail. People already familiar with 117 | installing Python packages and having pip package manager installed, can 118 | simply run the following command:

119 |
pip install robotframework
120 |

Robot Framework 3.0 and newer support Python 3 in addition to Python 2. Also 121 | this demo project is nowadays Python 3 compatible.

122 |
123 |
124 |

Running tests

125 |

Test cases are executed with the robot command:

126 |
robot keyword_driven.robot
127 |
128 |

Note

129 |

If you are using Robot Framework 2.9 or earlier, you need to 130 | use Python interpreter specific command pybot, jybot or 131 | ipybot instead.

132 |
133 |

To execute all test case files in a directory recursively, just give the 134 | directory as an argument. You can also give multiple files or directories in 135 | one go and use various command line options supported by Robot Framework. 136 | The results available online were created using the following command:

137 |
robot --name Robot --loglevel DEBUG keyword_driven.robot data_driven.robot gherkin.robot
138 |

Run robot --help for more information about the command line usage and see 139 | Robot Framework User Guide for more details about test execution in general.

140 |
141 |
142 |
143 | 144 | 145 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | :Author: Chad Skeeters 3 | :Contact: goobsoft@gmail.com 4 | 5 | Stylesheet for use with Docutils/rst2html. 6 | 7 | https://bitbucket.org/cskeeters/rst2html-style/ 8 | */ 9 | 10 | html { 11 | font-size: 100%; 12 | -webkit-text-size-adjust: 100%; 13 | -ms-text-size-adjust: 100%; 14 | } 15 | 16 | a:focus { 17 | outline: thin dotted #333; 18 | outline: 5px auto -webkit-focus-ring-color; 19 | outline-offset: -2px; 20 | } 21 | 22 | a:hover, 23 | a:active { 24 | outline: 0; 25 | } 26 | 27 | sub, 28 | sup { 29 | position: relative; 30 | font-size: 75%; 31 | line-height: 0; 32 | vertical-align: baseline; 33 | } 34 | 35 | sup { 36 | top: -0.5em; 37 | } 38 | 39 | sub { 40 | bottom: -0.25em; 41 | } 42 | 43 | img { 44 | width: auto\9; 45 | height: auto; 46 | max-width: 100%; 47 | vertical-align: middle; 48 | border: 0; 49 | -ms-interpolation-mode: bicubic; 50 | } 51 | 52 | @media print { 53 | * { 54 | color: #000 !important; 55 | text-shadow: none !important; 56 | background: transparent !important; 57 | box-shadow: none !important; 58 | } 59 | a, 60 | a:visited { 61 | text-decoration: underline; 62 | } 63 | a[href]:after { 64 | content: " (" attr(href) ")"; 65 | } 66 | abbr[title]:after { 67 | content: " (" attr(title) ")"; 68 | } 69 | .ir a:after, 70 | a[href^="javascript:"]:after, 71 | a[href^="#"]:after { 72 | content: ""; 73 | } 74 | pre, 75 | blockquote { 76 | border: 1px solid #999; 77 | page-break-inside: avoid; 78 | } 79 | thead { 80 | display: table-header-group; 81 | } 82 | tr, 83 | img { 84 | page-break-inside: avoid; 85 | } 86 | img { 87 | max-width: 100% !important; 88 | } 89 | @page { 90 | margin: 0.5cm; 91 | } 92 | h1 { 93 | page-break-before:always; 94 | } 95 | h1.title { 96 | page-break-before:avoid; 97 | } 98 | p, 99 | h2, 100 | h3 { 101 | orphans: 3; 102 | widows: 3; 103 | } 104 | h2, 105 | h3 { 106 | page-break-after: avoid; 107 | } 108 | } 109 | 110 | body { 111 | margin: 40px; 112 | margin-right: auto; 113 | margin-left: auto; 114 | width: 700px; 115 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 116 | font-size: 14px; 117 | line-height: 20px; 118 | color: #333333; 119 | background-color: #ffffff; 120 | } 121 | 122 | a { 123 | color: #0088cc; 124 | text-decoration: none; 125 | } 126 | 127 | a:hover, 128 | a:focus { 129 | color: #005580; 130 | text-decoration: underline; 131 | } 132 | 133 | .img-rounded { 134 | -webkit-border-radius: 6px; 135 | -moz-border-radius: 6px; 136 | border-radius: 6px; 137 | } 138 | 139 | .img-polaroid { 140 | padding: 4px; 141 | background-color: #fff; 142 | border: 1px solid #ccc; 143 | border: 1px solid rgba(0, 0, 0, 0.2); 144 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 145 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 146 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 147 | } 148 | 149 | p { 150 | margin: 0 0 10px; 151 | } 152 | 153 | small { 154 | font-size: 85%; 155 | } 156 | 157 | strong { 158 | font-weight: bold; 159 | } 160 | 161 | em { 162 | font-style: italic; 163 | } 164 | 165 | cite { 166 | font-style: normal; 167 | } 168 | 169 | h1, 170 | h2, 171 | h3, 172 | h4, 173 | h5, 174 | h6 { 175 | font-family: inherit; 176 | font-weight: bold; 177 | line-height: 20px; 178 | color: inherit; 179 | text-rendering: optimizelegibility; 180 | } 181 | h1 { 182 | font-size: 2em; 183 | padding-bottom:.2em; 184 | border-bottom:1px solid grey; 185 | } 186 | h1.title { 187 | padding-bottom:1em; 188 | border-bottom:0px; 189 | } 190 | h2 { 191 | font-size: 1.5em; 192 | } 193 | 194 | h3 { 195 | font-size: 1.3em; 196 | font-family:Georgia, serif; 197 | font-style:italic; 198 | /*font-weight:normal;*/ 199 | } 200 | 201 | h4 { 202 | font-size: 1.3em; 203 | } 204 | 205 | h5 { 206 | font-size: 1.2em; 207 | } 208 | 209 | h6 { 210 | font-size: 1.1em; 211 | } 212 | 213 | ul, 214 | ol { 215 | padding: 0; 216 | margin: 0 0 10px 25px; 217 | } 218 | 219 | ul ul, 220 | ul ol, 221 | ol ol, 222 | ol ul { 223 | margin-bottom: 0; 224 | } 225 | 226 | li { 227 | line-height: 20px; 228 | } 229 | 230 | dl { 231 | margin-bottom: 20px; 232 | } 233 | 234 | dt, 235 | dd { 236 | line-height: 20px; 237 | } 238 | 239 | dt { 240 | font-weight: bold; 241 | } 242 | 243 | dd { 244 | margin-left: 10px; 245 | } 246 | 247 | hr { 248 | margin: 20px 0; 249 | border: 0; 250 | border-top: 1px solid #eeeeee; 251 | border-bottom: 1px solid #ffffff; 252 | } 253 | 254 | abbr[title], 255 | abbr[data-original-title] { 256 | cursor: help; 257 | border-bottom: 1px dotted #999999; 258 | } 259 | 260 | abbr.initialism { 261 | font-size: 90%; 262 | text-transform: uppercase; 263 | } 264 | 265 | blockquote { 266 | padding: 0 0 0 15px; 267 | margin: 0 0 20px; 268 | border-left: 5px solid #eeeeee; 269 | } 270 | 271 | blockquote p { 272 | margin-bottom: 0; 273 | font-size: 17.5px; 274 | font-weight: 300; 275 | line-height: 1.25; 276 | } 277 | 278 | q:before, 279 | q:after, 280 | blockquote:before, 281 | blockquote:after { 282 | content: ""; 283 | } 284 | 285 | address { 286 | display: block; 287 | margin-bottom: 20px; 288 | font-style: normal; 289 | line-height: 20px; 290 | } 291 | 292 | code, 293 | pre { 294 | padding: 0 3px 2px; 295 | font-family: Monaco, Menlo, Consolas, "Courier New", monospace; 296 | font-size: 12px; 297 | color: #333333; 298 | -webkit-border-radius: 3px; 299 | -moz-border-radius: 3px; 300 | border-radius: 3px; 301 | } 302 | 303 | code { 304 | padding: 2px 4px; 305 | color: #d14; 306 | white-space: nowrap; 307 | background-color: #f7f7f9; 308 | border: 1px solid #e1e1e8; 309 | } 310 | 311 | pre { 312 | display: block; 313 | padding: 9.5px; 314 | margin: 0 0 10px; 315 | font-size: 13px; 316 | line-height: 20px; 317 | word-break: break-all; 318 | word-wrap: break-word; 319 | white-space: pre; 320 | white-space: pre-wrap; 321 | background-color: #f5f5f5; 322 | border: 1px solid #ccc; 323 | border: 1px solid rgba(0, 0, 0, 0.15); 324 | -webkit-border-radius: 4px; 325 | -moz-border-radius: 4px; 326 | border-radius: 4px; 327 | } 328 | 329 | pre.prettyprint { 330 | margin-bottom: 20px; 331 | } 332 | 333 | pre code { 334 | padding: 0; 335 | color: inherit; 336 | white-space: pre; 337 | white-space: pre-wrap; 338 | background-color: transparent; 339 | border: 0; 340 | } 341 | 342 | .pre-scrollable { 343 | max-height: 340px; 344 | overflow-y: scroll; 345 | } 346 | 347 | table { 348 | max-width: 100%; 349 | background-color: transparent; 350 | border-collapse: collapse; 351 | border-spacing: 0; 352 | } 353 | 354 | .table { 355 | width: 100%; 356 | margin-bottom: 20px; 357 | } 358 | 359 | .table th, 360 | .table td { 361 | padding: 8px; 362 | line-height: 20px; 363 | text-align: left; 364 | vertical-align: top; 365 | border-top: 1px solid #dddddd; 366 | } 367 | 368 | .table th { 369 | font-weight: bold; 370 | } 371 | 372 | .table thead th { 373 | vertical-align: bottom; 374 | } 375 | 376 | .table caption + thead tr:first-child th, 377 | .table caption + thead tr:first-child td, 378 | .table colgroup + thead tr:first-child th, 379 | .table colgroup + thead tr:first-child td, 380 | .table thead:first-child tr:first-child th, 381 | .table thead:first-child tr:first-child td { 382 | border-top: 0; 383 | } 384 | 385 | .table tbody + tbody { 386 | border-top: 2px solid #dddddd; 387 | } 388 | 389 | .table .table { 390 | background-color: #ffffff; 391 | } 392 | 393 | .table-condensed th, 394 | .table-condensed td { 395 | padding: 4px 5px; 396 | } 397 | 398 | .table-bordered { 399 | border: 1px solid #dddddd; 400 | border-collapse: separate; 401 | *border-collapse: collapse; 402 | border-left: 0; 403 | -webkit-border-radius: 4px; 404 | -moz-border-radius: 4px; 405 | border-radius: 4px; 406 | } 407 | 408 | .table-bordered th, 409 | .table-bordered td { 410 | border-left: 1px solid #dddddd; 411 | } 412 | 413 | .table-bordered caption + thead tr:first-child th, 414 | .table-bordered caption + tbody tr:first-child th, 415 | .table-bordered caption + tbody tr:first-child td, 416 | .table-bordered colgroup + thead tr:first-child th, 417 | .table-bordered colgroup + tbody tr:first-child th, 418 | .table-bordered colgroup + tbody tr:first-child td, 419 | .table-bordered thead:first-child tr:first-child th, 420 | .table-bordered tbody:first-child tr:first-child th, 421 | .table-bordered tbody:first-child tr:first-child td { 422 | border-top: 0; 423 | } 424 | 425 | .table-bordered thead:first-child tr:first-child > th:first-child, 426 | .table-bordered tbody:first-child tr:first-child > td:first-child, 427 | .table-bordered tbody:first-child tr:first-child > th:first-child { 428 | -webkit-border-top-left-radius: 4px; 429 | border-top-left-radius: 4px; 430 | -moz-border-radius-topleft: 4px; 431 | } 432 | 433 | .table-bordered thead:first-child tr:first-child > th:last-child, 434 | .table-bordered tbody:first-child tr:first-child > td:last-child, 435 | .table-bordered tbody:first-child tr:first-child > th:last-child { 436 | -webkit-border-top-right-radius: 4px; 437 | border-top-right-radius: 4px; 438 | -moz-border-radius-topright: 4px; 439 | } 440 | 441 | .table-bordered thead:last-child tr:last-child > th:first-child, 442 | .table-bordered tbody:last-child tr:last-child > td:first-child, 443 | .table-bordered tbody:last-child tr:last-child > th:first-child, 444 | .table-bordered tfoot:last-child tr:last-child > td:first-child, 445 | .table-bordered tfoot:last-child tr:last-child > th:first-child { 446 | -webkit-border-bottom-left-radius: 4px; 447 | border-bottom-left-radius: 4px; 448 | -moz-border-radius-bottomleft: 4px; 449 | } 450 | 451 | .table-bordered thead:last-child tr:last-child > th:last-child, 452 | .table-bordered tbody:last-child tr:last-child > td:last-child, 453 | .table-bordered tbody:last-child tr:last-child > th:last-child, 454 | .table-bordered tfoot:last-child tr:last-child > td:last-child, 455 | .table-bordered tfoot:last-child tr:last-child > th:last-child { 456 | -webkit-border-bottom-right-radius: 4px; 457 | border-bottom-right-radius: 4px; 458 | -moz-border-radius-bottomright: 4px; 459 | } 460 | 461 | .table-bordered tfoot + tbody:last-child tr:last-child td:first-child { 462 | -webkit-border-bottom-left-radius: 0; 463 | border-bottom-left-radius: 0; 464 | -moz-border-radius-bottomleft: 0; 465 | } 466 | 467 | .table-bordered tfoot + tbody:last-child tr:last-child td:last-child { 468 | -webkit-border-bottom-right-radius: 0; 469 | border-bottom-right-radius: 0; 470 | -moz-border-radius-bottomright: 0; 471 | } 472 | 473 | .table-bordered caption + thead tr:first-child th:first-child, 474 | .table-bordered caption + tbody tr:first-child td:first-child, 475 | .table-bordered colgroup + thead tr:first-child th:first-child, 476 | .table-bordered colgroup + tbody tr:first-child td:first-child { 477 | -webkit-border-top-left-radius: 4px; 478 | border-top-left-radius: 4px; 479 | -moz-border-radius-topleft: 4px; 480 | } 481 | 482 | .table-bordered caption + thead tr:first-child th:last-child, 483 | .table-bordered caption + tbody tr:first-child td:last-child, 484 | .table-bordered colgroup + thead tr:first-child th:last-child, 485 | .table-bordered colgroup + tbody tr:first-child td:last-child { 486 | -webkit-border-top-right-radius: 4px; 487 | border-top-right-radius: 4px; 488 | -moz-border-radius-topright: 4px; 489 | } 490 | 491 | .table-striped tbody > tr:nth-child(odd) > td, 492 | .table-striped tbody > tr:nth-child(odd) > th { 493 | background-color: #f9f9f9; 494 | } 495 | 496 | .table-hover tbody tr:hover > td, 497 | .table-hover tbody tr:hover > th { 498 | background-color: #f5f5f5; 499 | } 500 | 501 | table td[class*="span"], 502 | table th[class*="span"], 503 | .row-fluid table td[class*="span"], 504 | .row-fluid table th[class*="span"] { 505 | display: table-cell; 506 | float: none; 507 | margin-left: 0; 508 | } 509 | 510 | .hero-unit { 511 | padding: 60px; 512 | margin-bottom: 30px; 513 | font-size: 18px; 514 | font-weight: 200; 515 | line-height: 30px; 516 | color: inherit; 517 | background-color: #eeeeee; 518 | -webkit-border-radius: 6px; 519 | -moz-border-radius: 6px; 520 | border-radius: 6px; 521 | } 522 | 523 | .hero-unit h1 { 524 | margin-bottom: 0; 525 | font-size: 60px; 526 | line-height: 1; 527 | letter-spacing: -1px; 528 | color: inherit; 529 | } 530 | 531 | .hero-unit li { 532 | line-height: 30px; 533 | } 534 | 535 | 536 | /* rst2html default used to remove borders from tables and images */ 537 | .borderless, table.borderless td, table.borderless th { 538 | border: 0 } 539 | 540 | table.borderless td, table.borderless th { 541 | /* Override padding for "table.docutils td" with "! important". 542 | The right padding separates the table cells. */ 543 | padding: 0 0.5em 0 0 ! important } 544 | 545 | .first { 546 | /* Override more specific margin styles with "! important". */ 547 | margin-top: 0 ! important } 548 | 549 | .last, .with-subtitle { 550 | margin-bottom: 0 ! important } 551 | 552 | .hidden { 553 | display: none } 554 | 555 | a.toc-backref { 556 | text-decoration: none ; 557 | color: black } 558 | 559 | blockquote.epigraph { 560 | margin: 2em 5em ; } 561 | 562 | dl.docutils dd { 563 | margin-bottom: 0.5em } 564 | 565 | object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { 566 | overflow: hidden; 567 | } 568 | 569 | /* Uncomment (and remove this text!) to get bold-faced definition list terms 570 | dl.docutils dt { 571 | font-weight: bold } 572 | */ 573 | 574 | div.abstract { 575 | margin: 2em 5em } 576 | 577 | div.abstract p.topic-title { 578 | font-weight: bold ; 579 | text-align: center } 580 | 581 | div.admonition, div.attention, div.caution, div.danger, div.error, 582 | div.hint, div.important, div.note, div.tip, div.warning { 583 | margin: 2em ; 584 | border: medium outset ; 585 | padding: 1em } 586 | 587 | div.note, div.warning { 588 | margin:1.5em 0px; 589 | border: none; 590 | } 591 | 592 | div.note p.admonition-title, 593 | div.warning p.admonition-title 594 | { 595 | display:none; 596 | } 597 | 598 | /* Clearfix 599 | * http://css-tricks.com/snippets/css/clear-fix/ 600 | */ 601 | 602 | div.note:after, 603 | div.warning:after { 604 | content:""; 605 | display:table; 606 | clear:both; 607 | } 608 | 609 | div.note p:before, 610 | div.warning p:before { 611 | display:block; 612 | float:left; 613 | font-size:4em; 614 | line-height:1em; 615 | margin-right:20px; 616 | margin-left: 0em; 617 | margin-top:-10px; 618 | content:'\0270D'; /*handwriting*/ 619 | } 620 | 621 | div.warning p:before { 622 | content:'\026A0'; /*warning*/ 623 | } 624 | 625 | div.admonition p.admonition-title, div.hint p.admonition-title, 626 | div.important p.admonition-title, div.note p.admonition-title, 627 | div.tip p.admonition-title { 628 | font-weight: bold ; 629 | font-family: sans-serif } 630 | 631 | div.attention p.admonition-title, div.caution p.admonition-title, 632 | div.danger p.admonition-title, div.error p.admonition-title, 633 | div.warning p.admonition-title, .code .error { 634 | color: red ; 635 | font-weight: bold ; 636 | font-family: sans-serif } 637 | 638 | /* Uncomment (and remove this text!) to get reduced vertical space in 639 | compound paragraphs. 640 | div.compound .compound-first, div.compound .compound-middle { 641 | margin-bottom: 0.5em } 642 | 643 | div.compound .compound-last, div.compound .compound-middle { 644 | margin-top: 0.5em } 645 | */ 646 | 647 | div.dedication { 648 | margin: 2em 5em ; 649 | text-align: center ; 650 | font-style: italic } 651 | 652 | div.dedication p.topic-title { 653 | font-weight: bold ; 654 | font-style: normal } 655 | 656 | div.figure { 657 | margin-left: 2em ; 658 | margin-right: 2em } 659 | 660 | div.footer, div.header { 661 | clear: both; 662 | font-size: smaller } 663 | 664 | div.line-block { 665 | display: block ; 666 | margin-top: 1em ; 667 | margin-bottom: 1em } 668 | 669 | div.line-block div.line-block { 670 | margin-top: 0 ; 671 | margin-bottom: 0 ; 672 | margin-left: 1.5em } 673 | 674 | div.sidebar { 675 | margin: 0 0 0.5em 1em ; 676 | border: medium outset ; 677 | padding: 1em ; 678 | background-color: #ffffee ; 679 | width: 40% ; 680 | float: right ; 681 | clear: right } 682 | 683 | div.sidebar p.rubric { 684 | font-family: sans-serif ; 685 | font-size: medium } 686 | 687 | div.system-messages { 688 | margin: 5em } 689 | 690 | div.system-messages h1 { 691 | color: red } 692 | 693 | div.system-message { 694 | border: medium outset ; 695 | padding: 1em } 696 | 697 | div.system-message p.system-message-title { 698 | color: red ; 699 | font-weight: bold } 700 | 701 | div.topic { 702 | margin: 2em } 703 | 704 | h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, 705 | h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { 706 | margin-top: 0.4em } 707 | 708 | h1.title { 709 | text-align: center } 710 | 711 | h2.subtitle { 712 | text-align: center } 713 | 714 | hr.docutils { 715 | width: 75% } 716 | 717 | img.align-left, .figure.align-left, object.align-left { 718 | clear: left ; 719 | float: left ; 720 | margin-right: 1em } 721 | 722 | img.align-right, .figure.align-right, object.align-right { 723 | clear: right ; 724 | float: right ; 725 | margin-left: 1em } 726 | 727 | img.align-center, .figure.align-center, object.align-center { 728 | display: block; 729 | margin-left: auto; 730 | margin-right: auto; 731 | } 732 | 733 | .align-left { 734 | text-align: left } 735 | 736 | .align-center { 737 | clear: both ; 738 | text-align: center } 739 | 740 | .align-right { 741 | text-align: right } 742 | 743 | /* reset inner alignment in figures */ 744 | div.align-right { 745 | text-align: inherit } 746 | 747 | /* div.align-center * { */ 748 | /* text-align: left } */ 749 | 750 | ol.simple, ul.simple { 751 | margin-bottom: 1em } 752 | 753 | ol.arabic { 754 | list-style: decimal } 755 | 756 | ol.loweralpha { 757 | list-style: lower-alpha } 758 | 759 | ol.upperalpha { 760 | list-style: upper-alpha } 761 | 762 | ol.lowerroman { 763 | list-style: lower-roman } 764 | 765 | ol.upperroman { 766 | list-style: upper-roman } 767 | 768 | p.attribution { 769 | text-align: right ; 770 | margin-left: 50% } 771 | 772 | p.caption { 773 | font-style: italic } 774 | 775 | p.credits { 776 | font-style: italic ; 777 | font-size: smaller } 778 | 779 | p.label { 780 | white-space: nowrap } 781 | 782 | p.rubric { 783 | font-weight: bold ; 784 | font-size: larger ; 785 | color: maroon ; 786 | text-align: center } 787 | 788 | p.sidebar-title { 789 | font-family: sans-serif ; 790 | font-weight: bold ; 791 | font-size: larger } 792 | 793 | p.sidebar-subtitle { 794 | font-family: sans-serif ; 795 | font-weight: bold } 796 | 797 | p.topic-title { 798 | font-weight: bold } 799 | 800 | pre.address { 801 | margin-bottom: 0 ; 802 | margin-top: 0 ; 803 | font: inherit } 804 | 805 | pre.literal-block, pre.doctest-block, pre.math, pre.code { 806 | margin-left: 2em ; 807 | margin-right: 2em } 808 | 809 | pre.code .ln { color: grey; } /* line numbers */ 810 | pre.code, code { background-color: #eeeeee } 811 | pre.code .comment, code .comment { color: #5C6576 } 812 | pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } 813 | pre.code .literal.string, code .literal.string { color: #0C5404 } 814 | pre.code .name.builtin, code .name.builtin { color: #352B84 } 815 | pre.code .deleted, code .deleted { background-color: #DEB0A1} 816 | pre.code .inserted, code .inserted { background-color: #A3D289} 817 | 818 | span.classifier { 819 | font-family: sans-serif ; 820 | font-style: oblique } 821 | 822 | span.classifier-delimiter { 823 | font-family: sans-serif ; 824 | font-weight: bold } 825 | 826 | span.interpreted { 827 | font-family: sans-serif } 828 | 829 | span.option { 830 | white-space: nowrap } 831 | 832 | span.pre { 833 | white-space: pre } 834 | 835 | span.problematic { 836 | color: red } 837 | 838 | span.section-subtitle { 839 | /* font-size relative to parent (h1..h6 element) */ 840 | font-size: 80% } 841 | 842 | table.citation { 843 | border-left: solid 1px gray; 844 | margin-left: 1px } 845 | 846 | table.docinfo { 847 | margin: 2em 4em } 848 | 849 | table.docutils { 850 | margin-top: 0.5em ; 851 | margin-bottom: 0.5em } 852 | 853 | table.footnote { 854 | border-left: solid 1px black; 855 | margin-left: 1px } 856 | 857 | table.docutils td, table.docutils th, 858 | table.docinfo td, table.docinfo th { 859 | padding-left: 0.5em ; 860 | padding-right: 0.5em ; 861 | vertical-align: top } 862 | 863 | table.docutils th.field-name, table.docinfo th.docinfo-name { 864 | font-weight: bold ; 865 | text-align: left ; 866 | white-space: nowrap ; 867 | padding-left: 0 } 868 | 869 | h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, 870 | h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { 871 | font-size: 100% } 872 | 873 | ul.auto-toc { 874 | list-style-type: none } 875 | 876 | .code .pygments-hll { background-color: #ffffcc } 877 | .code .pygments-c { color: #60a0b0; font-style: italic } /* Comment */ 878 | .code .pygments-err { border: 1px solid #FF0000 } /* Error */ 879 | .code .pygments-k { color: #007020; font-weight: bold } /* Keyword */ 880 | .code .pygments-o { color: #666666 } /* Operator */ 881 | .code .pygments-cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */ 882 | .code .pygments-cp { color: #007020 } /* Comment.Preproc */ 883 | .code .pygments-c1 { color: #60a0b0; font-style: italic } /* Comment.Single */ 884 | .code .pygments-cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */ 885 | .code .pygments-gd { color: #A00000 } /* Generic.Deleted */ 886 | .code .pygments-ge { font-style: italic } /* Generic.Emph */ 887 | .code .pygments-gr { color: #FF0000 } /* Generic.Error */ 888 | .code .pygments-gh { color: #000080; font-weight: bold } /* Generic.Heading */ 889 | .code .pygments-gi { color: #00A000 } /* Generic.Inserted */ 890 | .code .pygments-go { color: #888888 } /* Generic.Output */ 891 | .code .pygments-gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 892 | .code .pygments-gs { font-weight: bold } /* Generic.Strong */ 893 | .code .pygments-gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 894 | .code .pygments-gt { color: #0044DD } /* Generic.Traceback */ 895 | .code .pygments-kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 896 | .code .pygments-kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 897 | .code .pygments-kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 898 | .code .pygments-kp { color: #007020 } /* Keyword.Pseudo */ 899 | .code .pygments-kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 900 | .code .pygments-kt { color: #902000 } /* Keyword.Type */ 901 | .code .pygments-m { color: #40a070 } /* Literal.Number */ 902 | .code .pygments-s { color: #4070a0 } /* Literal.String */ 903 | .code .pygments-na { color: #4070a0 } /* Name.Attribute */ 904 | .code .pygments-nb { color: #007020 } /* Name.Builtin */ 905 | .code .pygments-nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 906 | .code .pygments-no { color: #60add5 } /* Name.Constant */ 907 | .code .pygments-nd { color: #555555; font-weight: bold } /* Name.Decorator */ 908 | .code .pygments-ni { color: #d55537; font-weight: bold } /* Name.Entity */ 909 | .code .pygments-ne { color: #007020 } /* Name.Exception */ 910 | .code .pygments-nf { color: #06287e } /* Name.Function */ 911 | .code .pygments-nl { color: #002070; font-weight: bold } /* Name.Label */ 912 | .code .pygments-nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 913 | .code .pygments-nt { color: #062873; font-weight: bold } /* Name.Tag */ 914 | .code .pygments-nv { color: #bb60d5 } /* Name.Variable */ 915 | .code .pygments-ow { color: #007020; font-weight: bold } /* Operator.Word */ 916 | .code .pygments-w { color: #bbbbbb } /* Text.Whitespace */ 917 | .code .pygments-mf { color: #40a070 } /* Literal.Number.Float */ 918 | .code .pygments-mh { color: #40a070 } /* Literal.Number.Hex */ 919 | .code .pygments-mi { color: #40a070 } /* Literal.Number.Integer */ 920 | .code .pygments-mo { color: #40a070 } /* Literal.Number.Oct */ 921 | .code .pygments-sb { color: #4070a0 } /* Literal.String.Backtick */ 922 | .code .pygments-sc { color: #4070a0 } /* Literal.String.Char */ 923 | .code .pygments-sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 924 | .code .pygments-s2 { color: #4070a0 } /* Literal.String.Double */ 925 | .code .pygments-se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 926 | .code .pygments-sh { color: #4070a0 } /* Literal.String.Heredoc */ 927 | .code .pygments-si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 928 | .code .pygments-sx { color: #c65d09 } /* Literal.String.Other */ 929 | .code .pygments-sr { color: #235388 } /* Literal.String.Regex */ 930 | .code .pygments-s1 { color: #4070a0 } /* Literal.String.Single */ 931 | .code .pygments-ss { color: #517918 } /* Literal.String.Symbol */ 932 | .code .pygments-bp { color: #007020 } /* Name.Builtin.Pseudo */ 933 | .code .pygments-vc { color: #bb60d5 } /* Name.Variable.Class */ 934 | .code .pygments-vg { color: #bb60d5 } /* Name.Variable.Global */ 935 | .code .pygments-vi { color: #bb60d5 } /* Name.Variable.Instance */ 936 | .code .pygments-il { color: #40a070 } /* Literal.Number.Integer.Long */ 937 | -------------------------------------------------------------------------------- /gherkin.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Example test case using the gherkin syntax. 3 | ... 4 | ... This test has a workflow similar to the keyword-driven 5 | ... examples. The difference is that the keywords use higher 6 | ... abstraction level and their arguments are embedded into 7 | ... the keyword names. 8 | ... 9 | ... This kind of _gherkin_ syntax has been made popular by 10 | ... [http://cukes.info|Cucumber]. It works well especially when 11 | ... tests act as examples that need to be easily understood also 12 | ... by the business people. 13 | Library CalculatorLibrary.py 14 | 15 | *** Test Cases *** 16 | Addition 17 | Given calculator has been cleared 18 | When user types "1 + 1" 19 | and user pushes equals 20 | Then result is "2" 21 | 22 | *** Keywords *** 23 | Calculator has been cleared 24 | Push button C 25 | 26 | User types "${expression}" 27 | Push buttons ${expression} 28 | 29 | User pushes equals 30 | Push button = 31 | 32 | Result is "${result}" 33 | Result should be ${result} 34 | -------------------------------------------------------------------------------- /keyword_driven.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Example test cases using the keyword-driven testing approach. 3 | ... 4 | ... All tests contain a workflow constructed from keywords in 5 | ... ``CalculatorLibrary.py``. Creating new tests or editing 6 | ... existing is easy even for people without programming skills. 7 | ... 8 | ... The _keyword-driven_ appoach works well for normal test 9 | ... automation, but the _gherkin_ style might be even better 10 | ... if also business people need to understand tests. If the 11 | ... same workflow needs to repeated multiple times, it is best 12 | ... to use to the _data-driven_ approach. 13 | Library CalculatorLibrary.py 14 | 15 | *** Test Cases *** 16 | Push button 17 | Push button 1 18 | Result should be 1 19 | 20 | Push multiple buttons 21 | Push button 1 22 | Push button 2 23 | Result should be 12 24 | 25 | Simple calculation 26 | Push button 1 27 | Push button + 28 | Push button 2 29 | Push button = 30 | Result should be 3 31 | 32 | Longer calculation 33 | Push buttons 5 + 4 - 3 * 2 / 1 = 34 | Result should be 3 35 | 36 | Clear 37 | Push button 1 38 | Push button C 39 | Result should be ${EMPTY} # ${EMPTY} is a built-in variable 40 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements needed when generating releases. See BUILD.rst for details. 2 | invoke >= 0.20 3 | rellu >= 0.6 4 | docutils >= 0.14 5 | robotframework >= 3.1.1 6 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import shutil 3 | 4 | from docutils.core import publish_cmdline 5 | from invoke import task 6 | from rellu.tasks import clean 7 | from robot.libdoc import libdoc 8 | 9 | 10 | assert Path.cwd() == Path(__file__).parent 11 | 12 | 13 | @task 14 | def kw_docs(ctx): 15 | """Generates the library keyword documentation 16 | 17 | Documentation is generated by using the Libdoc tool. 18 | """ 19 | libdoc(str(Path('CalculatorLibrary.py')), 20 | str(Path('docs/CalculatorLibrary.html'))) 21 | 22 | 23 | @task 24 | def project_docs(ctx): 25 | """Generate project documentation. 26 | 27 | These docs are visible at http://robotframework.org/RobotDemo/. 28 | """ 29 | args = ['--stylesheet=style.css,extra.css', 30 | '--link-stylesheet', 31 | 'README.rst', 32 | 'docs/index.html'] 33 | publish_cmdline(writer_name='html5', argv=args) 34 | print(Path(args[-1]).absolute()) 35 | 36 | 37 | @task 38 | def move_docs(ctx): 39 | """Move report.html and log.html to docs 40 | 41 | These docs are visible http://robotframework.org/RobotDemo/. 42 | """ 43 | log = Path('./log.html') 44 | report = Path('./report.html') 45 | dest = Path('.') / 'docs' 46 | print(log.absolute()) 47 | shutil.copy(log.absolute(), str(dest)) 48 | print(report.absolute()) 49 | shutil.copy(report.absolute(), str(dest)) 50 | --------------------------------------------------------------------------------