Skip to content

Commit

Permalink
Merge pull request robotframework#2913 from HelioGuilherme66/develop
Browse files Browse the repository at this point in the history
Fix full path keywords (#158)
  • Loading branch information
HelioGuilherme66 authored Nov 20, 2024
2 parents 2814a28 + 3b69b3c commit e2bfcf5
Show file tree
Hide file tree
Showing 16 changed files with 177 additions and 78 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ All notable changes to this project will be documented in this file.
The format is based on http://keepachangelog.com/en/1.0.0/[Keep a Changelog]
and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioning].

// == https://github.com/robotframework/RIDE[Unreleased]
== https://github.com/robotframework/RIDE[Unreleased]

== https://github.com/robotframework/RIDE/blob/master/doc/releasenotes/ride-2.1.1.rst[2.1.1] - 2024-11-14

=== Fixed

- Fixed no recognition of keywords with embedded arguments and full name. Issue #1106 from 12 Sep 2012.

== https://github.com/robotframework/RIDE/blob/master/doc/releasenotes/ride-2.1.1.rst[2.1.1] - 2024-11-14

Expand Down
4 changes: 2 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ See the https://github.com/robotframework/RIDE/blob/master/doc/releasenotes/ride
**Version https://github.com/robotframework/RIDE/tree/release/1.7.4.2[1.7.4.2] was the last release supporting Python 2.7**


**The current development version is based on 2.1, supports Python from 3.8 up to 3.13 (5th November 2024).**
**The current development version is based on 2.1,1, supports Python from 3.8 up to 3.13 (20th November 2024).**

Currently, the unit tests are tested on Python 3.10, 3.11 and 3.13 (but 3.12 is the recommended version).
Likewise, the current version of wxPython, is 4.2.2, but RIDE is known to work with 4.0.7 and 4.1.1 versions.
Expand All @@ -40,7 +40,7 @@ Likewise, the current version of wxPython, is 4.2.2, but RIDE is known to work w

`pip install -U robotframework-ride`

(3.8 <= python <= 3.13) Install current development version (**2.1.1**) with:
(3.8 <= python <= 3.13) Install current development version (**2.2dev1**) with:

`pip install -U https://github.com/robotframework/RIDE/archive/develop.zip`

Expand Down
98 changes: 50 additions & 48 deletions src/robotide/application/CHANGELOG.html

Large diffs are not rendered by default.

7 changes: 2 additions & 5 deletions src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,7 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Fixed broken go to definition after editing content in resource files.</li>
<li>Fixed long arguments in fixtures appearing splitted in Grid Editor. Still, arguments info will not be correct at
calling step.</li>
<li>Fixed double action on Linux when pressing the DEL key</li>
<li>Fixed no recognition of keywords with embedded arguments and full name. Issue #1106 from 12 Sep 2012.</li>
</ul>
<!-- <p>We hope to implement or complete features and make fixes on next major version 2.1 (in mid Autumm of 2024).</p>
-->
Expand Down Expand Up @@ -227,7 +224,7 @@ def set_content(self, html_win, content):
<pre class="literal-block">python -m robotide.postinstall -install</pre>
<p>or</p>
<pre class="literal-block">ride_postinstall.py -install</pre>
<p>RIDE {VERSION} was released on 14/November/2024.</p>
<p>RIDE {VERSION} was released on 20/November/2024.</p>
<!-- <br/>
<h3>May The Fourth Be With You!</h3>
<h3>Celebrate the bank holiday, 10th June, Day of Portugal, Portuguese Communities and Camões!!</h3>
Expand Down
2 changes: 2 additions & 0 deletions src/robotide/controller/filecontrollers.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ def metadata(self):

def is_user_keyword(self, datafile, value):
_ = datafile
# print(f"DEBUG: filecontrollers.py _DataController is_user_keyword datafile={datafile} "
# f" value={value}")
return WithNamespace.is_user_keyword(self, self.datafile, value)

def is_library_keyword(self, datafile, value):
Expand Down
9 changes: 6 additions & 3 deletions src/robotide/namespace/embeddedargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ class EmbeddedArgsHandler(object):

def __init__(self, keyword):
if keyword.arguments:
raise TypeError('Cannot have normal arguments')
self.name_regexp, self.embedded_args = \
EmbeddedArgumentParser().parse(keyword.name)
# raise TypeError('Cannot have normal arguments')
print('DEBUG: Found normal arguments in embedded arguments keyword.')
# print(f'DEBUG: embeddedargs.py EmbeddedArgsHandler keyword={keyword.name} longname={keyword.longname}')
self.name_regexp, self.embedded_args = EmbeddedArgumentParser().parse(keyword.name)
if hasattr(keyword, 'longname'):
self.longname_regexp, _ = EmbeddedArgumentParser().parse(keyword.longname)
if not self.embedded_args:
raise TypeError('Must have embedded arguments')
16 changes: 14 additions & 2 deletions src/robotide/namespace/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ def new_resource(self, path, directory=''):

def find_user_keyword(self, datafile, kw_name):
kw = self.find_keyword(datafile, kw_name)
# print(f"DEBUG: namespace.py Namespace find_user_keyword datafile={datafile} "
# f" kw_name={kw_name} keyword={kw}")
return kw if isinstance(kw, UserKeywordInfo) else None

def is_user_keyword(self, datafile, kw_name):
Expand Down Expand Up @@ -633,18 +635,28 @@ def _add_embedded(self, kw):
try:
handler = EmbeddedArgsHandler(kw)
self.embedded_keywords[handler.name_regexp] = kw
if hasattr(handler, 'longname_regexp'):
self.embedded_keywords[handler.longname_regexp] = kw
# print(f"DEBUG: namespace.py _add_embedded add kw={kw.name} longname={kw.longname}\n"
# f"handler.name_regexp={handler.name_regexp}")
except TypeError:
pass

def get(self, kw_name):
if kw_name in self.keywords:
return self.keywords[kw_name]
# print(f"DEBUG: namespace.py _Keywords get keywords {self.keywords}")
bdd_name = self._get_bdd_name(kw_name)
if bdd_name and bdd_name in self.keywords:
return self.keywords[bdd_name]
# print(f"DEBUG: namespace.py _Keywords get embedded kws {self.embedded_keywords}"
# f"\nseaching keyword={kw_name}")
for regexp in self.embedded_keywords:
if regexp.match(kw_name) or (bdd_name and regexp.match(bdd_name)):
return self.embedded_keywords[regexp]
try:
if regexp.match(kw_name) or (bdd_name and regexp.match(bdd_name)):
return self.embedded_keywords[regexp]
except AttributeError:
pass
return None

def _get_bdd_name(self, kw_name):
Expand Down
2 changes: 1 addition & 1 deletion src/robotide/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
#
# Automatically generated by `tasks.py`.

VERSION = 'v2.1.1'
VERSION = 'v2.2dev1'
9 changes: 9 additions & 0 deletions utest/application/test_updatenotifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest
import sys
import typing
import unittest
import time
import urllib

from robotide.application.updatenotifier import UpdateNotifierController, UpdateDialog

IS_WINDOWS = sys.platform=='win32'

CHECKFORUPDATES = 'check for updates'
LASTUPDATECHECK = 'last update check'

Expand Down Expand Up @@ -222,6 +226,7 @@ def test_no_update_found_dev(self):
self.assertGreater(settings[LASTUPDATECHECK], time.time() - 1)
self.assertFalse(self._callback_called)

@pytest.mark.skipif(IS_WINDOWS, reason='Causes: Windows fatal exception: access violation')
def test_no_update_found_dev_notify(self):
settings = self.internal_settings()
ctrl = self._update_notifier_controller(settings, self.notebook, '0.55', '0.55')
Expand Down Expand Up @@ -287,6 +292,7 @@ def test_server_returns_older_version(self):
self.assertTrue(settings[CHECKFORUPDATES])
self.assertFalse(self._callback_called)

@pytest.mark.skipif(IS_WINDOWS, reason='Causes: Windows fatal exception: access violation')
def test_forced_check_released(self):
settings = self.internal_settings()
ctrl = self._update_notifier_controller(settings, self.notebook, '0.43.0', '0.43.1')
Expand All @@ -295,6 +301,7 @@ def test_forced_check_released(self):
self.assertTrue(settings[CHECKFORUPDATES])
self.assertTrue(self._callback_called)

@pytest.mark.skipif(IS_WINDOWS, reason='Causes: Windows fatal exception: access violation')
def test_forced_check_development(self):
settings = self.internal_settings()
ctrl = self._update_notifier_controller(settings, self.notebook, '0.44dev12', '0.44.dev14')
Expand All @@ -303,6 +310,7 @@ def test_forced_check_development(self):
self.assertTrue(settings[CHECKFORUPDATES])
self.assertTrue(self._callback_called)

@pytest.mark.skipif(IS_WINDOWS, reason='Causes: Windows fatal exception: access violation')
def test_forced_check_development_ok(self):
settings = self.internal_settings()
ctrl = self._update_notifier_controller(settings, self.notebook, '0.44dev12', '0.44.dev12')
Expand All @@ -311,6 +319,7 @@ def test_forced_check_development_ok(self):
self.assertTrue(settings[CHECKFORUPDATES])
self.assertFalse(self._callback_called)

@pytest.mark.skipif(IS_WINDOWS, reason='Causes: Windows fatal exception: access violation')
def test_normal_update_dialog(self):
""" This is not actually doing a test """
settings = self.internal_settings()
Expand Down
52 changes: 52 additions & 0 deletions utest/controller/test_cellinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,5 +415,57 @@ def _verify_cell_info(self, row, col, contenttype, celltype, macro=None):
assert cell_info.content_type == contenttype



class TestEmbeddedCellInfo(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.project_ctrl = datafilereader.construct_project(datafilereader.EMBEDDED_PROJECT)
# print(f"DEBUG: TestEmbeddedCellInfo setUpClass project_ctrl: {cls.project_ctrl.display_name}"
# f" {cls.project_ctrl.datafiles}")
cls.testsuite = cls.project_ctrl.datafiles[0]
cls.test1 = cls.testsuite.tests[0]
cls.test2 = cls.testsuite.tests[1]

@classmethod
def tearDownClass(cls):
cls.project_ctrl.close()

def test_var_and_kw(self):
# print("DEBUG: test_var_and_kw:")
# for s in self.test1.steps:
# print(f"{s.as_list()}")
self._verify_cell_info(0, 0, ContentType.LIBRARY_KEYWORD, CellType.KEYWORD, self.test1)
self._verify_cell_info(0, 1, ContentType.UNKNOWN_VARIABLE, CellType.OPTIONAL, self.test1)
self._verify_cell_info(0, 2, ContentType.STRING, CellType.OPTIONAL, self.test1)
self._verify_cell_info(1, 0, ContentType.USER_KEYWORD, CellType.KEYWORD, self.test1)
self._verify_cell_info(2, 0, ContentType.USER_KEYWORD, CellType.KEYWORD, self.test1)
# This was at TearDown
self.test1.execute(delete_rows([i for i in range(len(self.test1.steps))]))

def test_var_and_kw_prefix_resource(self):
# print("DEBUG: test_var_and_kw:")
# for s in self.test2.steps:
# print(f"{s.as_list()}")
self._verify_cell_info(0, 0, ContentType.LIBRARY_KEYWORD, CellType.KEYWORD, self.test2)
self._verify_cell_info(0, 1, ContentType.UNKNOWN_VARIABLE, CellType.OPTIONAL, self.test2)
self._verify_cell_info(0, 2, ContentType.STRING, CellType.OPTIONAL, self.test2)
self._verify_cell_info(1, 0, ContentType.USER_KEYWORD, CellType.KEYWORD, self.test2)
self._verify_cell_info(2, 0, ContentType.LIBRARY_KEYWORD, CellType.KEYWORD, self.test2)
self._verify_cell_info(2, 1, ContentType.UNKNOWN_VARIABLE, CellType.OPTIONAL, self.test2)
self._verify_cell_info(2, 2, ContentType.STRING, CellType.OPTIONAL, self.test2)
self._verify_cell_info(3, 0, ContentType.USER_KEYWORD, CellType.KEYWORD, self.test2)
self._verify_cell_info(3, 1, ContentType.VARIABLE, CellType.MANDATORY, self.test2)
# This was at TearDown
self.test2.execute(delete_rows([i for i in range(len(self.test2.steps))]))

@staticmethod
def _verify_cell_info(row, col, contenttype, celltype, macro=None):
cell_info = macro.get_cell_info(row, col)
# print(f"DEBUG:test_cellinfo type cell_type{cell_info.cell_type} content_type{cell_info.content_type}")
assert cell_info.cell_type == celltype
assert cell_info.content_type == contenttype


if __name__ == "__main__":
unittest.main()
6 changes: 3 additions & 3 deletions utest/namespace/test_embedded_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ def __init__(self, name, args=None):

class TestEmbeddedArgs(unittest.TestCase):

def test_extra_arguments_are_illegal(self):
with pytest.raises(TypeError):
EmbeddedArgsHandler(KWMock('add user ${user} to db', ['${arg}']))
def test_extra_arguments_are_no_longer_illegal(self):
handler = EmbeddedArgsHandler(KWMock('add user ${user} to db', ['${arg}']))
assert handler.embedded_args == ['user']

def test_no_embedded_arguments(self):
with pytest.raises(TypeError):
Expand Down
18 changes: 6 additions & 12 deletions utest/namespace/test_keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,14 @@ def test_given_when_then(self):
assert self.kws.get('But my kw')

def test_embedded_args(self):
assert self.kws.get(
'john should embed arguments and something')
assert self.kws.get(
'WHEN john should embed arguments and something')
assert self.kws.get(
'but john should embed arguments and something')
assert not self.kws.get(
'this keyword has real args')
assert self.kws.get('john should embed arguments and something')
assert self.kws.get('WHEN john should embed arguments and something')
assert self.kws.get('but john should embed arguments and something')
assert self.kws.get('this keyword has real args') # Now it is possible to have normal arguments

def test_embedded_args_are_space_sensitive(self):
assert not self.kws.get(
'john shouldembed arguments and something')
assert not self.kws.get(
'given johnshould embed arguments and something')
assert not self.kws.get('john shouldembed arguments and something')
assert not self.kws.get('given johnshould embed arguments and something')

def test_first_come_prioritized_when_same_short_name(self):
kws = _Keywords([ItemMock('My kw', ['${arg}'], 'source.My kw'),
Expand Down
2 changes: 1 addition & 1 deletion utest/namespace/test_suggesters.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def _assert_suggestion_names(self, expected, value):
self.assertEqual(expected, self._suggestion_names(value))

def _suggestion_names(self, value):
return [s.name for s in self._suggester.get_suggestions(value)]
return [s.name for s in self._suggester.get_suggestions(value) if s.name != 'Telnet']


class _ImportSuggesterTests(_ImportSuggesterHelpers):
Expand Down
1 change: 1 addition & 0 deletions utest/resources/datafilereader.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def _makepath(*elements):
FOR_LOOP_PATH = _makepath('forloop')

ARGUMENTS_PATH = _makepath('arguments_suite')
EMBEDDED_PROJECT = _makepath('Embedded_arguments.robot')

SIMPLE_PROJECT = _makepath('simple', 'test.robot')

Expand Down
14 changes: 14 additions & 0 deletions utest/resources/robotdata/Embedded_arguments.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
*** Settings ***
Resource resources/resource.resource

*** Test Cases ***
Call keyword
VAR ${action} Sends
User ${action} Email
User Reads Email

Server keyword
VAR ${action} Sends Email
resource.Server ${action} To User John Doe
VAR ${user} Jane Doe
resource.Server Sends Message To User ${user}
7 changes: 7 additions & 0 deletions utest/resources/robotdata/resources/resource.resource
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@ Funny Def Value

Duplicate UK
No Operation

User ${action} Email
Log Keyword call is "User ${action} Email" console=True

Server ${action} To User
[Arguments] ${user}
Log Keyword call is "Server ${action} To User" where \${user}=${user} console=True

0 comments on commit e2bfcf5

Please sign in to comment.