diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index c5a8900aa..7d82f92d1 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -10,6 +10,8 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni
=== Added
+- Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace Default and
+Force Tags settings, after Robot Framework 7.0
- Added content help pop-up on Text Editor by pressing ``Ctrl`` for text at cursor position or selected autocomplete list item
- Added Exclude option in context nenu for Test files, previously was only possible for Test Suites folders
- Added exclusion of monitoring filesystem changes for files and directories excluded in Preferences
diff --git a/src/robotide/application/CHANGELOG.html b/src/robotide/application/CHANGELOG.html
index df139036d..15661cb78 100644
--- a/src/robotide/application/CHANGELOG.html
+++ b/src/robotide/application/CHANGELOG.html
@@ -1,6 +1,8 @@
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog
-and this project adheres to Semantic Versioning.
-
+and this project adheres to Semantic Versioning.
+- Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace Default and Force Tags settings, after Robot Framework 7.0
+-
Added content help pop-up on Text Editor by pressing ``Ctrl`` for text at cursor position or selected autocomplete list item
-
Added Exclude option in context nenu for Test files, previously was only possible for Test Suites folders
diff --git a/src/robotide/application/releasenotes.py b/src/robotide/application/releasenotes.py
index 4c467fcc6..ece29db8d 100644
--- a/src/robotide/application/releasenotes.py
+++ b/src/robotide/application/releasenotes.py
@@ -168,6 +168,7 @@ def set_content(self, html_win, content):
New Features and Fixes Highlights
+- Added Test Tags field (new, since Robot Framework 6.0) to Test Suites settings. This field will replace Default and Force Tags settings, after Robot Framework 7.0
- Improved RIDE Log and Parser Log windows to allow Zoom In/Out with Ctrl-Mouse Wheel
- Hide continuation markers in Project Tree
- Improved content assistance in Text Editor by allowing to filter list as we type
diff --git a/src/robotide/controller/filecontrollers.py b/src/robotide/controller/filecontrollers.py
index 08b1179fa..5d5c5986a 100644
--- a/src/robotide/controller/filecontrollers.py
+++ b/src/robotide/controller/filecontrollers.py
@@ -29,7 +29,7 @@
from .basecontroller import WithUndoRedoStacks, _BaseController, WithNamespace, ControllerWithParent
from .robotdata import new_test_case_file, new_test_data_directory
from .settingcontrollers import (DocumentationController, FixtureController, TimeoutController, TemplateController,
- DefaultTagsController, ForceTagsController)
+ DefaultTagsController, ForceTagsController, TestTagsController)
from .tablecontrollers import (VariableTableController, TestCaseTableController, KeywordTableController,
ImportSettingsController, MetadataListController)
from .macrocontrollers import TestCaseController, UserKeywordController
@@ -161,7 +161,7 @@ def internal_settings(self):
FixtureController(self, ss.suite_teardown),
FixtureController(self, ss.test_setup),
FixtureController(self, ss.test_teardown),
- self.force_tags]
+ self.force_tags, self.test_tags]
@property
def setting_table(self):
@@ -171,6 +171,10 @@ def setting_table(self):
def force_tags(self):
return ForceTagsController(self, self.setting_table.force_tags)
+ @property
+ def test_tags(self):
+ return TestTagsController(self, self.setting_table.test_tags)
+
@property
def variables(self):
if self._variables_table_controller is None:
@@ -1015,7 +1019,7 @@ def internal_settings(self):
FixtureController(self, ss.suite_teardown),
FixtureController(self, ss.test_setup),
FixtureController(self, ss.test_teardown),
- self.force_tags]
+ self.force_tags, self.test_tags]
@property
def setting_table(self):
@@ -1025,6 +1029,10 @@ def setting_table(self):
def force_tags(self):
return ForceTagsController(self, self.setting_table.force_tags)
+ @property
+ def test_tags(self):
+ return TestTagsController(self, self.setting_table.test_tags)
+
@property
def dirty(self):
return False
@@ -1089,7 +1097,7 @@ def internal_settings(self):
sett = _DataController.internal_settings(self)
sett.insert(-1, TemplateController(self, ss.test_template))
sett.insert(-1, TimeoutController(self, ss.test_timeout))
- return sett + [self.default_tags, self.force_tags] # OK doing some cheating here ;)
+ return sett + [self.default_tags, self.force_tags, self.test_tags] # OK doing some cheating here ;)
@property
def longname(self):
@@ -1173,6 +1181,10 @@ def setting_table(self):
def force_tags(self): # Yes, I know this is impossible, but is Exclude file, right?
return None # ForceTagsController(self, self.setting_table.force_tags)
+ @property
+ def test_tags(self): # Yes, I know this is impossible, but is Exclude file, right?
+ return None
+
@property
def dirty(self):
return False
diff --git a/src/robotide/controller/macrocontrollers.py b/src/robotide/controller/macrocontrollers.py
index 289bb8c17..8c69a4438 100644
--- a/src/robotide/controller/macrocontrollers.py
+++ b/src/robotide/controller/macrocontrollers.py
@@ -342,6 +342,10 @@ def tags(self):
def force_tags(self):
return self.datafile_controller.force_tags
+ @property
+ def test_tags(self):
+ return self.datafile_controller.test_tags
+
@property
def default_tags(self):
return self.datafile_controller.default_tags
@@ -416,6 +420,7 @@ def _init(self, kw):
# Needed for API compatibility in tag search
self.force_tags = []
self.default_tags = []
+ self.test_tags = []
def __eq__(self, other):
if self is other:
diff --git a/src/robotide/controller/settingcontrollers.py b/src/robotide/controller/settingcontrollers.py
index a54f2fc8d..c4f0cb411 100644
--- a/src/robotide/controller/settingcontrollers.py
+++ b/src/robotide/controller/settingcontrollers.py
@@ -22,7 +22,7 @@
RideItemSettingsChanged, RideImportSettingAdded)
from ..utils import variablematcher, unescape_newlines_and_whitespaces
from .basecontroller import ControllerWithParent
-from .tags import Tag, ForcedTag, DefaultTag
+from .tags import Tag, ForcedTag, DefaultTag, TestTag
class _SettingController(ControllerWithParent):
@@ -222,13 +222,14 @@ def remove(self, tag):
def __iter__(self):
forced = self._parent.force_tags
+ test_tags = self._parent.test_tags
if self.tags.value is None:
- return chain(forced, self._parent.default_tags).__iter__()
+ return chain(forced, self._parent.default_tags, test_tags).__iter__()
if len(self.tags.value) == 0:
- return chain(forced, [Tag('', controller=self)])
+ return chain(forced, test_tags, [Tag('', controller=self)])
own_tags = (Tag(t, index, self)
for index, t in enumerate(self.tags.value))
- return chain(forced, own_tags).__iter__()
+ return chain(forced, test_tags, own_tags).__iter__()
@property
def is_set(self):
@@ -284,11 +285,47 @@ def _recursive_gather_from(self, obj, result):
def _gather_from_data(tags, parent):
if tags.value is None:
return []
- print(f"DEBUG: SettingsController _gather_from_data entry tags={tags.value}")
return [ForcedTag(t, index, parent)
for index, t in enumerate(tags.value)]
+class TestTagsController(TagsController):
+
+ def empty_tag(self):
+ return TestTag(None, controller=self)
+
+ def __iter__(self):
+ return self._recursive_gather_from(self.parent, []).__iter__()
+
+ def __eq__(self, other):
+ if self is other:
+ return True
+ if other is None:
+ return False
+ if not isinstance(other, self.__class__):
+ return False
+ return self.tags == other.tags
+
+ def _recursive_gather_from(self, obj, result):
+ if obj is None:
+ return result
+ try:
+ test_tags = obj.setting_table.test_tags
+ except AttributeError: # In the case of a .resource file, there is no Test Tags fields
+ return result
+ # print(f"DEBUG: SettingsController _recursive_gather_from force_tags={force_tags}, obj.parent={obj.parent}")
+ return self._recursive_gather_from(
+ obj.parent,
+ self._gather_from_data(test_tags, obj.test_tags) + result)
+
+ @staticmethod
+ def _gather_from_data(tags, parent):
+ if tags.value is None:
+ return []
+ return [TestTag(t, index, parent)
+ for index, t in enumerate(tags.value)]
+
+
class TimeoutController(_SettingController):
def _init(self, timeout):
diff --git a/src/robotide/controller/tags.py b/src/robotide/controller/tags.py
index d97e03a0f..411b5a8af 100644
--- a/src/robotide/controller/tags.py
+++ b/src/robotide/controller/tags.py
@@ -65,3 +65,11 @@ class DefaultTag(Tag):
def tooltip(self):
return u'Default tag from suite {0}'.format(
self.controller.datafile_controller.name)
+
+
+class TestTag(Tag):
+
+ @property
+ def tooltip(self):
+ return u'Apply Test Tags from suite {0} (since Robot Framework 6.0)'.format(
+ self.controller.datafile_controller.name)
diff --git a/src/robotide/editor/dialoghelps.py b/src/robotide/editor/dialoghelps.py
index c8b8ed2cf..61767308f 100644
--- a/src/robotide/editor/dialoghelps.py
+++ b/src/robotide/editor/dialoghelps.py
@@ -79,6 +79,13 @@ def get_help(title):
%(TAG)s
%(ESCAPE)s
+Test Tags
+These tags are applied to all test cases in this test suite. This field exists since Robot Framework 6.0 and will
+ replace Force and Default Tags after version 7.0.
+Inherited tags are not shown in this view.
+%(TAG)s
+%(ESCAPE)s
+
Tags
These tags are set to this test case in addition to Force Tags and they override possible Default Tags.
Inherited tags are not shown in this view.
diff --git a/src/robotide/editor/editordialogs.py b/src/robotide/editor/editordialogs.py
index 173e854bb..33502efc7 100644
--- a/src/robotide/editor/editordialogs.py
+++ b/src/robotide/editor/editordialogs.py
@@ -236,6 +236,12 @@ def _execute(self):
pass
+class TestTagsDialog(_SettingDialog):
+ def _execute(self):
+ """ Just ignore it """
+ pass
+
+
class TagsDialog(_SettingDialog):
def _execute(self):
""" Just ignore it """
diff --git a/src/robotide/editor/editors.py b/src/robotide/editor/editors.py
index 00dae9c08..e42f87e03 100644
--- a/src/robotide/editor/editors.py
+++ b/src/robotide/editor/editors.py
@@ -92,7 +92,7 @@ def __init__(self, plugin, parent, controller, tree):
self.SetSizer(self.sizer)
if self.title:
self.sizer.Add(self._create_header(self.title),
- 0, wx.EXPAND | wx.ALL, 5)
+ 0, wx.EXPAND | wx.ALL, 6)
self._editors = []
self._last_shown_tooltip = None
self._reset_last_show_tooltip()
@@ -269,12 +269,6 @@ def _get_editor_class(controller):
def build(self, settings, plugin, tree):
for setting in settings:
editor = self.create_editor_for(setting, plugin, tree)
- """
- editor.SetBackgroundColour(Colour(200, 222, 40))
- editor.SetOwnBackgroundColour(Colour(200, 222, 40))
- editor.SetForegroundColour(Colour(7, 0, 70))
- editor.SetOwnForegroundColour(Colour(7, 0, 70))
- """
self._sizer.Add(editor, 0, wx.ALL | wx.EXPAND, self.BORDER)
self._editors.append(editor)
editor.Refresh()
diff --git a/src/robotide/editor/tags.py b/src/robotide/editor/tags.py
index 513a7244f..599dba539 100644
--- a/src/robotide/editor/tags.py
+++ b/src/robotide/editor/tags.py
@@ -17,7 +17,7 @@
from wx.lib.scrolledpanel import ScrolledPanel
from ..context import IS_WINDOWS
from ..controller import ctrlcommands
-from ..controller.tags import ForcedTag, DefaultTag
+from ..controller.tags import ForcedTag, DefaultTag, TestTag
class TagsDisplay(ScrolledPanel):
@@ -225,7 +225,8 @@ def properties(tag, controller):
if tag.controller == controller:
return TagBoxProperties(tag)
return tag.choose({ForcedTag: ForcedTagBoxProperties,
- DefaultTag: DefaultTagBoxProperties})(tag)
+ DefaultTag: DefaultTagBoxProperties,
+ TestTag: TestTagBoxProperties})(tag)
class _TagBoxProperties(object):
@@ -292,3 +293,10 @@ class DefaultTagBoxProperties(_TagBoxProperties):
foreground_color = '#666666'
background_color = '#D3D3D3' # Colour(200, 222, 40)
enabled = False
+
+
+class TestTagBoxProperties(_TagBoxProperties):
+ # DEBUG: Use colours from settings
+ foreground_color = 'orange'
+ background_color = '#D3D3D3' # Colour(200, 222, 40)
+ enabled = False
diff --git a/src/robotide/lib/robot/parsing/model.py b/src/robotide/lib/robot/parsing/model.py
index 0e8f22bfb..858bf4205 100644
--- a/src/robotide/lib/robot/parsing/model.py
+++ b/src/robotide/lib/robot/parsing/model.py
@@ -45,7 +45,7 @@ def TestData(parent=None, source=None, include_suites=None,
:returns: :class:`~.model.TestDataDirectory` if `source` is a directory,
:class:`~.model.TestCaseFile` otherwise.
"""
- # TODO: Remove in RF 3.2.
+ # DEBUG: Remove in RF 3.2.
if warn_on_skipped != 'DEPRECATED':
warnings.warn("Option 'warn_on_skipped' is deprecated and has no "
"effect.", DeprecationWarning)
@@ -77,7 +77,7 @@ def _get_tables(self):
(self._keyword_table_names, self.keyword_table),
(self._comment_table_names, None)]:
# remove Comments section, because we want to keep them as they are in files
- # , (self._comment_table_names, None)]:
+ # , (self._comment_table_names, None)
for name in names:
yield name, table
@@ -117,7 +117,7 @@ def _resolve_deprecated_table(self, used_name):
for name in (self._setting_table_names + self._variable_table_names +
self._testcase_table_names + self._keyword_table_names):
# remove Comments section, because we want to keep them as they are in files
- # + self._comment_table_names):
+ # + self._comment_table_names
if normalize(name) == normalized:
self._report_deprecated_table(used_name, name)
return name
@@ -414,8 +414,9 @@ def __init__(self, parent):
self.suite_teardown = Fixture('Suite Teardown', self)
self.test_setup = Fixture('Test Setup', self)
self.test_teardown = Fixture('Test Teardown', self)
- self.force_tags = Tags('Force Tags', self)
- self.default_tags = Tags('Default Tags', self)
+ self.force_tags = Tags('Force Tags', self) # To deprecate after RF 7.0
+ self.default_tags = Tags('Default Tags', self) # To deprecate after RF 7.0
+ self.test_tags = Tags('Test Tags', self) # New since RF 6.0
self.test_template = Template('Test Template', self)
self.test_timeout = Timeout('Test Timeout', self)
self.metadata = MetadataList(self)
@@ -454,6 +455,7 @@ class TestCaseFileSettingTable(_SettingTable):
'Test Teardown': lambda s: s.test_teardown.populate,
'Force Tags': lambda s: s.force_tags.populate,
'Default Tags': lambda s: s.default_tags.populate,
+ 'Test Tags': lambda s: s.test_tags.populate,
'Test Template': lambda s: s.test_template.populate,
'Test Timeout': lambda s: s.test_timeout.populate,
'Library': lambda s: s.imports.populate_library,
@@ -468,7 +470,7 @@ class TestCaseFileSettingTable(_SettingTable):
def __iter__(self):
for setting in [self.doc, self.suite_setup, self.suite_teardown,
self.test_setup, self.test_teardown, self.force_tags,
- self.default_tags, self.test_template, self.test_timeout] \
+ self.default_tags, self.test_tags, self.test_template, self.test_timeout] \
+ self.metadata.data + self.imports.data:
yield setting
@@ -492,6 +494,7 @@ class InitFileSettingTable(_SettingTable):
'Test Teardown': lambda s: s.test_teardown.populate,
'Test Timeout': lambda s: s.test_timeout.populate,
'Force Tags': lambda s: s.force_tags.populate,
+ 'Test Tags': lambda s: s.test_tags.populate,
'Library': lambda s: s.imports.populate_library,
'Resource': lambda s: s.imports.populate_resource,
'Variables': lambda s: s.imports.populate_variables,
@@ -499,7 +502,7 @@ class InitFileSettingTable(_SettingTable):
def __iter__(self):
for setting in [self.doc, self.suite_setup, self.suite_teardown,
- self.test_setup, self.test_teardown, self.force_tags,
+ self.test_setup, self.test_teardown, self.force_tags, self.test_tags,
self.test_timeout] + self.metadata.data + self.imports.data:
yield setting
@@ -875,7 +878,7 @@ def __init__(self, content, comment=None):
# print(f"DEBUG: RFLib Model init Step: index={index} inner_kw_pos = {self.inner_kw_pos} indent={self.indent[:]} \ncontent {content}")
self.args = content[index + 1:] if content and index <= len(content) - 1 else []
# print(f"DEBUG: RFLib Model init Step: 1st cell len(content)={len(content)} index {index} indent={self.indent[:]}") # 1st cell: {content[index]}")
- # TODO: Create setters for Step.name and Step.args, see stepcontrollers.py replace_keyword
+ # DEBUG: Create setters for Step.name and Step.args, see stepcontrollers.py replace_keyword
if index < len(content):
self.name = content[index] if content else None
else:
@@ -899,7 +902,7 @@ def _get_assign(self):
if 0 <= index < len(cells) and self.is_kind_of_comment(cells[index]): # Special case for commented content
return []
# print(f"DEBUG: RFLib Model _get_assign VAR NORMAL (index={index}) inner_kw_pos={self.inner_kw_pos} content={content[:]}")
- # first handle non FOR cases
+ # first handle non-FOR cases
idx = 0
try:
if cells[self.inner_kw_pos] != 'FOR':
@@ -953,7 +956,7 @@ def is_comment(self):
return self.name.lower() == 'comment' or not (self.assign or self.name or self.args)
def is_for_loop(self):
- # TODO: remove steps ForLoop: return self.name == 'FOR'
+ # DEBUG: remove steps ForLoop: return self.name == 'FOR'
return False
def is_set(self):
diff --git a/src/robotide/lib/robot/running/defaults.py b/src/robotide/lib/robot/running/defaults.py
index 9fcf3745a..2b2c70ae3 100644
--- a/src/robotide/lib/robot/running/defaults.py
+++ b/src/robotide/lib/robot/running/defaults.py
@@ -22,12 +22,14 @@ def __init__(self, settings, parent=None):
self.timeout = settings.test_timeout
self.force_tags = settings.force_tags
self.default_tags = settings.default_tags
+ self.test_tags = settings.test_tags
self.template = settings.test_template
if parent:
self.setup = self.setup or parent.setup
self.teardown = self.teardown or parent.teardown
self.timeout = self.timeout or parent.timeout
self.force_tags += parent.force_tags
+ self.test_tags += parent.test_tags
def get_test_values(self, test):
return TestValues(test, self)
@@ -40,4 +42,4 @@ def __init__(self, test, defaults):
self.teardown = test.teardown or defaults.teardown
self.timeout = test.timeout or defaults.timeout
self.template = test.template or defaults.template
- self.tags = (test.tags or defaults.default_tags) + defaults.force_tags
+ self.tags = (test.tags or defaults.default_tags) + defaults.force_tags + defaults.test_tags
diff --git a/src/robotide/version.py b/src/robotide/version.py
index 3aa8ad401..a92a18e61 100644
--- a/src/robotide/version.py
+++ b/src/robotide/version.py
@@ -14,4 +14,4 @@
# limitations under the License.
#
# Automatically generated by `tasks.py`.
-VERSION = 'v2.0.8dev18'
+VERSION = 'v2.0.8dev19'
diff --git a/utest/controller/test_controllers.py b/utest/controller/test_controllers.py
index bfc1ff2c7..08bdfc147 100644
--- a/utest/controller/test_controllers.py
+++ b/utest/controller/test_controllers.py
@@ -57,6 +57,7 @@ def __init__(self):
self.datafile = None
self.force_tags = ForceTagsController(self, Tags('Force Tags'))
self.default_tags = DefaultTagsController(self, Tags('Default Tags'))
+ self.test_tags = DefaultTagsController(self, Tags('Test Tags'))
self._setting_table = self
def mark_dirty(self):
diff --git a/utest/controller/test_filecontrollers.py b/utest/controller/test_filecontrollers.py
index ecb062e29..a487e68cc 100644
--- a/utest/controller/test_filecontrollers.py
+++ b/utest/controller/test_filecontrollers.py
@@ -88,7 +88,7 @@ def setUp(self):
def test_creation(self):
for st in self.ctrl.settings:
assert st is not None
- assert len(self.ctrl.settings) == 9
+ assert len(self.ctrl.settings) == 10
assert not self.ctrl.dirty
def test_has_format(self):
@@ -236,7 +236,7 @@ def test_creation(self):
ctrl = TestDataDirectoryController(self.data)
for st in ctrl.settings:
assert st is not None
- assert len(ctrl.settings) == 6
+ assert len(ctrl.settings) == 7
def test_has_format(self):
ctrl = TestDataDirectoryController(self.data)
diff --git a/utest/searchtests/test_matcher.py b/utest/searchtests/test_matcher.py
index 55a22f9de..0b240068e 100644
--- a/utest/searchtests/test_matcher.py
+++ b/utest/searchtests/test_matcher.py
@@ -28,6 +28,7 @@ def _test(self, name='name', tags=None, doc='documentation'):
parent.register_for_namespace_updates = lambda *_: 0
parent.force_tags = []
parent.default_tags = []
+ parent.test_tags = []
robot_test = TestCase(parent=parent, name=name)
robot_test.get_setter('documentation')(doc)
robot_test.get_setter('tags')(tags or [])