diff --git a/.gitignore b/.gitignore index ac94b58c8c..b5a20a735e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ openstack_dashboard/local/* openstack_dashboard/local/local_settings.d/* !openstack_dashboard/local/local_settings.d/*.example openstack_dashboard/test/.secret_key_store +openstack_dashboard/test/integration_tests/horizon.conf.sample openstack_dashboard/test/integration_tests/local-horizon.conf openstack_dashboard/test/integration_tests/test_reports/ openstack_dashboard/wsgi/horizon.wsgi diff --git a/openstack_dashboard/test/integration_tests/README.rst b/openstack_dashboard/test/integration_tests/README.rst index ce3af80044..33968c7f12 100644 --- a/openstack_dashboard/test/integration_tests/README.rst +++ b/openstack_dashboard/test/integration_tests/README.rst @@ -8,8 +8,16 @@ Running the integration tests #. Set up an OpenStack server -#. Update the configuration file at `horizon.conf` or add overrides - to that file in `local-horizon.conf` which is ignored by git. +#. Prepare the configuration file at `local-horizon.conf` if you need + to change the default configurations. + Note that `horizon.conf` can be used for the same purpose too + from the historical reason. + + You can generate a sample configuration file by the following command:: + + $ oslo-config-generator \ + --namespace openstack_dashboard_integration_tests + --output-file openstack_dashboard/test/integration_tests/horizon.conf.sample #. Run the tests. :: diff --git a/openstack_dashboard/test/integration_tests/config.py b/openstack_dashboard/test/integration_tests/config.py index 379b8fc34d..ee6eb0b413 100644 --- a/openstack_dashboard/test/integration_tests/config.py +++ b/openstack_dashboard/test/integration_tests/config.py @@ -18,45 +18,44 @@ from oslo_config import cfg DashboardGroup = [ cfg.StrOpt('dashboard_url', default='http://localhost/dashboard/', - help="Where the dashboard can be found"), + help='Where the dashboard can be found'), cfg.StrOpt('help_url', default='https://docs.openstack.org/', - help="Dashboard help page url"), + help='Dashboard help page url'), ] IdentityGroup = [ cfg.StrOpt('username', default='demo', - help="Username to use for non-admin API requests."), + help='Username to use for non-admin API requests.'), cfg.StrOpt('password', default='secretadmin', - help="API key to use when authenticating.", + help='API key to use when authenticating.', secret=True), cfg.StrOpt('domain', default=None, - help="Domain name to use if required for login"), + help='Domain name to use if required for login'), cfg.StrOpt('home_project', default='demo', - help="Project to keep all objects belonging to a regular user." + help='Project to keep all objects belonging to a regular user.' ), cfg.StrOpt('admin_username', default='admin', - help="Administrative Username to use for admin API " - "requests."), + help='Administrative Username to use for admin API requests.'), cfg.StrOpt('admin_password', default='secretadmin', - help="API key to use when authenticating as admin.", + help='API key to use when authenticating as admin.', secret=True), cfg.StrOpt('admin_home_project', default='admin', - help="Project to keep all objects belonging to an admin user."), + help='Project to keep all objects belonging to an admin user.'), cfg.StrOpt('default_keystone_role', default='member', - help="Name of default role every user gets in his new project"), + help='Name of default role every user gets in his new project.'), cfg.StrOpt('default_keystone_admin_role', default='admin', - help="Name of the role that grants admin rights to a user in " - "his project"), + help=('Name of the role that grants admin rights to a user in ' + 'his project')), cfg.IntOpt('unique_last_password_count', # The default value is chosen to match the value of # [security_compliance] unique_last_password_count in DevStack @@ -65,10 +64,10 @@ IdentityGroup = [ # in keystone may differ, so you might need # to change this parameter. default=2, - help=("The number of passwords for a user that must be unique " - "before an old password can be used. " - "This should match the keystone configuration option " - "'[security_compliance] unique_last_password_count'.")), + help=('The number of passwords for a user that must be unique ' + 'before an old password can be used. ' + 'This should match the keystone configuration option ' + '"[security_compliance] unique_last_password_count".')), ] ImageGroup = [ @@ -98,28 +97,38 @@ NetworkGroup = [ AvailableServiceGroup = [ cfg.BoolOpt('neutron', - default=True), + default=True, + help='Whether neutron is expected to be available'), ] SeleniumGroup = [ - cfg.FloatOpt('message_implicit_wait', - default=0.1, - help="Time to wait for confirmation modal in seconds"), - cfg.IntOpt('implicit_wait', - default=10, - help="Implicit wait timeout in seconds"), - cfg.IntOpt('explicit_wait', - default=90, - help="Explicit wait timeout in seconds"), - cfg.IntOpt('page_timeout', - default=60, - help="Page load timeout in seconds"), - cfg.StrOpt('screenshots_directory', - default="integration_tests_screenshots", - help="Output screenshot directory"), - cfg.BoolOpt('maximize_browser', - default=True, - help="Is the browser size maximized for each test?"), + cfg.FloatOpt( + 'message_implicit_wait', + default=0.1, + help='Timeout in seconds to wait for message confirmation modal'), + cfg.IntOpt( + 'implicit_wait', + default=10, + help=('Implicit timeout to wait until element become available, ' + 'It is used for every find_element, find_elements call.')), + cfg.IntOpt( + 'explicit_wait', + default=90, + help=('Explicit timeout is used for long lasting operations, ' + 'Methods using explicit timeout are usually prefixed with ' + '"wait"')), + cfg.IntOpt( + 'page_timeout', + default=60, + help='Timeout in seconds to wait for a page to become available'), + cfg.StrOpt( + 'screenshots_directory', + default='integration_tests_screenshots', + help='Output directory for screenshots'), + cfg.BoolOpt( + 'maximize_browser', + default=True, + help='Maximize the browser window at the start of each test or not'), ] FlavorsGroup = [ @@ -137,13 +146,13 @@ ScenarioGroup = [ InstancesGroup = [ cfg.StrOpt('available_zone', default='nova', - help="Zone to be selected for launch Instances"), + help='Availability zone to be selected for launch instances'), cfg.StrOpt('image_name', default='cirros-0.4.0-x86_64-disk (12.1 MB)', - help="Boot Source to be selected for launch Instances"), + help='Boot Source to be selected for launch Instances'), cfg.StrOpt('flavor', default='m1.tiny', - help="Flavor to be selected for launch Instances"), + help='Flavor to be selected for launch instances'), ] VolumeGroup = [ @@ -156,14 +165,18 @@ VolumeGroup = [ ] PluginGroup = [ - cfg.BoolOpt('is_plugin', - default='False', - help="Set to true if this is a plugin"), - cfg.MultiStrOpt('plugin_page_path', - default='', - help='Additional path to look for plugin page content'), - cfg.MultiStrOpt('plugin_page_structure', - default='') + cfg.BoolOpt( + 'is_plugin', + default='False', + help='Set to true if this is a plugin'), + cfg.MultiStrOpt( + 'plugin_page_path', + default='', + help='Additional path to look for plugin page content'), + cfg.MultiStrOpt( + 'plugin_page_structure', + default='', + help=('JSON string to define the page structure for the plugin')), ] @@ -172,13 +185,11 @@ def _get_config_files(): os.path.abspath(os.path.dirname(os.path.dirname(__file__))), 'integration_tests') conf_file = os.environ.get('HORIZON_INTEGRATION_TESTS_CONFIG_FILE', - "%s/horizon.conf" % conf_dir) - config_files = [conf_file] + '%s/horizon.conf' % conf_dir) local_config = os.environ.get('HORIZON_INTEGRATION_TESTS_LOCAL_CONFIG', - "%s/local-horizon.conf" % conf_dir) - if os.path.isfile(local_config): - config_files.append(local_config) - return config_files + '%s/local-horizon.conf' % conf_dir) + config_files = [conf_file, local_config] + return [f for f in config_files if os.path.isfile(f)] def get_config(): @@ -197,3 +208,19 @@ def get_config(): cfg.CONF.register_opts(VolumeGroup, group="volume") return cfg.CONF + + +def list_opts(): + return [ + ("dashboard", DashboardGroup), + ("selenium", SeleniumGroup), + ("flavors", FlavorsGroup), + ("image", ImageGroup), + ("identity", IdentityGroup), + ("network", NetworkGroup), + ("service_available", AvailableServiceGroup), + ("scenario", ScenarioGroup), + ("launch_instances", InstancesGroup), + ("plugin", PluginGroup), + ("volume", VolumeGroup), + ] diff --git a/setup.cfg b/setup.cfg index 0ed816195f..003ea83237 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,6 +54,7 @@ add_comments = Translators: [entry_points] oslo.config.opts = openstack_dashboard = openstack_dashboard.utils.config:list_options + openstack_dashboard_integration_tests = openstack_dashboard.test.integration_tests.config:list_opts # We use a custom extractor to find translatable strings in AngularJS templates. # See http://babel.pocoo.org/docs/messages/#referencing-extraction-methods for # details on how this works. diff --git a/tox.ini b/tox.ini index 4bf1a423c3..7e67d53c6a 100644 --- a/tox.ini +++ b/tox.ini @@ -88,7 +88,9 @@ setenv = PYTHONHASHSEED=0 INTEGRATION_TESTS=1 SELENIUM_HEADLESS=1 -commands = {envpython} {toxinidir}/manage.py test openstack_dashboard --settings=openstack_dashboard.test.settings --verbosity 2 --tag integration {posargs} +commands = + oslo-config-generator --namespace openstack_dashboard_integration_tests + {envpython} {toxinidir}/manage.py test openstack_dashboard --settings=openstack_dashboard.test.settings --verbosity 2 --tag integration {posargs} [testenv:npm] passenv =