├── .gitattributes
├── .github
└── workflows
│ └── run-tests.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── MANIFEST.in
├── README.md
├── docs
├── Makefile
├── make.bat
└── source
│ ├── _static
│ ├── css
│ │ └── custom.css
│ └── img
│ │ ├── article-page.png
│ │ ├── empty-test.png
│ │ ├── example-test.png
│ │ ├── header-page.png
│ │ ├── interactive-console.png
│ │ ├── suite-example.png
│ │ ├── test-with-data-table.png
│ │ └── test-with-pages.png
│ ├── browsers.md
│ ├── command-line-interface.md
│ ├── conf.py
│ ├── finding-elements.md
│ ├── golem-actions.md
│ ├── golem-test-framework.md
│ ├── golem_public_api
│ ├── browser.md
│ ├── execution.md
│ ├── golem-expected-conditions.md
│ ├── index.rst
│ ├── webdriver-class.md
│ └── webelement-class.md
│ ├── gui.md
│ ├── guides
│ ├── index.rst
│ ├── run-from-jenkins.md
│ ├── standalone-executable.md
│ └── using-multiple-browsers.md
│ ├── index.rst
│ ├── installation.md
│ ├── interactive-mode.md
│ ├── pages.md
│ ├── report.md
│ ├── running-tests.md
│ ├── settings.md
│ ├── suites.md
│ ├── test-data.md
│ ├── tests.md
│ ├── tutorial-part-1.md
│ ├── tutorial-part-2.md
│ └── waiting-for-elements.md
├── golem
├── __init__.py
├── actions.py
├── bin
│ ├── __init__.py
│ ├── golem_admin.py
│ ├── golem_init.py
│ └── golem_standalone.py
├── browser.py
├── cli
│ ├── __init__.py
│ ├── argument_parser.py
│ ├── commands.py
│ └── messages.py
├── core
│ ├── __init__.py
│ ├── environment_manager.py
│ ├── errors.py
│ ├── exceptions.py
│ ├── file_manager.py
│ ├── page.py
│ ├── parsing_utils.py
│ ├── project.py
│ ├── secrets_manager.py
│ ├── session.py
│ ├── settings_manager.py
│ ├── suite.py
│ ├── tags_manager.py
│ ├── test.py
│ ├── test_data.py
│ ├── test_directory.py
│ ├── test_parser.py
│ └── utils.py
├── execution.py
├── execution_runner
│ ├── __init__.py
│ ├── execution_runner.py
│ ├── interactive.py
│ └── multiprocess_executor.py
├── gui
│ ├── __init__.py
│ ├── api.py
│ ├── gui_start.py
│ ├── gui_utils.py
│ ├── report.py
│ ├── static
│ │ ├── css
│ │ │ ├── bootstrap
│ │ │ │ ├── bootstrap-theme.min.css
│ │ │ │ └── bootstrap.min.css
│ │ │ ├── code-editor-common.css
│ │ │ ├── font-awesome
│ │ │ │ └── font-awesome.min.css
│ │ │ ├── fonts
│ │ │ │ ├── FontAwesome.otf
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.svg
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ ├── fontawesome-webfont.woff
│ │ │ │ ├── fontawesome-webfont.woff2
│ │ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ │ ├── glyphicons-halflings-regular.woff
│ │ │ │ ├── glyphicons-halflings-regular.woff2
│ │ │ │ ├── lato-bol-webfont.woff
│ │ │ │ └── lato-lig-webfont.woff
│ │ │ ├── json_code_editor.css
│ │ │ ├── list_common.css
│ │ │ ├── main.css
│ │ │ ├── page_object.css
│ │ │ ├── report.css
│ │ │ ├── suite.css
│ │ │ ├── test_case.css
│ │ │ ├── test_case_code.css
│ │ │ ├── test_case_common.css
│ │ │ └── toastr
│ │ │ │ └── toastr.min.css
│ │ ├── img
│ │ │ ├── icons
│ │ │ │ ├── favicon-16x16.png
│ │ │ │ ├── favicon-32x32.png
│ │ │ │ └── favicon.ico
│ │ │ ├── plus_sign.png
│ │ │ └── plus_sign2.png
│ │ └── js
│ │ │ ├── drivers.js
│ │ │ ├── environments.js
│ │ │ ├── external
│ │ │ ├── bootstrap.min.js
│ │ │ ├── chart.min.js
│ │ │ ├── code_mirror
│ │ │ │ ├── addon
│ │ │ │ │ ├── edit
│ │ │ │ │ │ └── matchbrackets.js
│ │ │ │ │ └── hint
│ │ │ │ │ │ ├── python-hint.js
│ │ │ │ │ │ ├── simple-hint.css
│ │ │ │ │ │ └── simple-hint.js
│ │ │ │ ├── codemirror.css
│ │ │ │ ├── codemirror.js
│ │ │ │ ├── javascript.js
│ │ │ │ └── python.js
│ │ │ ├── datatable
│ │ │ │ ├── dataTables.bootstrap.js
│ │ │ │ └── datatables.min.js
│ │ │ ├── jquery.autocomplete.min.js
│ │ │ ├── jquery.min.js
│ │ │ ├── sortable.min.js
│ │ │ ├── toastr.min.js
│ │ │ └── treeview.js
│ │ │ ├── file.js
│ │ │ ├── index.js
│ │ │ ├── list
│ │ │ ├── list_common.js
│ │ │ ├── page_list.js
│ │ │ ├── suite_list.js
│ │ │ └── test_list.js
│ │ │ ├── main.js
│ │ │ ├── new_user.js
│ │ │ ├── page.js
│ │ │ ├── page_code.js
│ │ │ ├── report_dashboard.js
│ │ │ ├── report_dashboard_old.js
│ │ │ ├── report_execution.js
│ │ │ ├── report_test.js
│ │ │ ├── settings.js
│ │ │ ├── suite.js
│ │ │ ├── suite_code.js
│ │ │ ├── test.js
│ │ │ ├── test_code.js
│ │ │ ├── test_common.js
│ │ │ └── users.js
│ ├── templates
│ │ ├── 404.html
│ │ ├── common_element_error.html
│ │ ├── drivers.html
│ │ ├── environments.html
│ │ ├── index.html
│ │ ├── layout.html
│ │ ├── list
│ │ │ ├── page_list.html
│ │ │ ├── suite_list.html
│ │ │ └── test_list.html
│ │ ├── login.html
│ │ ├── not_permission.html
│ │ ├── page_builder
│ │ │ ├── page.html
│ │ │ └── page_code.html
│ │ ├── report
│ │ │ ├── report_dashboard.html
│ │ │ ├── report_dashboard_old.html
│ │ │ ├── report_execution.html
│ │ │ ├── report_execution_static.html
│ │ │ └── report_test.html
│ │ ├── settings
│ │ │ ├── global_settings.html
│ │ │ └── project_settings.html
│ │ ├── suite.html
│ │ ├── suite_code.html
│ │ ├── test_builder
│ │ │ ├── test.html
│ │ │ └── test_code.html
│ │ └── users
│ │ │ ├── reset_password.html
│ │ │ ├── user_form.html
│ │ │ ├── user_profile.html
│ │ │ └── users.html
│ ├── user_management.py
│ └── web_app.py
├── helpers.py
├── main.py
├── report
│ ├── __init__.py
│ ├── cli_report.py
│ ├── execution_report.py
│ ├── html_report.py
│ ├── junit_report.py
│ ├── report.py
│ ├── test_report.py
│ └── utils.py
├── test_runner
│ ├── __init__.py
│ ├── conf.py
│ ├── test_logger.py
│ ├── test_runner.py
│ └── test_runner_utils.py
└── webdriver
│ ├── __init__.py
│ ├── common.py
│ ├── extended_driver.py
│ ├── extended_webelement.py
│ └── golem_expected_conditions.py
├── images
├── example-test-code.png
├── execution-report.png
├── report-dashboard.png
├── test-case.png
└── test-execution-detail.png
├── pytest.ini
├── requirements.txt
├── requirements_dev.txt
├── setup.cfg
├── setup.py
├── tests
├── browser_test.py
├── cli
│ └── commands_test.py
├── conftest.py
├── core
│ ├── environment_manager_test.py
│ ├── file_manager_test.py
│ ├── page_test.py
│ ├── parsing_utils_test.py
│ ├── project_test.py
│ ├── secrets_manager_test.py
│ ├── settings_manager_test.py
│ ├── suite_test.py
│ ├── tags_manager_test.py
│ ├── test_data_test.py
│ ├── test_directory_test.py
│ ├── test_parser_test.py
│ ├── test_test.py
│ └── utils_test.py
├── execution_runner
│ └── execution_runner_test.py
├── golem_admin_cli_test.py
├── golem_cli_test.py
├── gui
│ ├── gui_utils_test.py
│ └── user_management_test.py
├── helpers_test.py
├── report
│ ├── cli_report_test.py
│ ├── execution_report_test.py
│ ├── html_report_test.py
│ ├── junit_report_test.py
│ ├── report_test.py
│ └── test_report_test.py
└── test_runner
│ └── test_runner_test.py
└── tox.ini
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.bat -text
--------------------------------------------------------------------------------
/.github/workflows/run-tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - "*"
7 | pull_request:
8 | branches:
9 | - "master"
10 |
11 | jobs:
12 | build:
13 | runs-on: ${{ matrix.os }}
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | include:
18 | - { python: '3.6', os: ubuntu-latest }
19 | - { python: '3.7', os: ubuntu-latest }
20 | - { python: '3.8', os: ubuntu-latest }
21 | - { python: '3.9', os: ubuntu-latest }
22 | - { python: '3.7', os: windows-latest }
23 | - { python: '3.7', os: macos-latest }
24 |
25 | steps:
26 | - uses: actions/checkout@v2
27 | - uses: actions/setup-python@v2
28 | with:
29 | python-version: ${{ matrix.python }}
30 | - name: Install dependencies
31 | run: python -m pip install --upgrade pip setuptools wheel pytest
32 | - name: Install
33 | run: |
34 | python setup.py install
35 | - name: Test with pytest
36 | run: |
37 | pytest tests
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info/
2 | *__pycache__/
3 | *.pyc
4 | .cache/
5 | .coverage
6 | .eggs/
7 | .tox
8 | .idea
9 | .vscode
10 | *.pytest_cache/
11 | build/
12 | dist/
13 | geckodriver.log
14 | docs/build/
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Golem
2 |
3 | Thank you for your interest in contributing to Golem!
4 |
5 | ## Questions
6 |
7 | If you have a question please use the Gitter channel: https://gitter.im/golem-framework/golem
8 |
9 | ## Submitting a Bug
10 |
11 | When reporting new bugs, please make sure that the report includes as much information as possible. Not all of the following is required, it depends on the issue.
12 |
13 | - A reproducible test case
14 | - Golem version
15 | - Operating System and Python version
16 | - Selenium version
17 | - Webdriver version
18 | - Browser version
19 | - Screenshot, traceback, or log
20 |
21 | Please note: Always try to use the latest version of Selenium, the Webdriver executables, and the browsers before raising a bug.
22 |
23 | ## Suggestions
24 |
25 | Enhancements and new feature ideas are welcome, however, time is limited. We encourage people to try and submit a pull request. Read how to do that below.
26 |
27 | ## How to Contribute Code
28 |
29 | If you have improvements or fixes for Golem, send us your pull requests! For those
30 | just getting started, Github has a [howto](https://help.github.com/articles/using-pull-requests/).
31 |
32 | ## Development Guide
33 |
34 | ### Running Unit Tests
35 |
36 | ```
37 | pip install pytest
38 | pytest run tests
39 | ```
40 |
41 | To run only fast tests use the following command:
42 |
43 | ```
44 | pytest run tests --fast
45 | ```
46 |
47 | ### Running Integration and UI tests
48 |
49 | Golem has suites for integration and UI tests. The steps to run them can be found here: https://github.com/golemhq/golem-tests
50 |
51 | ### Code Style
52 |
53 | The code style for the project is [PEP 8](https://www.python.org/dev/peps/pep-0008/) with the following additions:
54 |
55 | - Line length limit is extended to 90 characters
56 | - Line length can exceed 90 characters if readability would be reduced otherwise
57 | - Single quote strings are preferred unless the string itself contains a single quote.
58 |
59 | ### Building the Docs
60 |
61 | ```
62 | pip install sphinx
63 | pip install recommonmark
64 | cd docs
65 | make html
66 | ```
67 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Luciano Renzi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include MANIFEST.in
2 | include LICENSE
3 |
4 | recursive-include golem/gui/static *.*
5 | recursive-include golem/gui/templates *.*
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Golem - Test Automation
2 |
3 | [](https://github.com/golemhq/golem/actions/workflows/run-tests.yml?query=branch:master)
4 | [](https://golem-framework.readthedocs.io/en/latest/?badge=latest)
5 | [](https://gitter.im/golem-framework/golem?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | Golem is a test framework and a complete tool for browser automation.
8 | Tests can be written with code in Python, codeless using the web IDE, or both.
9 |
10 | 
11 |
12 | **Tests can be written with the web app**
13 |
14 |
15 |
16 |
17 | **But, they are still Python code**
18 |
19 |
20 |
21 |
22 | ## Batteries Included
23 |
24 | * Multi-user web IDE
25 | * Extended classes for [WebDriver](https://golem-framework.readthedocs.io/en/latest/golem_public_api/webdriver-class.html) and [WebElement](https://golem-framework.readthedocs.io/en/latest/golem_public_api/webelement-class.html)
26 | * More than 200 self documenting [Actions](https://golem-framework.readthedocs.io/en/latest/golem-actions.html)
27 | * [Webdriver-manager](https://github.com/golemhq/webdriver-manager) utility
28 | * Built in parallel test support
29 | * Reporting engine
30 |
31 |
32 |
33 | **Golem is still in beta!**. Read the changelog before upgrading.
34 |
35 |
36 |
37 | ## Screen Captures
38 |
39 | **Report Dashboard**
40 |
41 |
42 |
43 |
44 | **Execution Report**
45 |
46 |
47 |
48 |
49 | **Test Execution Detail**
50 |
51 |
52 |
53 |
54 | ## Installation
55 |
56 | Golem works with Python 3.6+
57 |
58 | ```
59 | pip install golem-framework
60 | ```
61 |
62 | Read the full installation guide here: [https://golem-framework.readthedocs.io/en/latest/installation.html](https://golem-framework.readthedocs.io/en/latest/installation.html)
63 |
64 | ## Quick Start
65 |
66 | **Create a test directory anywhere in your machine**
67 |
68 | ```
69 | golem-admin createdirectory
70 | ```
71 |
72 | **Download the latest webdriver executables**
73 |
74 | ```
75 | cd
76 | webdriver-manager update
77 | ```
78 |
79 | Webdriver executables are downloaded to the *drivers* folder. For more information check [this page](https://golem-framework.readthedocs.io/en/latest/browsers.html) of the documentation.
80 |
81 | **Start the Web Module**
82 |
83 | ```
84 | golem gui
85 | ```
86 |
87 | The Web Module can be accessed at http://localhost:5000/
88 |
89 | By default, the following user is available: username: *admin* / password: *admin*
90 |
91 | **Run a Test From Console**
92 |
93 | ```
94 | golem run
95 | golem run
96 | ```
97 |
98 | Args:
99 |
100 | * -b | --browsers: a list of browsers, by default use defined in settings.json or Chrome
101 | * -p | --processes: run in parallel, default 1 (not parallel)
102 | * -e | --environments: a list of environments, the default is none
103 | * -t | --tags: filter tests by tags
104 |
105 | ## Documentation
106 |
107 | [https://golem-framework.readthedocs.io/](https://golem-framework.readthedocs.io/)
108 |
109 | ## Questions
110 |
111 | If you have any question please use the [Gitter channel](https://gitter.im/golem-framework/golem).
112 |
113 | ## Contributing
114 |
115 | If you found a bug or want to contribute code please read the [contributing guide](https://github.com/golemhq/golem/blob/master/CONTRIBUTING.md).
116 |
117 | ## License
118 |
119 | [MIT](https://tldrlegal.com/license/mit-license)
120 |
121 | ## Credits
122 |
123 | Logo based on ["to believe"](https://www.toicon.com/icons/feather_believe) by Shannon E Thomas, [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)
124 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = python -msphinx
7 | SPHINXPROJ = Golem
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=python -msphinx
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 | set SPHINXPROJ=Golem
13 |
14 | if "%1" == "" goto help
15 |
16 | %SPHINXBUILD% >NUL 2>NUL
17 | if errorlevel 9009 (
18 | echo.
19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed,
20 | echo.then set the SPHINXBUILD environment variable to point to the full
21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the
22 | echo.Sphinx directory to PATH.
23 | echo.
24 | echo.If you don't have Sphinx installed, grab it from
25 | echo.http://sphinx-doc.org/
26 | exit /b 1
27 | )
28 |
29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
30 | goto end
31 |
32 | :help
33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
34 |
35 | :end
36 | popd
37 |
--------------------------------------------------------------------------------
/docs/source/_static/css/custom.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | table {
4 | font-family: arial, sans-serif;
5 | border-collapse: collapse;
6 | width: 100%;
7 | }
8 |
9 | td, th {
10 | border: 1px solid #dddddd;
11 | text-align: left;
12 | padding: 8px;
13 | }
14 |
15 | tr:nth-child(even) {
16 | background-color: #dddddd;
17 | }
18 |
19 | p.note {
20 |
21 | }
22 |
23 | .border-image {
24 | border: 1px solid #d3d3d3;
25 | padding: 10px;
26 | box-sizing: border-box;
27 | -webkit-box-sizing: border-box;
28 | -moz-box-sizing: border-box;
29 | }
30 |
31 | #permissionTable tr {
32 | background-color: white;
33 | }
34 |
35 | #permissionTable td, #permissionTable th {
36 | padding: 5px;
37 | }
38 |
39 | #permissionTable td {
40 | text-align: center;
41 | }
42 |
43 | .align-left {
44 | text-align: left !important;
45 | }
46 |
--------------------------------------------------------------------------------
/docs/source/_static/img/article-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/article-page.png
--------------------------------------------------------------------------------
/docs/source/_static/img/empty-test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/empty-test.png
--------------------------------------------------------------------------------
/docs/source/_static/img/example-test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/example-test.png
--------------------------------------------------------------------------------
/docs/source/_static/img/header-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/header-page.png
--------------------------------------------------------------------------------
/docs/source/_static/img/interactive-console.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/interactive-console.png
--------------------------------------------------------------------------------
/docs/source/_static/img/suite-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/suite-example.png
--------------------------------------------------------------------------------
/docs/source/_static/img/test-with-data-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/test-with-data-table.png
--------------------------------------------------------------------------------
/docs/source/_static/img/test-with-pages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/docs/source/_static/img/test-with-pages.png
--------------------------------------------------------------------------------
/docs/source/command-line-interface.md:
--------------------------------------------------------------------------------
1 | The CLI
2 | ==================================================
3 |
4 | ## golem-admin
5 |
6 | ### createdirectory
7 |
8 | ```
9 | golem-admin createdirectory
10 | ```
11 |
12 | Used to generate a new test directory.
13 | Name must be a relative or absolute path for the new test directory.
14 | Use '.' to use the current directory.
15 |
16 | ## golem
17 |
18 | ### run
19 |
20 | The command to run tests or suites from the command line:
21 |
22 | ```
23 | golem run [-b|--browsers]
24 | [-p|--processes] [-e|--environments] [-t|--tags]
25 | [-i|--interactive] [-r|--report] [--report-folder]
26 | [--report-name] [-l|--cli-log-level][--timestamp]
27 | ```
28 |
29 | #### -b, \-\-browsers
30 |
31 | One or more browsers to use for the tests.
32 | If not provided, the browsers defined inside the suite or the default browser defined in settings will be used.
33 | The valid options are listed [here](browsers.html#valid-options).
34 |
35 | #### -p, \-\-processes
36 |
37 | The number of tests to run in parallel. The default is 1.
38 |
39 | #### -e, \-\-environments
40 |
41 | The environments to use for the execution.
42 | If not provided, the environments defined inside the suite will be used.
43 |
44 | #### -t, \-\-tags
45 |
46 | Filter the tests by tags.
47 |
48 | Example, run all tests with tag "smoke":
49 |
50 | ```
51 | golem run project_name . --tags smoke
52 | ```
53 |
54 | Or using a tag expression:
55 |
56 | ```
57 | golem run project suite --tags "smoke and (regression or not 'release 001')"
58 | ```
59 |
60 | #### -i, \-\-interactive
61 |
62 | Run the test in interactive mode.
63 | This is required for the *interactive_mode* and *set_trace* actions.
64 |
65 | See [Interactive Mode](interactive-mode.html)
66 |
67 | #### -r, \-\-report
68 |
69 | Select which reports should be generated at the end of the execution.
70 | Options are: *junit*, *html*, *html-no-images*, and *json*
71 |
72 | #### \-\-report-folder
73 |
74 | Absolute path to the generated reports.
75 | The default is ```//projects//resports//```
76 |
77 | #### \-\-report-name
78 |
79 | Name of the generated reports. The default is 'report'
80 |
81 | #### \-l, \-\-cli-log-level
82 |
83 | command line log level.
84 | Options are: DEBUG, INFO, WARNING, ERROR, CRITICAL. Default is INFO.
85 |
86 | #### \-\-timestamp
87 |
88 | Used by the execution. Optional.
89 | The default is auto-generated with the format: 'year.month.day.hour.minutes.seconds.milliseconds'
90 |
91 | ### gui
92 |
93 | ```
94 | golem gui [--host -p|--port -d|--debug]
95 | ```
96 |
97 | Start Golem Web Module (GUI).
98 | Default host is 127.0.0.1 (localhost).
99 | Use host 0.0.0.0 to make the GUI publicly accessible.
100 | Default port is 5000.
101 | Debug runs the application in debug mode, default is False.
102 | Do not run in debug mode on production machines.
103 |
104 | See [GUI - Web Module](gui.html) for more info.
105 |
106 | ### createproject
107 |
108 | ```
109 | golem createproject
110 | ```
111 |
112 | Creates a new project with the given name. Creates the base files and folders.
113 |
114 | ### createtest
115 |
116 | ```
117 | golem createtest
118 | ```
119 |
120 | Creates a new test inside the given project.
121 |
122 | ### createsuite
123 |
124 | ```
125 | golem createsuite
126 | ```
127 |
128 | Creates a new suite inside the given project.
129 |
130 | ### createsuperuser
131 |
132 | ```
133 | golem createuser [-u|--username -e|email -p|--password -n|--noinput]
134 | ```
135 |
136 | Create a new superuser.
137 | The command is interactive unless username and password are provided.
138 | Email is optional.
139 |
140 | ## webdriver-manager
141 |
142 | ### update
143 |
144 | ```
145 | webdriver-manager update -b chrome
146 | ```
147 |
148 | To learn more about the Webdriver Manager see: .
149 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | from recommonmark.parser import CommonMarkParser
2 |
3 | import os
4 | import sys
5 |
6 | # insert Golem path into the system
7 | sys.path.insert(0, os.path.abspath(".."))
8 |
9 | import golem
10 |
11 |
12 | def setup(app):
13 | app.add_stylesheet('css/custom.css')
14 |
15 | # Add any Sphinx extension module names here, as strings. They can be
16 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
17 | # ones.
18 | extensions = ['sphinx.ext.coverage', 'sphinx.ext.autodoc']
19 |
20 | # Add any paths that contain templates here, relative to this directory.
21 | templates_path = ['_templates']
22 |
23 | # The suffix(es) of source filenames.
24 | # You can specify multiple suffix as a list of string:
25 | #
26 | # source_suffix = ['.rst', '.md']
27 | source_suffix = ['.rst', '.md']
28 |
29 | source_parsers = {
30 | '.md': CommonMarkParser,
31 | }
32 |
33 | # The master toctree document.
34 | master_doc = 'index'
35 |
36 | # General information about the project.
37 | project = 'Golem'
38 | copyright = '2017, Luciano Renzi'
39 | author = 'Luciano Renzi'
40 |
41 | html_theme = 'alabaster'
42 | html_sidebars = {
43 | '**': [
44 | 'about.html',
45 | 'navigation.html',
46 | 'relations.html',
47 | 'searchbox.html',
48 | ]
49 | }
50 | html_theme_options = {
51 | 'description': 'test automation framework',
52 | 'page_width': '1050px',
53 | 'github_user': 'golemhq',
54 | 'github_repo': 'golem',
55 | 'github_type': 'star',
56 | 'github_count': 'true',
57 | 'analytics_id': 'UA-139149408-1'
58 | }
59 |
60 | # The version info for the project you're documenting, acts as replacement for
61 | # |version| and |release|, also used in various other places throughout the
62 | # built documents.
63 | #
64 | # The short X.Y version.
65 | version = golem.__version__
66 | # The full version, including alpha/beta/rc tags.
67 | release = golem.__version__
68 |
69 | # The language for content autogenerated by Sphinx. Refer to documentation
70 | # for a list of supported languages.
71 | #
72 | # This is also used if you do content translation via gettext catalogs.
73 | # Usually you set "language" from the command line for these cases.
74 | language = None
75 |
76 | # List of patterns, relative to source directory, that match files and
77 | # directories to ignore when looking for source files.
78 | # This patterns also effect to html_static_path and html_extra_path
79 | exclude_patterns = []
80 |
81 | # The name of the Pygments (syntax highlighting) style to use.
82 | pygments_style = 'sphinx'
83 |
84 | # If true, `todo` and `todoList` produce output, else they produce nothing.
85 | todo_include_todos = False
86 |
87 |
88 | # Add any paths that contain custom static files (such as style sheets) here,
89 | # relative to this directory. They are copied after the builtin static files,
90 | # so a file named "default.css" will overwrite the builtin "default.css".
91 | html_static_path = ['_static']
92 |
93 |
94 | # -- Options for HTMLHelp output ------------------------------------------
95 |
96 | # Output file base name for HTML help builder.
97 | htmlhelp_basename = 'Golemdoc'
98 |
99 |
100 | # -- Options for LaTeX output ---------------------------------------------
101 |
102 | latex_elements = {
103 | # The paper size ('letterpaper' or 'a4paper').
104 | #
105 | # 'papersize': 'letterpaper',
106 |
107 | # The font size ('10pt', '11pt' or '12pt').
108 | #
109 | # 'pointsize': '10pt',
110 |
111 | # Additional stuff for the LaTeX preamble.
112 | #
113 | # 'preamble': '',
114 |
115 | # Latex figure (float) alignment
116 | #
117 | # 'figure_align': 'htbp',
118 | }
119 |
120 | # Grouping the document tree into LaTeX files. List of tuples
121 | # (source start file, target name, title,
122 | # author, documentclass [howto, manual, or own class]).
123 | latex_documents = [
124 | (master_doc, 'Golem.tex', 'Golem Documentation',
125 | 'Luciano Renzi', 'manual'),
126 | ]
127 |
128 |
129 | # -- Options for manual page output ---------------------------------------
130 |
131 | # One entry per manual page. List of tuples
132 | # (source start file, name, description, authors, manual section).
133 | man_pages = [
134 | (master_doc, 'golem', 'Golem Documentation',
135 | [author], 1)
136 | ]
137 |
138 |
139 | # -- Options for Texinfo output -------------------------------------------
140 |
141 | # Grouping the document tree into Texinfo files. List of tuples
142 | # (source start file, target name, title, author,
143 | # dir menu entry, description, category)
144 | texinfo_documents = [
145 | (master_doc, 'Golem', 'Golem Documentation',
146 | author, 'Golem', 'One line description of project.',
147 | 'Miscellaneous'),
148 | ]
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/docs/source/finding-elements.md:
--------------------------------------------------------------------------------
1 | Finding Elements
2 | ==================================================
3 |
4 | ## Actions and Elements
5 |
6 | Golem actions that require a WebElement can be defined in four different ways:
7 |
8 | An element tuple:
9 |
10 | ```python
11 | from golem import actions
12 |
13 | input = ('id', 'myElementId', 'Username input')
14 | actions.send_keys(input, 'my username')
15 |
16 | # the third element is optional
17 | button = ('css', 'button.btn-default')
18 | actions.click(button)
19 | ```
20 |
21 | A css selector string:
22 |
23 | ```python
24 | from golem import actions
25 |
26 | actions.send_keys('#myElementId', 'my username')
27 | actions.click('button.btn-default')
28 | ```
29 |
30 | An XPath selector string:
31 |
32 | ```python
33 | from golem import actions
34 |
35 | actions.send_keys('//input[@id="myElementId"]', 'my username')
36 | actions.click('//button[@class="btn-default"]')
37 | ```
38 |
39 | A WebElement object:
40 |
41 | ```python
42 | from golem import actions
43 |
44 | webelement = actions.get_browser().find(id='myElementId')
45 | actions.send_keys(webelement, 'my username')
46 |
47 | webelement = actions.get_browser().find(css='button.btn-default')
48 | actions.click(webelement)
49 | ```
50 |
51 |
52 | ## find and find_all
53 |
54 | The browser has two methods used to find elements: **find** and **find_all**
55 |
56 | ### find()
57 |
58 | GolemExtendedDriver.**find**(*element=None, id=None, name=None, link_text=None, partial_link_text=None, css=None, xpath=None, tag_name=None, timeout=None, wait_displayed=None*)
59 |
60 | The **find()** method provides a few ways to find elements.
61 | Only one search criteria must be provided.
62 | *element* must be a CSS selector string, an XPath selector string, an element tuple, or a WebElement object.
63 |
64 | The *timeout* argument determines how much time to wait until the element is present.
65 | If this is not provided, the value defined in settings by the *search_timeout* key will be used.
66 | This is considered the global search timeout.
67 |
68 | The *wait_displayed* argument makes **find()** wait for the element to be displayed (visible) as well.
69 | This value is taken by default from the *wait_displayed* key in settings.
70 |
71 | Some examples:
72 |
73 | ```python
74 | from golem import actions
75 |
76 |
77 | browser = actions.get_browser()
78 |
79 | # by an element tuple
80 | element = browser.find(('id', 'someId'))
81 |
82 | # by a css selector (positional argument)
83 | element = browser.find('input.someClass')
84 |
85 | # by an XPath selector (positional argument)
86 | element = browser.find('//input[@class="someClass"]')
87 |
88 | # by a WebElement object
89 | element = browser.find(id='someId')
90 | element = browser.find(element)
91 |
92 | # by css selector (keyword argument)
93 | element = browser.find(css='input.someClass')
94 |
95 | # by id
96 | element = browser.find(id='someId')
97 |
98 | # by name
99 | element = browser.find(name='someName')
100 |
101 | # by link text
102 | element = browser.find(link_text='link text')
103 |
104 | # by partial link text
105 | element = browser.find(partial_link_text='link')
106 |
107 | # by xpath (keyword argument)
108 | element = browser.find(xpath="//input[@id='someId']")
109 |
110 | # by tag name
111 | element = browser.find(tag_name='input')
112 | ```
113 |
114 | ### find_all()
115 |
116 | GolemExtendedDriver.**find_all**(*element=None, id=None, name=None, link_text=None, partial_link_text=None, css=None, xpath=None, tag_name=None*)
117 |
118 | Finds all the elements that match the selected criteria.
119 | Only one search criteria must be provided. Returns a list of WebElements.
120 | *element* must be a CSS selector string, an XPath selector string, an element tuple, or a WebElement object.
121 |
122 | ```python
123 | from golem import actions
124 |
125 | browser = actions.get_browser()
126 | table_rows = browser.find_all('table.myTable > tbody > tr')
127 | ```
128 |
129 | ## Finding children elements
130 |
131 | WebElements also have the *find()* and *find_all()* methods. They can be used to find children elements from a parent element.
132 |
133 | ```python
134 | from golem import actions
135 |
136 | browser = actions.get_browser()
137 |
138 | table_rows = browser.find('table.myTable').find_all('tr')
139 |
140 | for row in table_rows:
141 | print(row.find('td.resultColumn').text)
142 | ```
143 |
144 | ## element() and elements() Shortcuts
145 |
146 | **element()** and **elements()** provide handy shortcuts to **find()** and **find_all()** respectively.
147 |
148 | ```python
149 | from golem.browser import element, elements
150 |
151 |
152 | title = element(id='headerTitle')
153 | print(title.text)
154 |
155 | table_rows = elements('table > tbody > tr')
156 | print(len(table_rows))
157 | ```
--------------------------------------------------------------------------------
/docs/source/golem-test-framework.md:
--------------------------------------------------------------------------------
1 | Test Framework
2 | ==================================================
3 |
4 | ## Test File
5 |
6 | A test file contains one or more tests (functions that start with 'test')
7 |
8 | Test files also can have the setup and teardown hooks:
9 |
10 | ### Setup
11 |
12 | A function that is executed before every test of a test file.
13 |
14 | ### Teardown
15 |
16 | A function that is executed after all tests even if there were exceptions or errors.
17 |
18 | ### Example
19 |
20 | ```python
21 |
22 | description = 'the description for my test'
23 |
24 | pages = ['login', 'menu', 'releases']
25 |
26 | def setup(data):
27 | navigate(data.env.url)
28 | login.login(data.env.user1)
29 |
30 | def test(data):
31 | menu.navigate_to('Releases')
32 | data.store('release_name', 'release_001')
33 | releases.create_release(data.release_name)
34 | releases.assert_release_exists(data.release_name)
35 |
36 | def teardown(data):
37 | releases.remove_release(data.release_name)
38 | ```
39 |
40 | ## Test Results
41 |
42 | The test can end with one of the following result statuses:
43 |
44 | **Success**: The test run without any errors.
45 |
46 | **Failure**: The test threw an AssertionError.
47 |
48 | Possible reasons for a test to end with failure:
49 | * Actions that start with 'assert_'.
50 | ```python
51 | actions.assert_title('My App Title')
52 | ```
53 | * A call to *fail()* action.
54 | ```python
55 | actions.fail('this is a failure')
56 | ```
57 | * A normal Python assert statement.
58 | ```python
59 | assert browser.title == 'My App Title', 'this is the incorrect title'
60 | ```
61 |
62 | **Error**:
63 |
64 | The test had at least one error. Possible reasons for errors:
65 | * Actions that start with 'verify_'.
66 | * An error added manually:
67 | ```python
68 | actions.error('my error message')
69 | ```
70 |
71 | **Code error**:
72 |
73 | Any exception that is not an AssertionError will mark the test as 'code error'.
74 |
75 | Example:
76 |
77 | test1.py
78 | ```python
79 | def test(data):
80 | send_keys('#button', 'there is something missing here'
81 | ```
82 |
83 | ```bash
84 | >golem run project1 test1
85 | 17:55:25 INFO Test execution started: test1
86 | 17:55:25 ERROR SyntaxError: unexpected EOF while parsing
87 | Traceback (most recent call last):
88 | File "C:\...\testdir\projects\project1\tests\test1.py"
89 | SyntaxError: unexpected EOF while parsing
90 | 17:55:25 INFO Test Result: CODE ERROR
91 | ```
92 |
93 | ## Assertions and Verifications
94 |
95 | ### Soft Assertions
96 |
97 | Every action that starts with "verify" is a soft assertion, meaning that the error will be recorded. The test will be marked as 'error' at the end, but the test execution will not be stopped.
98 |
99 | ### Hard Assertions
100 |
101 | Every action that starts with "assert" is a hard assertion, meaning that it will stop the test execution at that point. The teardown method will still be executed afterward.
102 |
103 | ### Assertion Actions Example
104 |
105 | **verify_element_present(element)**:
106 | - It adds an error to the error list
107 | - Logs the error to console and to file
108 | - Takes a screenshot if *screenshot_on_error* setting is True
109 | - The test is not stopped.
110 | - The test result will be: 'error'
111 |
112 | **assert_element_present(element)**:
113 | - An AssertionError is thrown
114 | - It adds an error to the list
115 | - Logs the error to console and to file
116 | - Takes a screenshot if *screenshot_on_error* setting is True
117 | - The test is stopped, jumps to teardown function
118 | - The test result will be: 'failure'
119 |
--------------------------------------------------------------------------------
/docs/source/golem_public_api/browser.md:
--------------------------------------------------------------------------------
1 | Browser Module
2 | ==================================================
3 |
4 | Functions to manipulate WebDriver Browser instances.
5 |
6 | Location: golem.**browser**
7 |
8 |
9 | ## **open_browser**(browser_name=None, capabilities=None, remote_url=None, browser_id=None)
10 |
11 | When no arguments are provided the browser is selected from the CLI -b|--browsers argument, the suite *browsers* list, or the *default_browser* setting.
12 |
13 | This can be overridden in two ways:
14 | - a local webdriver instance or
15 | - a remote Selenium Grid driver instance.
16 |
17 | To open a local Webdriver instance pass browser_name with a [valid value](../browsers.html#valid-options)
18 |
19 | To open a remote Selenium Grid driver pass a capabilities dictionary and
20 | a remote_url.
21 | The minimum capabilities required is:
22 | ```
23 | {
24 | browserName: 'chrome'
25 | version: ''
26 | platform: ''
27 | }
28 | ```
29 | More info here: [https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities)
30 |
31 | If remote_url is None it will be taken from the `remote_url` setting.
32 |
33 | When opening more than one browser instance per test
34 | provide a browser_id to switch between browsers later on
35 |
36 | Returns:
37 | the opened browser
38 |
39 | ## **get_browser**()
40 |
41 | Returns the active browser. Starts a new one if there is none.
42 |
43 | ## **activate_browser**(browser_id)
44 |
45 | Activate an opened browser.
46 | Only needed when the test starts more than one browser instance.
47 |
48 | Raises:
49 | - InvalidBrowserIdError: The browser Id does not correspond to an opened browser
50 |
51 | Returns: the active browser
52 |
53 | ## **element**(*args, **kwargs)
54 |
55 | Shortcut to golem.browser.get_browser().find().
56 |
57 | See [find](webdriver-class.html#find-args-kwargs-small-golem-small).
58 |
59 | ## **elements**(*args, **kwargs)
60 |
61 | Shortcut to golem.browser.get_browser().find_all()
62 |
63 | See [find_all](webdriver-class.html#find-all-args-kwargs-small-golem-small).
--------------------------------------------------------------------------------
/docs/source/golem_public_api/execution.md:
--------------------------------------------------------------------------------
1 | Execution Module
2 | ==================================================
3 |
4 | This module stores values specific to a test file execution.
5 | These values should be read-only, modifying them can cause errors.
6 |
7 | Location: golem.**execution**
8 |
9 | **Example:**
10 |
11 | test.py
12 | ```
13 | from golem import execution
14 | from golem.browser import open_browser
15 |
16 |
17 | def test(data):
18 | print('Running test:', execution.test_name)
19 | open_browser()
20 | execution.browser.navigate('http://...')
21 | execution.logger.info('log this message')
22 | ```
23 |
24 | ## test_file
25 |
26 | Module path of the current test file, relative to the tests folder.
27 |
28 | ## browser
29 |
30 | The current active browser object. None if there is no open browser yet.
31 |
32 | ## browser_definition
33 |
34 | The browser definition passed to this test.
35 |
36 | ## browsers
37 |
38 | A dictionary with all the open browsers.
39 |
40 | ## data
41 |
42 | The data object.
43 |
44 | ## secrets
45 |
46 | The secrets data object.
47 |
48 | ## description
49 |
50 | The description of the test.
51 |
52 | ## settings
53 |
54 | The settings passed to this test.
55 |
56 | ## test_dirname
57 |
58 | The directory path where the test is located.
59 |
60 | ## test_path
61 |
62 | Full path to the test file.
63 |
64 | ## project_name
65 |
66 | Name of the project.
67 |
68 | ## project_path
69 |
70 | Path to the project directory.
71 |
72 | ## testdir
73 |
74 | Golem root directory.
75 |
76 | ## execution_reportdir
77 |
78 | Path for the execution report.
79 |
80 | ## testfile_reportdir
81 |
82 | Path for the test file report
83 |
84 | ## logger
85 |
86 | Test logger object.
87 |
88 | ## tags
89 |
90 | The list of tags passed to the execution.
91 |
92 | ## environment
93 |
94 | Name of the environment passed to the test.
95 | None is no environment was selected.
96 |
97 | ## Values for each test function
98 |
99 | ### test_name
100 |
101 | Current test function name.
102 |
103 | ### steps
104 |
105 | Steps collected by the current test function.
106 |
107 | ### errors
108 |
109 | A list of errors collected by the test function.
110 |
111 | ### test_reportdir
112 |
113 | Path for the test function report.
114 |
115 | ### timers
116 |
117 | A dictionary with timers, used by the *timer_start* and *timer_stop* actions.
118 |
--------------------------------------------------------------------------------
/docs/source/golem_public_api/golem-expected-conditions.md:
--------------------------------------------------------------------------------
1 | Golem Expected Conditions
2 | ==================================================
3 |
4 | Some extra expected conditions.
5 | Located at golem.webdriver.golem_expected_conditions.
6 |
7 | **element_to_be_enabled(element)**
8 |
9 | An Expectation for checking an element is enabled
10 |
11 | **text_to_be_present_in_page(text)**
12 |
13 | An Expectation for checking page contains text
14 |
15 | **element_text_to_be(element, text)**
16 |
17 | An expectation for checking the given text matches element text
18 |
19 | **element_text_to_contain(element, text)**
20 |
21 | An expectation for checking element contains the given text
22 |
23 | **element_to_have_attribute(element, attribute)**
24 |
25 | An expectation for checking element has attribute
26 |
27 | **window_present_by_partial_title(partial_title)**
28 |
29 | An expectation for checking a window is present by partial title
30 |
31 | **window_present_by_partial_url(partial_url)**
32 |
33 | An expectation for checking a window is present by partial url
34 |
35 | **window_present_by_title(title)**
36 |
37 | An expectation for checking a window is present by title
38 |
39 | **window_present_by_url(url)**
40 |
41 | An expectation for checking a window is present by url
--------------------------------------------------------------------------------
/docs/source/golem_public_api/index.rst:
--------------------------------------------------------------------------------
1 | API Reference
2 | =================================
3 |
4 | .. toctree::
5 | :maxdepth: 1
6 | :glob:
7 |
8 | browser
9 | /golem-actions
10 | execution
11 | webdriver-class
12 | webelement-class
13 | golem-expected-conditions
14 |
15 |
16 | .. Indices and tables
17 | .. ==================
18 |
19 | .. * :ref:`genindex`
20 | .. * :ref:`modindex`
21 | .. * :ref:`search`
22 |
--------------------------------------------------------------------------------
/docs/source/guides/index.rst:
--------------------------------------------------------------------------------
1 | Guides
2 | =================================
3 |
4 | .. toctree::
5 | :maxdepth: 1
6 | :glob:
7 |
8 | run-from-jenkins
9 | using-multiple-browsers
10 | standalone-executable
11 |
12 |
13 | .. Indices and tables
14 | .. ==================
15 |
16 | .. * :ref:`genindex`
17 | .. * :ref:`modindex`
18 | .. * :ref:`search`
19 |
--------------------------------------------------------------------------------
/docs/source/guides/run-from-jenkins.md:
--------------------------------------------------------------------------------
1 | Running Tests with Jenkins
2 | ==================================================
3 |
4 | In this guide let's see how Golem tests can be run in jenkins.
5 |
6 | ## Pre-requisites
7 |
8 | - Jenkins is installed.
9 | - Python 3.6+ is installed in the Jenkins machine.
10 | - A Golem directory with tests is stored in a git repository.
11 |
12 | ## Steps
13 |
14 | In Jenkins go to Dashboard > Manage Jenkins > Global Tool Configuration
15 |
16 | In Python > Python installations section add a Python version with the path to the executable:
17 |
18 | 
19 |
20 | We will be using ShiningPanda to manage the virtual environments in the Jenkins job:
21 | [https://plugins.jenkins.io/shiningpanda/](https://plugins.jenkins.io/shiningpanda/).
22 |
23 | In Jenkins go to Dashboard > Manage Jenkins > Manage Plugins.
24 | Install the ShiningPanda plugin and restart Jenkins.
25 |
26 | Create a new Jenkins job of type "Freestyle project"
27 |
28 | Define the location of the tests in the Source Code Management section:
29 |
30 | 
31 |
32 | Add a build step of type "Virtualenv Builder":
33 |
34 | 
35 |
36 | Add a post-build action that collects the generated JUnit XML report:
37 |
38 | 
39 |
40 | Run!
41 |
42 | 
43 |
44 |
45 |
--------------------------------------------------------------------------------
/docs/source/guides/standalone-executable.md:
--------------------------------------------------------------------------------
1 | Generate a Standalone Executable
2 | ==================================================
3 |
4 | ## Using PyInstaller
5 |
6 | A Golem standalone executable without any dependencies (including Python) can be generated using [PyInstaller](https://pyinstaller.readthedocs.io/).
7 |
8 | Note: the executable must be generated in the same platform that it will be used (e.g.: Windows 10 64 with Python 3.7)
9 |
10 | ### Steps
11 |
12 | Create an empty virtualenv (having the required packages only reduces the final executable size):
13 |
14 | ```
15 | virtualenv env
16 | ```
17 |
18 | Clone the repo and install:
19 |
20 | ```
21 | git clone https://github.com/golemhq/golem.git
22 | cd golem
23 | pip install .
24 | ```
25 |
26 | Install PyInstaller
27 |
28 | ```
29 | pip install pyinstaller
30 | ```
31 |
32 | Install python3-dev if needed (Linux)
33 | ```
34 | apt-get install python3-dev
35 | ```
36 |
37 | Generate the executable
38 |
39 | Linux:
40 | ```
41 | pyinstaller golem/bin/golem_standalone.py --onefile -n golem --add-data "golem/gui/templates:golem/gui/templates" --add-data "golem/gui/static:golem/gui/static"
42 | ```
43 |
44 | Windows:
45 | ```
46 | pyinstaller golem\bin\golem_standalone.py --onefile -n golem --add-data "golem\gui\templates;golem\gui\templates" --add-data "golem\gui\static;golem\gui\static"
47 | ```
48 |
49 | Where:
50 |
51 | ```--onefile``` generates a single file instead of a folder
52 |
53 | ```-n golem``` is the name of the executable
54 |
55 | ```--add-data``` includes the templates and static files required by the GUI
56 |
57 | The executable is generated in the *dist* folder.
58 |
59 |
60 | ## How to Use the Standalone Executable
61 |
62 | Put the executable in your path.
63 |
64 | The executable includes the *golem*, *golem-admin*, and *webdriver-manager* interfaces.
65 |
66 | Usage:
67 |
68 | ```
69 | golem golem-admin createdirectory .
70 | golem webdriver-manager update
71 | golem gui
72 | golem run project test
73 | ```
74 |
--------------------------------------------------------------------------------
/docs/source/guides/using-multiple-browsers.md:
--------------------------------------------------------------------------------
1 | Using Multiple Browsers Sessions
2 | ==================================================
3 |
4 | Sometimes a test requires two or more different browser sessions opened at the same time.
5 |
6 | A browser is opened by default when using an action that needs a browser.
7 | To open a browser explicitly use the **open_browser** action or *golem.browser.**open_browser()***.
8 |
9 | ## Open Multiple Browsers
10 |
11 | To open a second browser use **open_browser** again and pass an id to identify it.
12 | The first browser has 'main' as its id by default.
13 |
14 | The list of opened browsers is stored in golem.execution.browsers.
15 |
16 | To use a browser when there is more than one, it has to be activated first:
17 |
18 | ```python
19 | open_browser()
20 | open_browser('second')
21 | activate_browser('second')
22 | ```
23 |
24 | As an example, testing a chat application with two concurrent users:
25 |
26 | ```python
27 | def test(data):
28 | navigate('https://app-url.com/') # browser opened with id='main'
29 | open_browser('second browser') # second browser opened
30 | navigate('https://app-url.com/')
31 | activate_browser('main')
32 | send_chat_message('hey there!')
33 | activate_browser('second browser')
34 | assert_message_received('hey there!')
35 | ```
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | Golem
2 | =================================
3 |
4 | Intro
5 | ********
6 | Golem is a complete test automation tool and framework for end-to-end testing.
7 | It creates powerful, robust and maintainable test suites, yet, it is easy to pick up and learn even without a lot of programming knowledge.
8 | It is based on Selenium Webdriver and it can be extended using Python.
9 |
10 | **It can:**
11 |
12 | - Use the Page Object pattern
13 | - Write tests with multi data sets (data-driven)
14 | - Run tests in parallel
15 | - Test APIs
16 | - Run tests remotely (Selenium Grid or a cloud testing provider)
17 | - It can be executed from Jenkins or any other CI tool
18 |
19 |
20 | **It has:**
21 |
22 | - A complete GUI module (a web application) to write and execute tests
23 | - A reporting engine and a web reports module
24 | - An interactive console
25 |
26 |
27 | Selenium
28 | ********
29 |
30 | Some prior knowledge of Selenium is required to work with Golem.
31 | Golem extends Selenium webdriver and webelement classes and uses a simplified interface for finding elements.
32 | It is advised to understand how standard Selenium works.
33 | `This guide `_ is a good place to start.
34 |
35 |
36 | Contents
37 | ********
38 |
39 | .. toctree::
40 | :maxdepth: 2
41 | :glob:
42 |
43 | installation
44 | tutorial-part-1
45 | tutorial-part-2
46 | browsers
47 | finding-elements
48 | waiting-for-elements
49 | golem-actions
50 | tests
51 | pages
52 | suites
53 | test-data
54 | running-tests
55 | report
56 | settings
57 | golem-test-framework
58 | gui
59 | command-line-interface
60 | interactive-mode
61 | golem_public_api/index
62 | guides/index
63 |
64 | .. Indices and tables
65 | .. ==================
66 |
67 | .. * :ref:`genindex`
68 | .. * :ref:`modindex`
69 | .. * :ref:`search`
70 |
--------------------------------------------------------------------------------
/docs/source/installation.md:
--------------------------------------------------------------------------------
1 | Installation
2 | ==================================================
3 |
4 | ## Requirements
5 |
6 | ### Python
7 |
8 | Golem requires Python 3.6 or higher.
9 |
10 | **Windows**:
11 |
12 | The Windows installer works fine, you can get it from here: [python.org/downloads/](http://www.python.org/downloads/)
13 |
14 | **Mac**:
15 |
16 | To install on Mac OS follow these instructions: [Installing Python 3 on Mac OS X](http://python-guide.readthedocs.io/en/latest/starting/install3/osx/)
17 |
18 | **Linux**:
19 |
20 | Debian 8 and Ubuntu 14.04 comes with Python 3.4 pre-installed, newer Linux distributions might come with newer Python versions.
21 |
22 | Since Linux tends to have both Python 2 and 3 installed alongside each other, the command to execute the latter should be 'python3' instead of just 'python'.
23 |
24 | ### PIP
25 |
26 | PIP is the package manager for Python. It is required to install Golem and its dependencies. Check if you already have it. PIP comes bundled with the newer versions of Python.
27 |
28 | ```
29 | pip --version
30 | ```
31 | or
32 | ```
33 | pip3 --version
34 | ```
35 |
36 | If you don't have PIP installed, follow [these instructions](https://pip.pypa.io/en/stable/installing/).
37 |
38 |
39 | ## Create a Virtual Environment
40 |
41 | It is recommended to install Golem and its dependencies in a [virtual environment](http://www.virtualenv.org/en/latest/) instead of globally. To do that, follow these steps:
42 |
43 | ### Install Virtualenv
44 |
45 | ```
46 | pip install virtualenv
47 | ```
48 |
49 | Create a new virtualenv in the './env' folder
50 |
51 | ```
52 | virtualenv env
53 | ```
54 |
55 | If the virtual environment is being created with Python 2 instead of 3, use the following command instead:
56 |
57 | ```
58 | virtualenv env -p python3
59 | ```
60 |
61 | ### Activate the Environment
62 |
63 | To use a virtual environment it needs to be activated first.
64 |
65 | **Windows**:
66 |
67 | ```
68 | env\scripts\activate
69 | ```
70 |
71 | **Mac/Linux**:
72 |
73 | ```
74 | source env/bin/activate
75 | ```
76 |
77 | ## Install Golem Using PIP
78 |
79 | The quickest and the preferred way to install Golem.
80 |
81 | ```
82 | pip install golem-framework
83 | ```
84 |
85 |
86 | ## Installing From Source
87 |
88 | ```
89 | pip install -U https://github.com/golemhq/golem/archive/master.tar.gz
90 | ```
91 |
--------------------------------------------------------------------------------
/docs/source/interactive-mode.md:
--------------------------------------------------------------------------------
1 | Interactive Mode
2 | ==================================================
3 |
4 | With Golem the execution of a test can be paused at any point to start an interactive console with all the actions available.
5 | This is useful for debugging tests.
6 |
7 | ## interactive_mode Action
8 |
9 | To start the interactive console at any point of a test just add the **interactive_mode** action. Example:
10 |
11 | **test.py**
12 | ```python
13 | def test(data):
14 | navigate('http://wikipedia.org/')
15 | interactive_mode()
16 | click(page.button)
17 | take_screenshot('final screenshot')
18 | ```
19 |
20 | When the test reaches the second step, the interactive console is going to start:
21 |
22 |
23 | 
24 |
25 |
26 | When the interactive console is terminated, the test will resume the execution from that point on.
27 |
28 |
29 |
30 |
Note
31 |
If the test is not run with the -i flag, the interactive_mode action will be ignored
32 |
33 |
34 |
35 | ## Quick Interactive Mode
36 |
37 | It is possible to start a quick interactive shell by not providing a project and test to the run command:
38 |
39 | ```
40 | golem run -i
41 | ```
42 |
43 | This will start an interactive console with a clean slate.
44 |
45 | ```text
46 | >golem run -i
47 | Entering interactive mode
48 | type exit() to stop
49 | type help() for more info
50 | >>> navigate('https://en.wikipedia.org')
51 | 12:47:54 INFO Navigate to: 'https://en.wikipedia.org'
52 | >>> send_keys('#searchInput', 'automation')
53 | 12:48:58 INFO Write 'automation' in element #searchInput
54 | >>> click('#searchButton')
55 | 12:49:18 INFO Click #searchButton
56 | >>> get_browser().title
57 | 'Automation - Wikipedia'
58 | >>> assert_title_contains('Automation')
59 | 12:49:50 INFO Assert page title contains 'Automation'
60 | ```
61 |
62 | ## Python Debugger
63 |
64 | It is possible to add a Python debugger breakpoint (pdb.set_trace()) using the **set_trace** action.
65 | As with the **interactive_mode**, the test must be run with the *-i* flag for this action to take effect.
66 | More info about pdb [here](https://docs.python.org/3/library/pdb.html).
67 |
68 | Example:
69 |
70 | **test.py**
71 | ```python
72 | def test(data):
73 | navigate('https://en.wikipedia.org')
74 | set_trace()
75 | ```
76 |
77 | ```text
78 | >golem run project test -i
79 | 12:57:11 INFO Test execution started: test
80 | 12:57:11 INFO Browser: chrome
81 | 12:57:11 INFO Navigate to: 'https://en.wikipedia.org'
82 | --Return--
83 | > c:\[...]\golem\actions.py(1578)set_trace()->None
84 | -> pdb.set_trace()
85 | (Pdb)
86 | ```
--------------------------------------------------------------------------------
/docs/source/pages.md:
--------------------------------------------------------------------------------
1 | Pages
2 | ==================================================
3 |
4 | A page in Golem is a module that can be imported into a test.
5 | It can be used as a Page Object.
6 |
7 | ## Page Elements
8 |
9 | A way to store element selectors in a page is by using a tuple.
10 | This is the default behavior for the Web Module.
11 |
12 | ```python
13 | input = ('id', 'myID', 'My Input')
14 | button = ('css', 'button.btn-default', 'My Button')
15 | ```
16 |
17 | The third value in the tuple is optional and it is used as a friendly name by the execution report.
18 |
19 | ## Page Functions
20 |
21 | A page can have functions and these will be available from the test after importing the page into it.
22 | These functions will also be available when using the Web Module as regular actions do.
23 |
24 | **Example 1:**
25 |
26 | page1.py
27 | ```python
28 | from golem.browser import element
29 |
30 | title = ('css', 'h1')
31 |
32 | def assert_title(expected_title):
33 | assert element(title).text == expected_title
34 | ```
35 |
36 | test1.py
37 | ```python
38 | pages = ['page1']
39 |
40 | def test(data):
41 | navigate('http://...')
42 | page1.assert_title('My Expected Title')
43 | ```
44 |
45 | **Example 2:**
46 |
47 | page2.py
48 | ```python
49 | from golem.browser import elements
50 |
51 | table_rows = ('css', 'table > tbody > tr')
52 |
53 | def assert_row_amount(expected_amount):
54 | rows = elements(table_rows)
55 | assert len(rows) == expected_amount, 'Incorrect amount of rows'
56 | ```
57 |
58 | test2.py
59 | ```python
60 | pages = ['page2']
61 |
62 | def test(data):
63 | navigate('http://...')
64 | page2.assert_row_amount(5)
65 | ```
--------------------------------------------------------------------------------
/docs/source/report.md:
--------------------------------------------------------------------------------
1 | Report
2 | ==================================================
3 |
4 | ## Default Report
5 |
6 | When an execution is run a JSON report is generated in this location:
7 |
8 | ```
9 | /projects//reports///report.json
10 | ```
11 |
12 | ## Generate Reports After Execution
13 |
14 | These are the available report types:
15 |
16 | * html (single html file, screenshots included)
17 | * html-no-images (single html file, without screenshots)
18 | * json
19 | * junit (XML compatible with Jenkins)
20 |
21 | Example:
22 | ```
23 | golem run project suite -r junit html html-no-images json
24 | ```
25 |
26 | ### Report Location
27 |
28 | The location of the reports can be specified with the --report-folder argument:
29 |
30 | ```
31 | golem run project suite -r html --report-folder /the/path/to/the/report
32 | ```
33 |
34 | ### Report Name
35 |
36 | By default, the report name is 'report' ('report.xml', 'report.html', 'report-no-images.html' and 'report.json')
37 |
38 | The name of the reports can be modified with the --report-name argument:
39 |
40 | ```
41 | golem run project suite -r html --report-name report_name
42 | ```
43 |
44 | ## Modify Screenshot Format, Size, and Quality
45 |
46 | The size and compression of the screenshots can be modified to reduce the size on disk.
47 |
48 | For example:
49 |
50 | Given the default settings (PNG image, no resize, no compression), a screenshot was ~**149kb**.
51 |
52 | When these settings were applied:
53 |
54 | ```JSON
55 | {
56 | "screenshots": {
57 | "format": "jpg",
58 | "quality": 50,
59 | "resize": 70
60 | }
61 | }
62 | ```
63 |
64 | Then the same screenshot takes ~**35kb**.
65 |
66 | Experiment to find optimum settings. More info on screenshot formatting [here](settings.html#screenshots).
67 |
--------------------------------------------------------------------------------
/docs/source/running-tests.md:
--------------------------------------------------------------------------------
1 | Running tests
2 | ==================================================
3 |
4 | ## Run a single test
5 |
6 | A test file can be run using the file path or the test module path.
7 | In both cases it should be relative to the *tests* folder.
8 | ```
9 | golem run project_name test_name
10 | golem run project_name test_name.py
11 | golem run project_name folder.test_name
12 | golem run project_name folder/test_name.py
13 | ```
14 |
15 | ## Run a suite
16 |
17 | Similar to a test, a suite can be run using the file path or the test module path.
18 | In both cases it should be relative to the *suites* folder.
19 |
20 | ```
21 | golem run project_name suite_name
22 | golem run project_name suite_name.py
23 | golem run project_name folder.suite_name
24 | golem run project_name folder/suite_name.py
25 | ```
26 |
27 | ## Run every test in a directory
28 |
29 | To select all the tests in a directory and subdirectories a relative path can be supplied.
30 | The path has to be relative to the *tests* folder.
31 |
32 | ```
33 | golem run project_name folder/
34 | ```
35 |
36 | ### Run every test in a project
37 |
38 | ```
39 | golem run project_name .
40 | ```
41 |
42 | ## Select Browsers
43 |
44 | ```
45 | golem run project suite -b chrome firefox
46 | ```
47 |
48 | Every selected test will be run for each of the selected browsers.
49 | The browsers can also be defined inside a suite.
50 | If no browser is set, the default defined in settings will be used.
51 | The valid options for browsers are listed [here](browsers.html#valid-options).
52 |
53 | ## Run Tests in Parallel
54 |
55 | To run test files in parallel the number of processes must be set to more than 1.
56 | This can be done through the *golem run* command or by the *processes* attribute of a suite.
57 |
58 | ```
59 | golem run project suite_name -p 3
60 | ```
61 |
62 | ## Select Environments
63 |
64 | Select which [environments](test-data.html#environments) to use for a test execution:
65 |
66 | ```
67 | golem run project suite -e internal staging
68 | ```
69 |
70 | ## Filter Tests by Tags
71 |
72 | The selected tests for an execution can be filtered by tags.
73 |
74 | ```
75 | golem run project suite -t smoke "release 42.11.01"
76 | ```
77 |
78 | Multiple tags are always used with *and* operator.
79 | To use a combination of *and*, *or*, and *not*, a tag expression must be used:
80 |
81 | ```
82 | golem run project suite -t "smoke and (regression or not 'release 001')"
83 | ```
84 |
--------------------------------------------------------------------------------
/docs/source/settings.md:
--------------------------------------------------------------------------------
1 | Settings
2 | ==================================================
3 |
4 | Settings are defined in the settings.json file. They modify certain Golem behaviors.
5 | There is a global settings.json file and a project settings.json file.
6 | Note: project settings override global settings.
7 |
8 |
9 | ## Setting List
10 |
11 |
12 | ### search_timeout
13 |
14 | Default time to wait looking for an element until it is present. Default is 20 seconds.
15 |
16 | ### wait_displayed
17 |
18 | Wait for elements to be present and displayed. Default is False.
19 |
20 | ### screenshot_on_error
21 |
22 | Take a screenshot on error by default. Default is True.
23 |
24 | ### screenshot_on_step
25 |
26 | Take a screenshot on every step. Default is False.
27 |
28 | ### screenshot_on_end
29 |
30 | Take a screenshot after 'test' function ends. Default is False.
31 |
32 | ### highlight_elements
33 |
34 | Highlight elements on the screen when found. Default is False
35 |
36 | ### wait_hook
37 |
38 | Custom wait method to use for every action, that can be specific to each application. It must be defined inside extend.py
39 |
40 | ### default_browser
41 |
42 | Define the driver to use unless overriden by the -b/--browsers flag. Default is 'chrome'. The valid options are listed [here](browsers.html#valid-options).
43 |
44 | ### chromedriver_path
45 |
46 | Path to the Chrome driver executable.
47 |
48 | ### edgedriver_path
49 |
50 | Path to the Edge driver executable.
51 |
52 | ### geckodriver_path
53 |
54 | Path to the Gecko driver executable.
55 |
56 | ### iedriver_path
57 |
58 | Path to the Internet Explorer driver driver executable.
59 |
60 | ### operadriver_path
61 |
62 | Path to the Opera driver executable.
63 |
64 | ### opera_binary_path
65 |
66 | The path to the Opera binary file. Used to fix "Error: cannot find Opera binary" error.
67 |
68 | ### remote_url
69 |
70 | The URL to use when connecting to a remote webdriver, for example, when using selenium grid. Default is 'http://localhost:4444/wd/hub'
71 |
72 | ### remote_browsers
73 |
74 | Defines a list of remote browsers with its capabilities, required to run tests with Selenium Grid or another remote device provider.
75 | The minimum capabilities required are 'browserName', 'version' and 'platform', read [this](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities) for more info.
76 |
77 | Example: settings.json
78 | ```
79 | {
80 |
81 | "remote_browsers": {
82 | "chrome_60_mac": {
83 | "browserName": "chrome",
84 | "version": "60.0",
85 | "platform": "macOS 10.12"
86 | },
87 | "firefox_56_windows": {
88 | "browserName": "firefox",
89 | "version": "56.0",
90 | "platform": "Windows 10"
91 | }
92 | }
93 |
94 | }
95 | ```
96 |
97 |
98 | ### implicit_actions_import
99 | Import golem.actions module implicitly to the tests.
100 | Modifies test saving behavior when using the GUI test builder.
101 | Default is true.
102 |
103 | ### implicit_page_import
104 | Import pages implicitly to the test from a list of strings.
105 | When true, imported pages are saved as a list of strings. When false, import statements are used instead.
106 | Default is true.
107 |
108 | ### screenshots
109 |
110 | Modify screenshot format, size, and quality before saving to disk.
111 |
112 | Requires Pillow to be installed. It must be installed separately: ```pip install pillow```
113 |
114 | It should be an object with the following attributes: format, quality, width, height, and resize
115 |
116 | * **format**: "jpg" or "png". The default is "png".
117 | * **quality**: an int in 1..95 range. The default is 75. This only applies to "jpg" files.
118 | * **width**: defines the width of screenshots. If "height" is not set, this will maintain the aspect ratio.
119 | * **height**: defines the height of screenshots. If "width" is not set, this will maintain the aspect ratio.
120 | * **resize**: the percentage to resize screenshots. Must be int or string in the format "55" or "55%". To resize by percentage do not set width or height.
121 |
122 | Example:
123 | ```JSON
124 | {
125 | "screenshots": {
126 | "format": "jpg",
127 | "quality": 50,
128 | "resize": 70
129 | }
130 | }
131 | ```
132 |
133 | ### cli_log_level
134 |
135 | command line log level.
136 | Options are: DEBUG, INFO, WARNING, ERROR, CRITICAL. Default is INFO.
137 |
138 | ### log_all_events
139 |
140 | Log all events or just Golem events. Default is true.
141 |
142 | ### start_maximized
143 |
144 | Start the browser maximized. Default is true.
--------------------------------------------------------------------------------
/docs/source/suites.md:
--------------------------------------------------------------------------------
1 | Suites
2 | ==================================================
3 |
4 | A suite can execute a set of tests with the specified configuration.
5 | A suite contains a list of *tests*, *browsers*, and *environments* and the number of *processes*, and *tags*.
6 | Consider the following example:
7 |
8 | **full_regression.py**
9 | ```python
10 |
11 | browsers = ['firefox', 'chrome']
12 |
13 | environments = []
14 |
15 | processes = 2
16 |
17 | tags = []
18 |
19 | tests = [
20 | 'test1',
21 | 'test2',
22 | 'some_folder.test3',
23 | ]
24 |
25 | ```
26 |
27 |
28 |
29 |
30 |
Note
31 |
This suite will execute all marked tests, once per each browser, environment and test set
32 |
33 |
34 |
35 | ### Test Parallelization
36 |
37 | The 'processes = 2' tells Golem how many tests should be executed at the same time. The default is one (one at a time). How many tests can be parallelized depends on your test infrastructure.
38 |
39 |
40 | ### Environments
41 |
42 | Environments are defined in the environments.json file inside a project. See [Environments](test-data.html#environments).
43 |
--------------------------------------------------------------------------------
/docs/source/tests.md:
--------------------------------------------------------------------------------
1 | Tests
2 | ==================================================
3 |
4 | Tests are functions that begin with 'test' and are located in Python modules in the test folder of a project.
5 |
6 | To create a test first start a Golem test directory, if you don't already have one, and add a project to it:
7 |
8 | ```
9 | golem-admin createdirectory
10 | cd
11 | golem createproject
12 | ```
13 |
14 | Then add a test file inside that project:
15 |
16 | ```
17 | golem createtest
18 | ```
19 |
20 | A project and a test can also be created using the Web Module:
21 |
22 | ```
23 | golem gui
24 | ```
25 |
26 | ## Test Structure
27 |
28 | ```python
29 |
30 | description = ''
31 |
32 | tags = []
33 |
34 | pages = []
35 |
36 | skip = False
37 |
38 |
39 | def setup(data):
40 | pass
41 |
42 |
43 | def test_one(data):
44 | pass
45 |
46 |
47 | def test_two(data):
48 | pass
49 |
50 |
51 | def teardown(data):
52 | pass
53 | ```
54 |
55 |
56 | A test file must implement at least one **test** function that receives a data object as argument.
57 |
58 | ## Multiple Tests per File
59 |
60 | All test functions inside a test file are run in sequence. The data is shared between tests.
61 | The browser session is shared as well, unless a test explicitly closes the current open browser.
62 |
63 | ## Test Data
64 |
65 | Test data can be defined inside the file or in a separate CSV file.
66 | For detailed info about see: [Test Data](test-data.html)
67 |
68 | ### CSV Data
69 |
70 | It should be defined in a CSV file with the same name and in the same folder as the test.
71 |
72 | ### Infile Test Data
73 |
74 | A test can have data defined as a list of dictionaries.
75 |
76 | ```python
77 |
78 | data = [
79 | {
80 | 'url': 'http://url1.com',
81 | 'title': 'title1'
82 | },
83 | {
84 | 'url': 'http://url2.com',
85 | 'title': 'title2'
86 | }
87 | ]
88 |
89 | def test(data):
90 | navigate(data.url)
91 | assert_title(data.title)
92 | ```
93 |
94 | Note: when saving a test using the Test Module, if the *test_data* setting is not 'infile', any data stored in the test will be moved to a CSV file.
95 |
96 | ## Skip flag
97 |
98 | A flag variable to indicate that this test should be skipped.
99 | It should be a boolean or a string to use as skip message.
100 | Note: tests will only be skipped when running from a suite.
101 |
102 | ## Tags
103 |
104 | A list of tags (strings).
105 | Tags can be used to filter tests when running a suite.
106 | See [Filter Tests by Tags](running-tests.html#filter-tests-by-tags).
107 |
108 | ## Implicit vs Explicit Imports
109 |
110 | By default, the test runner imports the golem.actions module and any page module implicitly during the execution.
111 | Pages are saved as a list of strings.
112 | The GUI test builder complies with this format and generates code like the following:
113 |
114 | ```python
115 | pages = ['page1']
116 |
117 |
118 | def test(data):
119 | navigate('https://...')
120 | page1.custom_funtion()
121 | ```
122 |
123 | This behaviour can be turned off by setting [implicit_actions_import](settings.html#implicit-actions-import) and [implicit_page_import](settings.html#implicit-page-import) to false.
124 |
125 | Then, the test structure will be:
126 |
127 | ```python
128 | from golem import actions
129 |
130 | from projects..pages import page1
131 |
132 |
133 | def test(data):
134 | actions.navigate('https://...')
135 | page1.custom_funtion()
136 | ```
137 |
138 |
139 | ### GUI Test Builder and Imports Statements
140 |
141 | The GUI test builder only supports import statements for the **golem.actions** module and any Python module
142 | inside the **pages** folder; and only when the implicit modes are turned off.
143 | Any other import statements will be discarded when saving a test from the GUI test builder.
--------------------------------------------------------------------------------
/docs/source/tutorial-part-1.md:
--------------------------------------------------------------------------------
1 | Tutorial - Part 1
2 | ==================================================
3 |
4 | Let's create the first test in Golem and learn the main features along the way.
5 | This tutorial assumes Golem is already installed. If not, head over to the [Installation](installation.html) section.
6 |
7 |
8 | ## Create a Test Directory
9 |
10 | A **Test Directory** needs to be created first. This directory contains the initial folder structure and some config files.
11 | To create a Test Directory, open a console wherever you want the new directory to be and execute this command:
12 |
13 | ```
14 | golem-admin createdirectory
15 | ```
16 |
17 | This will create a **testdir** folder that will be used for all subsequent projects.
18 |
19 |
20 | ## Download Webdriver
21 |
22 | Each browser requires its own Webdriver executable.
23 | Golem uses the [webdriver-manager](https://github.com/golemhq/webdriver-manager) tool to download these automatically.
24 |
25 | ```
26 | cd
27 | webdriver-manager update
28 | ```
29 |
30 | The Webdriver executables are downloaded into the *drivers* folder inside the Test Directory.
31 |
32 | The settings.json file contains a key for each browser that should point to the Webdriver file for that browser.
33 | For example:
34 |
35 | *settings.json*
36 | ```
37 | {
38 | "chromedriver_path": "./drivers/chromedriver*"
39 | }
40 | ```
41 |
42 | The '\*' wildcard at the end of the path is used to match the highest version available, in the case there's more than one present.
43 |
44 | This doesn't need to be modified unless the Webdriver files are located elsewhere.
45 |
46 |
47 | For more detail, check [this page](browsers.html#webdriver-manager).
48 |
49 |
50 | ## Start the Web Module
51 |
52 | To start the Golem Web Module, run the following command:
53 |
54 | ```
55 | golem gui
56 | ```
57 |
58 | The Web Module can be accessed at [http://localhost:5000/](http://localhost:5000/)
59 |
60 | The following superuser is available at the start: username: **admin** / password: **admin**
61 |
62 |
63 |
64 | ## Create a New Project
65 |
66 | A new **project** can be created by using the Web Module or by the following command:
67 |
68 | ```
69 | cd
70 | golem createproject
71 | ```
72 |
73 | A new project has the following structure:
74 | ```
75 | project_name/
76 | pages/
77 | reports/
78 | suites/
79 | tests/
80 | environments.json
81 | settings.json
82 | ```
83 |
84 |
85 | Once the test directory and the project are created you can proceed to [Tutorial - Part 2](tutorial-part-2.html)
--------------------------------------------------------------------------------
/docs/source/waiting-for-elements.md:
--------------------------------------------------------------------------------
1 | Waiting for Elements
2 | ==================================================
3 |
4 | ## Implicit Wait
5 |
6 | There is an implicit search timeout that is applied to every time a web element is searched.
7 | This search timeout is defined in the settings file by using the [search_timeout](settings.html#search-timeout) setting key.
8 |
9 | It can also be set by using the [set_search_timeout](golem-actions.html#set-search-timeout-timeout) and [get_search_timeout](golem-actions.html#get-search-timeout) actions.
10 |
11 | Note that this timeout only waits for the element to be present (to exist in the DOM). It does not wait for the element to be visible, clickable, enabled, etc.
12 |
13 |
14 | ## Explicit Wait
15 |
16 | There are a few ways to wait for an element to be present.
17 |
18 | Using the [wait_for_element_present](golem-actions.html#wait-for-element-present-element-timeout-30) action:
19 |
20 | ```python
21 | from golem import actions
22 |
23 | actions.wait_for_element_present('#button-id', 15)
24 | actions.click('#button-id')
25 | ```
26 |
27 | Using the *timeout* argument in the find methods:
28 |
29 | ```python
30 | from golem import actions
31 |
32 | button = actions.get_browser().find('#button-id', timeout=15)
33 | button.click()
34 | ```
35 |
36 | Using the wait_for_element_present method of the WebDriver class:
37 |
38 | ```python
39 | from golem import actions
40 |
41 | actions.get_browser().wait_for_element_present('#button-id', timeout=15)
42 | ```
43 |
44 | ## Wait for Element Displayed
45 |
46 | Very often an element needs to be displayed (visible) besides being present. Here are the ways to wait for an element to be visible.
47 |
48 | Using the [wait_displayed](settings.html#wait-displayed) setting. This is defined globally for every search.
49 |
50 | Using the [wait_for_element_displayed](golem-actions.html#wait-for-element-displayed-element-timeout-30) action.
51 |
52 | Using the *wait_displayed* argument in the find methods:
53 |
54 | ```python
55 | from golem import actions
56 |
57 | actions.get_browser().find('#button-id', 15, wait_displayed=True).click()
58 | ```
59 |
60 | Using the *wait_displayed* method of the WebElement class:
61 |
62 | ```python
63 | from golem import actions
64 |
65 | button = actions.get_browser().find('#button-id')
66 | button.wait_displayed().click()
67 | ```
--------------------------------------------------------------------------------
/golem/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.10.1"
2 |
--------------------------------------------------------------------------------
/golem/bin/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/bin/__init__.py
--------------------------------------------------------------------------------
/golem/bin/golem_admin.py:
--------------------------------------------------------------------------------
1 | """A CLI admin script used to generate the initial test directory that
2 | contains the projects and all the required fields for Golem to
3 | work.
4 | """
5 | from golem.cli import argument_parser, commands, messages
6 |
7 |
8 | def main():
9 | parser = argument_parser.get_admin_parser()
10 | args = parser.parse_args()
11 | if args.help:
12 | print(messages.ADMIN_USAGE_MSG)
13 | elif args.command:
14 | if args.command == 'createdirectory':
15 | commands.createdirectory_command(args.name, args.no_confirm)
16 | else:
17 | print(messages.ADMIN_USAGE_MSG)
18 |
--------------------------------------------------------------------------------
/golem/bin/golem_init.py:
--------------------------------------------------------------------------------
1 | """CLI script to start golem"""
2 | import os
3 | import sys
4 |
5 | from golem.main import execute_from_command_line
6 |
7 |
8 | def main():
9 | sys.dont_write_bytecode = True
10 | execute_from_command_line(os.getcwd())
11 |
--------------------------------------------------------------------------------
/golem/bin/golem_standalone.py:
--------------------------------------------------------------------------------
1 | """Golem standalone script
2 |
3 | Use PyInstaller to generate an executable:
4 |
5 | pyinstaller golem/bin/golem_standalone.py --distpath . --onefile -n golem
6 | --add-data "golem/gui/templates:golem/gui/templates"
7 | --add-data "golem/gui/static:golem/gui/static"
8 |
9 | Note: use `;` (semi-colon) instead of `:` (colon) in Windows
10 | """
11 | import os
12 | import sys
13 | from multiprocessing import freeze_support
14 |
15 | from golem.main import execute_from_command_line
16 | from golem.bin import golem_admin
17 | from webdriver_manager.main import main as webdriver_manager_main
18 | from golem.cli.messages import STANDALONE_USAGE
19 |
20 |
21 | if __name__ == '__main__':
22 | freeze_support()
23 | if len(sys.argv) > 1:
24 | if sys.argv[1] in ['golem-admin', 'admin']:
25 | del sys.argv[1]
26 | golem_admin.main()
27 | elif sys.argv[1] == 'webdriver-manager':
28 | del sys.argv[1]
29 | webdriver_manager_main()
30 | else:
31 | execute_from_command_line(os.getcwd())
32 | else:
33 | print(STANDALONE_USAGE)
34 |
--------------------------------------------------------------------------------
/golem/cli/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/cli/__init__.py
--------------------------------------------------------------------------------
/golem/cli/argument_parser.py:
--------------------------------------------------------------------------------
1 | import argparse
2 |
3 |
4 | def get_parser():
5 | parser = argparse.ArgumentParser(prog='golem', add_help=False)
6 | parser.add_argument('-h', '--help', nargs='?', const=True, default=False)
7 | parser.add_argument('--golem-dir', type=str)
8 | parser.add_argument('-v', '--version', action='store_true', default=False)
9 | subparsers = parser.add_subparsers(dest='command')
10 |
11 | # run
12 | report_choices = ['junit', 'html', 'html-no-images', 'json']
13 | parser_run = subparsers.add_parser('run', add_help=False)
14 | parser_run.add_argument('project', nargs='?', default='')
15 | parser_run.add_argument('test_query', nargs='?', default='')
16 | parser_run.add_argument('-b', '--browsers', nargs='*', default=[], type=str)
17 | parser_run.add_argument('-p', '--processes', nargs='?', default=1, type=int)
18 | parser_run.add_argument('-e', '--environments', nargs='+', default=[], type=str)
19 | parser_run.add_argument('--test-functions', nargs='*', default=[], type=str)
20 | parser_run.add_argument('-t', '--tags', nargs='*', default=[], type=str)
21 | parser_run.add_argument('-i', '--interactive', action='store_true', default=False)
22 | parser_run.add_argument('-r', '--report', nargs='+', choices=report_choices,
23 | default=[], type=str)
24 | parser_run.add_argument('--report-folder', nargs='?', type=str)
25 | parser_run.add_argument('--report-name', nargs='?', type=str)
26 | parser_run.add_argument('--timestamp', nargs='?', type=str)
27 | parser_run.add_argument('-l', '--cli-log-level')
28 | parser_run.add_argument('-h', '--help', action='store_true')
29 |
30 | # gui
31 | parser_gui = subparsers.add_parser('gui', add_help=False)
32 | parser_gui.add_argument('--host', action='store', nargs='?', default=None)
33 | parser_gui.add_argument('-p', '--port', action='store', nargs='?', default=5000, type=int)
34 | parser_gui.add_argument('-d', '--debug', action='store_true', default=False)
35 | parser_gui.add_argument('-h', '--help', action='store_true')
36 |
37 | # createproject
38 | parser_createproject = subparsers.add_parser('createproject', add_help=False)
39 | parser_createproject.add_argument('project')
40 | parser_createproject.add_argument('-h', '--help', action='store_true')
41 |
42 | # createtest
43 | parser_createtest = subparsers.add_parser('createtest', add_help=False)
44 | parser_createtest.add_argument('project')
45 | parser_createtest.add_argument('test')
46 | parser_createtest.add_argument('-h', '--help', action='store_true')
47 |
48 | # createsuite
49 | parser_createsuite = subparsers.add_parser('createsuite', add_help=False)
50 | parser_createsuite.add_argument('project')
51 | parser_createsuite.add_argument('suite')
52 | parser_createsuite.add_argument('-h', '--help', action='store_true')
53 |
54 | # createsuperuser
55 | subparsers.add_parser('createuser', add_help=False)
56 |
57 | # createsuperuser
58 | parser_createsuperuser = subparsers.add_parser('createsuperuser', add_help=False)
59 | parser_createsuperuser.add_argument('-u', '--username')
60 | parser_createsuperuser.add_argument('-e', '--email')
61 | parser_createsuperuser.add_argument('-p', '--password')
62 | parser_createsuperuser.add_argument('-n', '--noinput', action='store_true', default=False)
63 | parser_createsuperuser.add_argument('-h', '--help', action='store_true')
64 |
65 | return parser
66 |
67 |
68 | def get_admin_parser():
69 | parser = argparse.ArgumentParser(prog='golem-admin', add_help=False)
70 | parser.add_argument('-h', '--help', nargs='?', const=True, default=False)
71 | subparsers = parser.add_subparsers(dest='command')
72 |
73 | # createdirectory
74 | parser_createdirectory = subparsers.add_parser('createdirectory', add_help=False)
75 | parser_createdirectory.add_argument('name')
76 | parser_createdirectory.add_argument('-y', '--yes', action='store_true',
77 | default=False, dest='no_confirm')
78 | parser_createdirectory.add_argument('-h', '--help', action='store_true')
79 |
80 | return parser
81 |
--------------------------------------------------------------------------------
/golem/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/core/__init__.py
--------------------------------------------------------------------------------
/golem/core/environment_manager.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 |
4 | from golem.core import utils, session
5 |
6 |
7 | def get_envs(project):
8 | """get the list of envs defined in the environments.json file"""
9 | envs = []
10 | env_data = get_environment_data(project)
11 | if env_data:
12 | envs = list(env_data.keys())
13 | return envs
14 |
15 |
16 | def get_environment_data(project):
17 | """get the full env data defined in the environments.json file"""
18 | env_data = {}
19 | env_path = environments_file_path(project)
20 | if os.path.isfile(env_path):
21 | json_data = utils.load_json_from_file(env_path, ignore_failure=True, default={})
22 | if json_data:
23 | env_data = json_data
24 | return env_data
25 |
26 |
27 | def get_environments_as_string(project):
28 | """get the contents of environments.json file as string"""
29 | env_data = ''
30 | env_path = environments_file_path(project)
31 | if os.path.isfile(env_path):
32 | with open(env_path, encoding='utf-8') as f:
33 | env_data = f.read()
34 | return env_data
35 |
36 |
37 | def save_environments(project, env_data):
38 | """save environments.json file contents.
39 | env_data must be a valid json string.
40 | Returns a string with the error or empty string otherwise"""
41 | error = ''
42 | if len(env_data):
43 | try:
44 | json.loads(env_data)
45 | except json.JSONDecodeError:
46 | error = 'must be valid JSON'
47 | if not error:
48 | env_path = environments_file_path(project)
49 | with open(env_path, 'w', encoding='utf-8') as f:
50 | f.write(env_data)
51 | return error
52 |
53 |
54 | def environments_file_path(project):
55 | """Path to environments.json file"""
56 | return os.path.join(session.testdir, 'projects', project, 'environments.json')
57 |
--------------------------------------------------------------------------------
/golem/core/errors.py:
--------------------------------------------------------------------------------
1 |
2 | invalid_test_directory = ('Error: {} is not an valid Golem test directory; '
3 | '.golem file not found')
4 |
--------------------------------------------------------------------------------
/golem/core/exceptions.py:
--------------------------------------------------------------------------------
1 |
2 | class IncorrectSelectorType(Exception):
3 | pass
4 |
5 |
6 | class ElementNotFound(Exception):
7 | pass
8 |
9 |
10 | class TextNotPresent(Exception):
11 | pass
12 |
13 |
14 | class ElementNotDisplayed(Exception):
15 | pass
16 |
--------------------------------------------------------------------------------
/golem/core/parsing_utils.py:
--------------------------------------------------------------------------------
1 | import ast
2 |
3 |
4 | def ast_parse_file(filename):
5 | """Parse a Python file using ast.
6 | Returns a ast.Module node
7 | """
8 | with open(filename, "rt", encoding='utf-8') as file:
9 | return ast.parse(file.read(), filename=filename)
10 |
11 |
12 | def top_level_functions(ast_node):
13 | """Given an ast.Module node return the names of the top level functions"""
14 | return [f.name for f in ast_node.body if isinstance(f, ast.FunctionDef)]
15 |
16 |
17 | def top_level_assignments(ast_node):
18 | """Given an ast Module node,
19 | return the names of the top level assignments.
20 | e.g.: "foo = 2" -> returns ['foo']
21 | https://greentreesnakes.readthedocs.io/en/latest/nodes.html#Assign
22 | """
23 | assignments = []
24 | for v in ast_node.body:
25 | if type(v) == ast.Assign:
26 | if len(v.targets) == 1:
27 | assignments.append(v.targets[0].id)
28 | return assignments
29 |
--------------------------------------------------------------------------------
/golem/core/secrets_manager.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from golem.core import utils, session
4 |
5 |
6 | def get_secrets(project):
7 | secrets_data = {}
8 | secrets_json_path = os.path.join(session.testdir, 'projects', project, 'secrets.json')
9 | if os.path.exists(secrets_json_path):
10 | json_data = utils.load_json_from_file(secrets_json_path, ignore_failure=True, default={})
11 | if json_data:
12 | secrets_data = json_data
13 | return secrets_data
14 |
--------------------------------------------------------------------------------
/golem/core/session.py:
--------------------------------------------------------------------------------
1 | """Global values for current session"""
2 |
3 | testdir = None
4 |
5 | settings = {}
6 |
--------------------------------------------------------------------------------
/golem/core/test_directory.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import webdriver_manager
4 |
5 | from golem.core import file_manager, settings_manager, session
6 | from golem.gui.user_management import Users
7 |
8 |
9 | def create_test_directory(testdir):
10 | file_manager.create_directory(path_list=[testdir], add_init=True)
11 | file_manager.create_directory(path_list=[testdir, 'projects'], add_init=True)
12 | file_manager.create_directory(path_list=[testdir, 'drivers'], add_init=False)
13 | settings_manager.create_global_settings_file(testdir)
14 | create_testdir_golem_file(testdir)
15 | session.testdir = testdir
16 | Users.create_super_user('admin', 'admin')
17 |
18 |
19 | def create_testdir_golem_file(testdir):
20 | """Create .golem file"""
21 | golem_file = os.path.join(testdir, '.golem')
22 | with open(golem_file, 'w') as f:
23 | secret_key = os.urandom(24).hex()
24 | f.write('[gui]\n')
25 | f.write(f'secret_key = {secret_key}\n')
26 |
27 |
28 | def get_projects():
29 | path = os.path.join(session.testdir, 'projects')
30 | projects = next(os.walk(path))[1]
31 | projects = [x for x in projects if x != '__pycache__']
32 | return projects
33 |
34 |
35 | def project_exists(project):
36 | return project in get_projects()
37 |
38 |
39 | def is_valid_test_directory(testdir):
40 | """Verify `testdir` is a valid test directory path.
41 | It must contain a .golem file.
42 | """
43 | return os.path.isfile(os.path.join(testdir, '.golem'))
44 |
45 |
46 | def drivers_path():
47 | return os.path.join(session.testdir, 'drivers')
48 |
49 |
50 | # def get_driver_versions():
51 | # return webdriver_manager.versions(drivers_path())
52 |
53 |
54 | def get_driver_folder_files():
55 | """Get the list of files in testdir/drivers folder.
56 | Folders are ignored. TODO
57 | """
58 | files = []
59 | path = drivers_path()
60 | lst = os.listdir(path)
61 | for elem in lst:
62 | if os.path.isfile(os.path.join(path, elem)):
63 | files.append(elem)
64 | return files
65 |
66 |
67 | def delete_driver_file(filename):
68 | errors = []
69 | path = os.path.join(drivers_path(), filename)
70 | if not os.path.isfile(path):
71 | errors.append(f'File {filename} does not exist')
72 | else:
73 | try:
74 | os.remove(path)
75 | except Exception as e:
76 | errors.append(f'There was an error removing file {filename}')
77 | return errors
78 |
79 |
80 | def update_driver(driver_name):
81 | if driver_name not in ['chromedriver', 'geckodriver']:
82 | return f'{driver_name} is not a valid driver name'
83 | webdriver_manager.update(driver_name, drivers_path())
84 | return '' # TODO: webdriver-manager should return actual error messages
85 |
--------------------------------------------------------------------------------
/golem/execution.py:
--------------------------------------------------------------------------------
1 | """ Stored values specific to a single test execution. """
2 |
3 | test_file = None
4 | browser = None
5 | browser_definition = None
6 | browsers = {}
7 | data = None
8 | secrets = None
9 | description = None
10 | settings = None
11 | test_dirname = None
12 | test_path = None
13 | project_name = None
14 | project_path = None
15 | testdir = None
16 | execution_reportdir = None
17 | testfile_reportdir = None
18 | logger = None
19 | tags = []
20 | environment = None
21 |
22 | # values below correspond to the current running test function
23 | test_name = None
24 | steps = []
25 | errors = []
26 | test_reportdir = None
27 | timers = {}
28 |
--------------------------------------------------------------------------------
/golem/execution_runner/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/execution_runner/__init__.py
--------------------------------------------------------------------------------
/golem/execution_runner/interactive.py:
--------------------------------------------------------------------------------
1 | """Golem interactive mode."""
2 | from golem.core import utils, settings_manager, session
3 | from golem import execution
4 | from golem import actions
5 | from golem.gui import gui_utils
6 | from golem.execution_runner.execution_runner import define_browsers
7 | from golem.test_runner import test_logger
8 |
9 |
10 | def interactive(settings, cli_browsers):
11 | """Starts the Golem interactive shell."""
12 | browsers = utils.choose_browser_by_precedence(
13 | cli_browsers=cli_browsers, suite_browsers=[],
14 | settings_default_browser=settings['default_browser'])
15 | execution.browser_name = browsers[0]
16 | remote_browsers = settings_manager.get_remote_browsers(session.settings)
17 | default_browsers = gui_utils.get_supported_browsers_suggestions()
18 | browser_defs = define_browsers(browsers, remote_browsers, default_browsers, [])
19 | execution.testdir = session.testdir
20 | execution.browser_definition = browser_defs[0]
21 | execution.settings = settings
22 | execution.settings['interactive'] = True
23 | execution.logger = test_logger.get_logger(
24 | cli_log_level=execution.settings['cli_log_level'],
25 | log_all_events=execution.settings['log_all_events'])
26 | actions.interactive_mode()
27 |
--------------------------------------------------------------------------------
/golem/execution_runner/multiprocess_executor.py:
--------------------------------------------------------------------------------
1 | """The multiprocess_executor method runs all the test cases
2 | provided in parallel using multiprocessing.
3 | """
4 | from multiprocessing import Pool
5 | from multiprocessing.pool import ApplyResult
6 |
7 | from golem.core import session
8 | from golem.test_runner.test_runner import run_test
9 |
10 |
11 | def multiprocess_executor(project, execution_list, has_failed_tests, test_functions, processes=1,
12 | tags=None, is_suite=False):
13 | """Runs a list of tests in parallel using multiprocessing"""
14 | pool = Pool(processes=processes, maxtasksperchild=1)
15 | results = []
16 | for test in execution_list:
17 | args = (session.testdir,
18 | project,
19 | test.name,
20 | test.data_set,
21 | test.secrets,
22 | test.browser,
23 | test.env,
24 | session.settings,
25 | test.reportdir,
26 | test.set_name,
27 | test_functions,
28 | has_failed_tests,
29 | tags,
30 | is_suite)
31 | apply_async = pool.apply_async(run_test, args=args)
32 | results.append(apply_async)
33 | map(ApplyResult.wait, results)
34 | pool.close()
35 | pool.join()
36 |
--------------------------------------------------------------------------------
/golem/gui/__init__.py:
--------------------------------------------------------------------------------
1 | """The Golem GUI web application"""
2 | import os
3 | import sys
4 |
5 | from flask import Flask, g, render_template
6 | from flask_login import current_user, LoginManager
7 |
8 | import golem
9 | from . import gui_utils, user_management
10 | from golem.core import session, settings_manager, errors, test_directory
11 | from .api import api_bp
12 | from .web_app import webapp_bp
13 | from .report import report_bp
14 |
15 |
16 | def create_app():
17 | """Call this function to create a Golem GUI app object.
18 | If called externally (e.g.: from a WSGI server) the cwd
19 | should be a valid Golem test directory"""
20 | if not session.testdir:
21 | testdir = os.getcwd()
22 | if not test_directory.is_valid_test_directory(testdir):
23 | sys.exit(errors.invalid_test_directory.format(testdir))
24 | else:
25 | session.testdir = testdir
26 | if not session.settings:
27 | session.settings = settings_manager.get_global_settings()
28 | app = Flask(__name__)
29 | app.secret_key = gui_utils.get_secret_key()
30 | app.config['SESSION_TYPE'] = 'filesystem'
31 | app.config['GOLEM_VERSION'] = golem.__version__
32 | login_manager = LoginManager()
33 | login_manager.login_view = 'webapp.login'
34 | login_manager.init_app(app)
35 | app.register_blueprint(webapp_bp)
36 | app.register_blueprint(report_bp)
37 | app.register_blueprint(api_bp)
38 | app.jinja_env.globals['get_user_projects'] = gui_utils.ProjectsCache.get_user_projects
39 |
40 | @login_manager.user_loader
41 | def load_user(user_id):
42 | return user_management.Users.get_user_by_id(user_id)
43 |
44 | @app.before_request
45 | def before_request():
46 | g.user = current_user
47 |
48 | @app.errorhandler(404)
49 | def page_not_found(error):
50 | return render_template('404.html', message=error.description), 404
51 |
52 | return app
53 |
--------------------------------------------------------------------------------
/golem/gui/gui_start.py:
--------------------------------------------------------------------------------
1 | """A function to start the GUI application."""
2 | import sys
3 | import os
4 |
5 | from golem import gui
6 |
7 | from werkzeug import _reloader
8 |
9 |
10 | ORIGINAL_GET_ARGS = None
11 |
12 |
13 | def run_gui(host=None, port=5000, debug=False):
14 | # Patch Werkzeug._reloader._get_args_for_reloading()
15 | # The Flask development server reloader does not work when
16 | # started from the Golem standalone (PyInstaller) in Linux
17 | # TODO
18 | patch_werkzeug_get_args_for_reloading_wrapper()
19 | app = gui.create_app()
20 | app.run(host=host, port=port, debug=debug)
21 |
22 |
23 | def patch_werkzeug_get_args_for_reloading_wrapper():
24 | global ORIGINAL_GET_ARGS
25 | if ORIGINAL_GET_ARGS is None:
26 | ORIGINAL_GET_ARGS = _reloader._get_args_for_reloading
27 | _reloader._get_args_for_reloading = _get_args_for_reloading_wrapper
28 |
29 |
30 | def _get_args_for_reloading_wrapper():
31 | rv = ORIGINAL_GET_ARGS()
32 | __main__ = sys.modules["__main__"]
33 | py_script = rv[1]
34 | if __main__.__package__ is None:
35 | # Executed a file, like "python app.py".
36 | if os.name != 'nt' and os.path.isfile(py_script) and os.access(py_script, os.X_OK):
37 | # The file is marked as executable. Nix adds a wrapper that
38 | # shouldn't be called with the Python executable.
39 | rv.pop(0)
40 | return rv
41 |
--------------------------------------------------------------------------------
/golem/gui/static/css/code-editor-common.css:
--------------------------------------------------------------------------------
1 |
2 | #codeEditorContainer {
3 | border: 1px solid #e0e0e0;
4 | border-radius: 3px
5 | }
6 |
7 | .CodeMirror {
8 | font-size: 13px;
9 | height: auto;
10 | }
11 |
12 | .CodeMirror-scroll {
13 | min-height: 400px
14 | }
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/lato-bol-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/lato-bol-webfont.woff
--------------------------------------------------------------------------------
/golem/gui/static/css/fonts/lato-lig-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/css/fonts/lato-lig-webfont.woff
--------------------------------------------------------------------------------
/golem/gui/static/css/json_code_editor.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | .codeEditorContainer {
4 | margin-top: 15px;
5 | }
6 |
7 | .CodeMirror {
8 | font-size:13px;
9 | height: auto;
10 | border-radius: 3px;
11 | border: 1px solid #e0e0e0;
12 | }
13 |
14 | .CodeMirror-scroll {
15 | min-height: 150px
16 | }
--------------------------------------------------------------------------------
/golem/gui/static/css/list_common.css:
--------------------------------------------------------------------------------
1 | .box-container{
2 | margin-bottom: 15px;
3 | padding: 10px;
4 | border-radius: 3px
5 | }
6 |
7 | .folder-content {
8 | font-size: 16px;
9 | margin: 0;
10 | padding: 0;
11 | list-style: none
12 | }
13 |
14 | #breadcrumb {
15 | font-size: 16px;
16 | font-weight: 700;
17 | }
18 |
19 | #breadcrumb a {
20 | text-decoration: none;
21 | color: #369;
22 | }
23 |
24 | .folder-content .folder-content {
25 | margin-left: 1em;
26 | position: relative
27 | }
28 |
29 | .folder-content .folder-content:before {
30 | content: "";
31 | display: block;
32 | width: 0;
33 | position: absolute;
34 | top: 0;
35 | bottom: 0;
36 | left: 0;
37 | border-left: 1px solid;
38 | z-index: 10
39 | }
40 |
41 | .folder-content>.tree-element {
42 | padding: 0 1em;
43 | line-height: 2em;
44 | color: #369;
45 | font-weight: 700;
46 | position: relative;
47 | overflow: hidden;
48 | white-space: nowrap;
49 | }
50 |
51 | .folder-content .folder-content .tree-element:before {
52 | content:"";
53 | display: block;
54 | width: 10px;
55 | height: 0;
56 | border-top: 1px solid;
57 | margin-top: -1px;
58 | position: absolute;
59 | top: 1em;
60 | left: 0
61 | }
62 |
63 | .folder-content .folder-content .tree-element:last-child:before {
64 | background: #fff;
65 | height: auto;
66 | top: 1em;
67 | bottom: 0;
68 | z-index: 11
69 | }
70 |
71 | .folder-content .folder-content .tree-element.file:last-child:hover:before {
72 | background: #fafafa;
73 | border-left: 1px solid #fff
74 | }
75 |
76 | .folder-icon {
77 | margin-right:5px;
78 | }
79 |
80 | .tree-element a, #bottomMenu a {
81 | text-decoration: none;
82 | color: #369;
83 | }
84 |
85 | .tree-element a.file-link {
86 | max-width: calc(100% - 90px);
87 | overflow: hidden;
88 | float: left;
89 | text-overflow: ellipsis;
90 | }
91 |
92 | .folder-content li[type="test"] a.list-item-link {
93 | max-width: calc(100% - 105px);
94 | }
95 |
96 | .folder-content li button {
97 | text-decoration: none;
98 | color: #369;
99 | border: none;
100 | background: transparent;
101 | margin: 0px;
102 | padding: 0px;
103 | outline: 0;
104 | }
105 |
106 | .tree-element:not(.folder):hover {
107 | background-color: #fafafa;
108 | }
109 |
110 | .tree-element:not(.folder):hover > a.file-link {
111 | word-break: break-word;
112 | white-space: pre-wrap;
113 | }
114 |
115 | .new-element-input {
116 | max-width: 500px;
117 | font-size: 16px;
118 | padding-left: 2px
119 | }
120 |
121 | #rootFolderContent {
122 | min-height: 5px
123 | }
124 |
125 | .folder-menu-button-toggle:hover, .folder-menu-button-toggle:active, .folder-menu-button-toggle:focus {
126 | background-color: inherit !important;
127 | box-shadow: unset;
128 | outline: unset !important
129 | }
130 |
131 | .folder-menu.active {
132 | display: none
133 | }
134 |
135 | .folder-menu.active {
136 | display: block
137 | }
138 |
139 | #bottomMenu {
140 | padding-left: 1em
141 | }
142 |
143 | .file-menu {
144 | display: none;
145 | background-color: #fafafa;
146 | position: absolute;
147 | right: 0px;
148 | top: 0px;
149 | padding-left:5px;
150 | }
151 |
152 | li.tree-element:hover > .file-menu {
153 | display: block;
154 | }
155 |
156 | .file-menu .file-menu-button {
157 | padding-left: 5px
158 | }
--------------------------------------------------------------------------------
/golem/gui/static/css/page_object.css:
--------------------------------------------------------------------------------
1 |
2 | .element {
3 | margin-bottom: 10px;
4 | width: 100%;
5 | }
6 |
7 | .function {
8 | margin-bottom: 10px;
9 | }
10 |
11 | pre {
12 | margin-bottom: 0px;
13 | }
14 |
15 | .step-remove-icon {
16 | float: right;
17 | height: 34px;
18 | line-height: 34px;
19 | width: 16px;
20 | }
21 |
22 | .step-remove-icon a {
23 | color: black;
24 | }
--------------------------------------------------------------------------------
/golem/gui/static/css/suite.css:
--------------------------------------------------------------------------------
1 | .tree, .tree ul {
2 | margin:0;
3 | padding:0;
4 | list-style:none
5 | }
6 |
7 | .tree ul {
8 | margin-left:1em;
9 | position:relative
10 | }
11 |
12 | .tree ul ul {
13 | margin-left:.5em
14 | }
15 |
16 | .tree ul:before {
17 | content:"";
18 | display:block;
19 | width:0;
20 | position:absolute;
21 | top:0;
22 | bottom:0;
23 | left:0;
24 | border-left:1px solid
25 | }
26 |
27 | .tree li {
28 | margin:0;
29 | padding:0 1em;
30 | line-height:2em;
31 | color:#369;
32 | font-weight:700;
33 | position:relative
34 | }
35 |
36 | .tree ul li:before {
37 | content:"";
38 | display:block;
39 | width:10px;
40 | height:0;
41 | border-top:1px solid;
42 | margin-top:-1px;
43 | position:absolute;
44 | top:1em;
45 | left:0
46 | }
47 |
48 | .tree ul li:last-child:before {
49 | background:#fff;
50 | height:auto;
51 | top:1em;
52 | bottom:0
53 | }
54 |
55 | .indicator {
56 | margin-right: 4px;
57 | }
58 |
59 | .tree li {
60 | word-break: break-all
61 | }
62 |
63 | .tree li[data-type="test"] label {
64 | margin-bottom: 0px
65 | }
66 |
67 | .tree li a {
68 | text-decoration: none;
69 | color:#369;
70 | }
71 |
72 | .tree li button, .tree li button:active, .tree li button:focus {
73 | text-decoration: none;
74 | color:#369;
75 | border:none;
76 | background:transparent;
77 | margin:0px 0px 0px 0px;
78 | padding:0px 0px 0px 0px;
79 | outline: 0;
80 | }
81 |
82 | .select-testcase-checkbox {
83 | float: left;
84 | font-size: 20px;
85 | margin-top: 6px !important;
86 | margin-right: 5px !important;
87 | }
88 |
89 | .form-group{
90 | margin-bottom: 5px;
91 | }
92 |
--------------------------------------------------------------------------------
/golem/gui/static/css/test_case.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | overflow: scroll
4 | }
5 |
6 | body.modal-open {
7 | overflow: hidden
8 | }
9 |
10 |
11 | .input-group {
12 | margin-bottom: 5px;
13 | width: 100%;
14 | }
15 |
16 | #stepsContainerContainer {
17 | padding-bottom: 10px;
18 | }
19 |
20 | #setupSteps {
21 | margin-bottom: 20px;
22 | }
23 |
24 |
25 | #testFunctions {
26 | clear: both
27 | }
28 |
29 | .test-function {
30 | overflow: auto
31 | }
32 |
33 | .testFunctionNameContainer {
34 | clear: both
35 | }
36 |
37 | .testFunctionNameContainer h4 {
38 | display: inline-block;
39 | float: left;
40 | }
41 |
42 | .steps {
43 | overflow: auto;
44 | clear: both
45 | }
46 |
47 | .step-input-container {
48 | padding-left: 0px;
49 | padding-right: 10px;
50 | }
51 |
52 | .step-numbering {
53 | float: left;
54 | height: 34px;
55 | line-height: 34px;
56 | font-weight: bold;
57 | margin-right: 5px;
58 | width: 16px;
59 | cursor: move;
60 | cursor: -webkit-grabbing;
61 | }
62 |
63 | .step-remove-icon {
64 | float: right;
65 | height: 34px;
66 | line-height: 34px;
67 | font-weight: bold;
68 | width: 16px;
69 | }
70 |
71 | .step-remove-icon a{
72 | color: black;
73 | }
74 |
75 | #steps {
76 | margin-bottom: 5px;
77 | float: left;
78 | width: 100%
79 | }
80 |
81 | #pageObjectsContainerContainer {
82 | padding-bottom: 10px;
83 | }
84 |
85 | #descriptionContainer {
86 | padding-bottom: 10px;
87 | }
88 |
89 | .step {
90 | float: left;
91 | width: 100%;
92 | clear: both;
93 | margin-bottom: 5px;
94 | }
95 |
96 | .add-new-icon {
97 | width: 25px;
98 | vertical-align: inherit;
99 | }
100 |
101 | .input-middle-btn button{
102 | border-radius: 0px;
103 | border-left: 0px;
104 | border-right: 0px;
105 | }
106 |
107 | .params {
108 | float: left;
109 | padding-left: 0px;
110 | padding-right: 0px;
111 | width: calc(100% - 307px);
112 | }
113 |
114 | .parameter-container {
115 | width: 264px;
116 | float: left;
117 | }
118 |
119 | .step-first-input, .parameter-input {
120 | padding: 6px
121 | }
122 |
123 | .step-first-input-container {
124 | float: left;
125 | width: 264px;
126 | }
127 |
128 | @media (min-width: 1400px) {
129 |
130 | .step-first-input-container {
131 | width: 296px;
132 | }
133 |
134 | .parameter-container {
135 | width: 296px;
136 | }
137 |
138 | .params {
139 | width: calc(100% - 335px);
140 | }
141 | }
142 |
143 | #pageObjects>.page>.page-name{
144 | cursor: default;
145 | }
146 |
147 | #pageModal.modal .modal-dialog{
148 | width: auto;
149 | margin: 10px;
150 | height: 90%;
151 | }
152 |
153 | #pageModal.modal .modal-dialog .modal-content{
154 | height: 100%;
155 | }
156 |
157 | #pageModal.modal .modal-dialog .modal-content .modal-body{
158 | width: 100%;
159 | height: calc(100% - 41px);
160 | padding: 0px;
161 | }
162 |
163 | #pageModal #pageModalIframe {
164 | border: none;
165 | width: 100%;
166 | height: 100%;
167 | border-bottom-left-radius: 6px;
168 | border-bottom-right-radius: 6px;
169 | }
170 |
171 | @media (min-width: 900px){
172 | #pageModal.modal .modal-dialog{
173 | width: 85%;
174 | margin: 30px auto;
175 | }
176 | }
177 |
178 | .not-exist {
179 | box-shadow: inset 0px 0px 4px 1px #d9534f;
180 | }
181 |
182 | .action-description {
183 | display: none;
184 | position: absolute;
185 | left: 100%;
186 | margin-top: -24px;
187 | margin-left: 3px;
188 | border-radius: 3px;
189 | width: 300px;
190 | background: white;
191 | min-height: 50px;
192 | padding: 10px;
193 | border: 1px solid #ccc;
194 | }
195 |
196 | .autocomplete-selected .action-description {
197 | display: block
198 | }
199 |
200 | .code-block {
201 | border: 1px solid #ccc;
202 | display: inline-block;
203 | width: calc(100% - 50px);
204 | border-radius: 3px;
205 | }
206 |
207 | .code-block .CodeMirror {
208 | height: auto;
209 | border-radius: 3px;
210 | }
211 |
212 | .code-block .CodeMirror-scroll {
213 | min-height: 50px
214 | }
215 |
216 | .CodeMirror-lines {
217 | padding: 6px 0; /* Vertical padding around content */
218 | }
219 | .CodeMirror pre {
220 | padding: 0 6px; /* Horizontal padding of content */
221 | }
222 |
223 | .step[step-type='code-block'] {
224 | margin-bottom: 1px;
225 | }
--------------------------------------------------------------------------------
/golem/gui/static/css/test_case_code.css:
--------------------------------------------------------------------------------
1 |
2 | #codeEditorContainer {
3 | border-bottom-left-radius: 0px;
4 | border-bottom-right-radius: 0px
5 | }
6 |
7 | #dataContainerContainer {
8 | border-left: 1px solid #e0e0e0;
9 | border-right: 1px solid #e0e0e0;
10 | border-bottom: 1px solid #e0e0e0;
11 | border-bottom-left-radius: 3px;
12 | border-bottom-right-radius: 3px
13 | }
14 |
15 | .light-gray-block {
16 | background-color: #F8F8F8;
17 | }
18 |
19 | .light-gray-block:hover {
20 | background-color: white;
21 | }
22 |
23 | .input-group {
24 | margin-bottom: 5px;
25 | width: 100%;
26 | }
27 |
28 | .add-new-icon {
29 | width: 25px;
30 | vertical-align: inherit;
31 | }
32 |
33 | .input-middle-btn button{
34 | border-radius: 0px;
35 | border-left: 0px;
36 | border-right: 0px;
37 | }
--------------------------------------------------------------------------------
/golem/gui/static/css/test_case_common.css:
--------------------------------------------------------------------------------
1 |
2 | #testNameInput {
3 | display: inline;
4 | }
5 |
6 | #testNameInput input {
7 | width: calc(100% - 20px);
8 | }
9 |
10 | .inline-remove-icon {
11 | display: inline-block;
12 | margin-top: 10px;
13 | margin-left: 5px;
14 | }
15 |
16 | .inline-remove-icon a {
17 | color: black;
18 | }
19 |
20 | /* data table */
21 |
22 | #dataContainerContainer {
23 | padding-bottom: 10px;
24 | }
25 |
26 | #dataTableContainer {
27 | display: table-cell;
28 | width: 100%;
29 | position: relative;
30 | }
31 |
32 | #dataTable {
33 | background-color: white;
34 | margin-bottom: 5px;
35 | }
36 |
37 | #dataTable td, #dataTable th {
38 | padding: 0px;
39 | }
40 |
41 | #dataTable th.index {
42 | width: 30px;
43 | text-align: center;
44 | }
45 |
46 | #dataTable .input-group {
47 | margin-bottom: 0px;
48 | }
49 |
50 | #dataTable input {
51 | border: 0px;
52 | padding: 4px 4px;
53 | height: 25px;
54 | }
55 |
56 |
57 | /* CODE EDITOR CODEMIRROR */
58 |
59 | .CodeMirror {
60 | font-size:13px;
61 | height: auto;
62 | border-radius: 3px;
63 | border: 1px solid #e0e0e0;
64 | }
65 |
66 | .CodeMirror-scroll {
67 | min-height: 150px
68 | }
--------------------------------------------------------------------------------
/golem/gui/static/img/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/img/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/golem/gui/static/img/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/img/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/golem/gui/static/img/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/img/icons/favicon.ico
--------------------------------------------------------------------------------
/golem/gui/static/img/plus_sign.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/img/plus_sign.png
--------------------------------------------------------------------------------
/golem/gui/static/img/plus_sign2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/golemhq/golem/84f51478b169cdeab73fc7e2a22a64d0a2a29263/golem/gui/static/img/plus_sign2.png
--------------------------------------------------------------------------------
/golem/gui/static/js/drivers.js:
--------------------------------------------------------------------------------
1 |
2 | $(document).ready(function() {
3 | Drivers.getDriverFiles()
4 | });
5 |
6 |
7 | const Drivers = new function(){
8 |
9 | this.getDriverFiles = function(){
10 | xhr.get('/api/drivers/files', {}, driverFiles => {
11 | if(driverFiles.length) {
12 | driverFiles.forEach(filename => {
13 | let li = $(`
14 |