robotframework-nl 2.2.1__tar.gz → 3.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {robotframework-nl-2.2.1/robotframework_nl.egg-info → robotframework_nl-3.1.0}/PKG-INFO +2 -2
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/pyproject.toml +2 -2
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0/robotframework_nl.egg-info}/PKG-INFO +2 -2
- robotframework_nl-3.1.0/robotframework_nl.egg-info/requires.txt +1 -0
- robotframework_nl-3.1.0/robotnl/CheckOperator.py +394 -0
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotnl/RobotChecks.py +14 -10
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotnl/inline_keywords.py +8 -13
- robotframework_nl-3.1.0/robotnl/version.py +1 -0
- robotframework-nl-2.2.1/robotframework_nl.egg-info/requires.txt +0 -1
- robotframework-nl-2.2.1/robotnl/CheckOperator.py +0 -227
- robotframework-nl-2.2.1/robotnl/version.py +0 -1
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/LICENSE +0 -0
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/README.md +0 -0
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotframework_nl.egg-info/SOURCES.txt +0 -0
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotframework_nl.egg-info/dependency_links.txt +0 -0
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotframework_nl.egg-info/top_level.txt +0 -0
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotnl/__init__.py +0 -0
- {robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robotframework-nl
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: robotnl is a proving ground to boost Robot framework closer to Natural Language.
|
|
5
5
|
Author-email: Johan Foederer <github@famfoe.nl>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -41,7 +41,7 @@ Classifier: Operating System :: OS Independent
|
|
|
41
41
|
Requires-Python: >=3.8
|
|
42
42
|
Description-Content-Type: text/markdown
|
|
43
43
|
License-File: LICENSE
|
|
44
|
-
Requires-Dist: robotframework>=
|
|
44
|
+
Requires-Dist: robotframework>=7.0
|
|
45
45
|
|
|
46
46
|
# robotframeworkNL - the oneliner
|
|
47
47
|
robotframeworkNL is a proving ground to boost Robot framework closer to Natural Language.
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "robotframework-nl"
|
|
10
|
-
version = "
|
|
10
|
+
version = "3.1.0"
|
|
11
11
|
description = "robotnl is a proving ground to boost Robot framework closer to Natural Language."
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
authors = [{ name = "Johan Foederer", email = "github@famfoe.nl" }]
|
|
@@ -19,7 +19,7 @@ classifiers = [
|
|
|
19
19
|
]
|
|
20
20
|
keywords = ["robotframework", "robot", "testing", "dsl"]
|
|
21
21
|
dependencies = [
|
|
22
|
-
"robotframework >=
|
|
22
|
+
"robotframework >= 7.0",
|
|
23
23
|
]
|
|
24
24
|
requires-python = ">=3.8"
|
|
25
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robotframework-nl
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: robotnl is a proving ground to boost Robot framework closer to Natural Language.
|
|
5
5
|
Author-email: Johan Foederer <github@famfoe.nl>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -41,7 +41,7 @@ Classifier: Operating System :: OS Independent
|
|
|
41
41
|
Requires-Python: >=3.8
|
|
42
42
|
Description-Content-Type: text/markdown
|
|
43
43
|
License-File: LICENSE
|
|
44
|
-
Requires-Dist: robotframework>=
|
|
44
|
+
Requires-Dist: robotframework>=7.0
|
|
45
45
|
|
|
46
46
|
# robotframeworkNL - the oneliner
|
|
47
47
|
robotframeworkNL is a proving ground to boost Robot framework closer to Natural Language.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
robotframework>=7.0
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# BSD 3-Clause License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2021, J. Foederer
|
|
6
|
+
# All rights reserved.
|
|
7
|
+
#
|
|
8
|
+
# Redistribution and use in source and binary forms, with or without
|
|
9
|
+
# modification, are permitted provided that the following conditions are met:
|
|
10
|
+
#
|
|
11
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
12
|
+
# list of conditions and the following disclaimer.
|
|
13
|
+
#
|
|
14
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
15
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
16
|
+
# and/or other materials provided with the distribution.
|
|
17
|
+
#
|
|
18
|
+
# 3. Neither the name of the copyright holder nor the names of its
|
|
19
|
+
# contributors may be used to endorse or promote products derived from
|
|
20
|
+
# this software without specific prior written permission.
|
|
21
|
+
#
|
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
25
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
26
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
27
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
28
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
29
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
30
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
31
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
+
|
|
33
|
+
from robot.api import TypeInfo
|
|
34
|
+
from robot.libraries.BuiltIn import BuiltIn
|
|
35
|
+
from robot.running.arguments import TypeConverter
|
|
36
|
+
from robot.utils import is_list_like
|
|
37
|
+
|
|
38
|
+
from .inline_keywords import keyword
|
|
39
|
+
|
|
40
|
+
class CheckOperator:
|
|
41
|
+
"""
|
|
42
|
+
This class defines a set of commonly used operators for use by 'Check that'
|
|
43
|
+
and other check keywords
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
################################################################################################
|
|
47
|
+
# Generic operators that can work on basically any object type
|
|
48
|
+
def equals(self, lValue, rValue):
|
|
49
|
+
"""Checks whether the left and right side are equal to each other [`=`]
|
|
50
|
+
|
|
51
|
+
Applies Robot type conversions when executing the check.
|
|
52
|
+
Examples:
|
|
53
|
+
| `Check that` | 7 | `=` | 7 |
|
|
54
|
+
| `Check that` | ${7} | `=` | 7.0 |
|
|
55
|
+
| `Check that` | _Two times_ | 6 | `equals` | 12 |
|
|
56
|
+
| `Check that` | text | `equals` | TeXT |
|
|
57
|
+
"""
|
|
58
|
+
return OperatorProxy("==").basicOperator(lValue, rValue)
|
|
59
|
+
|
|
60
|
+
def is_less_than(self, lValue, rValue):
|
|
61
|
+
"""Checks whether the left side `is less than` or smaller than the right side [`<`]
|
|
62
|
+
|
|
63
|
+
Applies Robot type conversions when executing the check.
|
|
64
|
+
Examples:
|
|
65
|
+
| `Check that` | 2 | `<` | 4 |
|
|
66
|
+
| `Check that` | 2 | `is less than` | 4 |
|
|
67
|
+
"""
|
|
68
|
+
return OperatorProxy("<").basicOperator(lValue, rValue)
|
|
69
|
+
|
|
70
|
+
def is_greater_than(self, lValue, rValue):
|
|
71
|
+
"""Checks whether the left side `is greater than` or larger than the right side [`>`]
|
|
72
|
+
|
|
73
|
+
Applies Robot type conversions when executing the check.
|
|
74
|
+
Examples:
|
|
75
|
+
| `Check that` | 4 | `>` | 2 |
|
|
76
|
+
| `Check that` | 4 | `is greater than` | 2 |
|
|
77
|
+
"""
|
|
78
|
+
return OperatorProxy(">").basicOperator(lValue, rValue)
|
|
79
|
+
|
|
80
|
+
def is_less_than_or_equal_to(self, lValue, rValue):
|
|
81
|
+
"""Checks whether the left side `is less than or equal to` the right side [`≤`]
|
|
82
|
+
|
|
83
|
+
Applies Robot type conversions when executing the check.
|
|
84
|
+
Examples:
|
|
85
|
+
| `Check that` | 2 | `≤` | 2 |
|
|
86
|
+
| `Check that` | 2 | `is less than or equal to` | 4 |
|
|
87
|
+
"""
|
|
88
|
+
return OperatorProxy("<=").basicOperator(lValue, rValue)
|
|
89
|
+
|
|
90
|
+
def is_greater_than_or_equal_to(self, lValue, rValue):
|
|
91
|
+
"""Checks whether the left side `is greater than or equal to` the right side [`≥`]
|
|
92
|
+
|
|
93
|
+
Applies Robot type conversions when executing the check.
|
|
94
|
+
Examples:
|
|
95
|
+
| `Check that` | 4 | `≥` | 4 |
|
|
96
|
+
| `Check that` | 4 | `is greater than or equal to` | 2 |
|
|
97
|
+
"""
|
|
98
|
+
return OperatorProxy(">=").basicOperator(lValue, rValue)
|
|
99
|
+
|
|
100
|
+
def does_not_equal(self, lValue, rValue):
|
|
101
|
+
"""Checks whether the left side `does not equal`, i.e. is different from the right side [`≠`]
|
|
102
|
+
|
|
103
|
+
Applies Robot type conversions when executing the check.
|
|
104
|
+
Examples:
|
|
105
|
+
| `Check that` | 7 | `≠` | 13 |
|
|
106
|
+
| `Check that` | 7 | `≠` | 7.01 |
|
|
107
|
+
| `Check that` | _Two times_ | 6 | `does not equal` | 13 |
|
|
108
|
+
| `Check that` | random text | `does not equal` | my text |
|
|
109
|
+
"""
|
|
110
|
+
return OperatorProxy("!=").basicOperator(lValue, rValue)
|
|
111
|
+
|
|
112
|
+
################################################################################################
|
|
113
|
+
# Operators that work on text items str() or Unicode()
|
|
114
|
+
def contains_text(self, baseString, subString):
|
|
115
|
+
"""Performs a case insensitive check whether the right side is a substring of the left side
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
| `Check that` | the time is now | `contains text` | me |
|
|
119
|
+
| `Check that` | the time is now | `contains text` | ME |
|
|
120
|
+
| `Check That` | Robotstraße | `contains text` | Strasse |
|
|
121
|
+
"""
|
|
122
|
+
try:
|
|
123
|
+
return self.contains_exact_text(baseString.casefold(), subString.casefold())
|
|
124
|
+
except:
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def contains_exact_text(self, baseString, subString):
|
|
128
|
+
"""Performs a case sensitive check whether the right side is a substring of the left side
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
| `Check that` | the time is now | `contains text` | me |
|
|
132
|
+
| `Check That` | Robotstraße | `contains text` | straße |
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
return subString in baseString
|
|
136
|
+
except:
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
def matches_without_case_to(self, leftText, rightText):
|
|
140
|
+
"""Performs a case insensitive check whether the left and right side texts are equal
|
|
141
|
+
|
|
142
|
+
Examples:
|
|
143
|
+
| `Check that` | the time is now | `matches without case to` | the time is now |
|
|
144
|
+
| `Check that` | the time is now | `matches without case to` | THE TIME IS NOW |
|
|
145
|
+
| `Check That` | Robotstraße | `matches without case to` | ROBOTstrasse |
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
return self.matches_with_case_to(leftText.casefold(), rightText.casefold())
|
|
149
|
+
except:
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
def matches_with_case_to(self, leftText, rightText):
|
|
153
|
+
"""Performs a case sensitive check whether the left and right side texts are equal
|
|
154
|
+
|
|
155
|
+
Examples:
|
|
156
|
+
| `Check that` | the time is now | `matches with case to` | the time is now |
|
|
157
|
+
| `Check That` | Robotstraße | `matches with case to` | Robotstraße |
|
|
158
|
+
"""
|
|
159
|
+
return leftText == rightText
|
|
160
|
+
|
|
161
|
+
def does_not_contain_text(self, baseString, subString):
|
|
162
|
+
"""Performs a case insensitive check whether the right side is not a substring of the left side
|
|
163
|
+
|
|
164
|
+
Examples:
|
|
165
|
+
| `Check that` | random text | `does not contain text` | my text |
|
|
166
|
+
"""
|
|
167
|
+
return not self.contains_text(baseString, subString)
|
|
168
|
+
|
|
169
|
+
def does_not_contain_exact_text(self, baseString, subString):
|
|
170
|
+
"""Performs a case sensitive check whether the right side is not a substring of the left side
|
|
171
|
+
|
|
172
|
+
Examples:
|
|
173
|
+
| `Check that` | random text | `does not contain exact text` | my text |
|
|
174
|
+
| `Check that` | random text | `does not contain exact text` | RANDOM text |
|
|
175
|
+
"""
|
|
176
|
+
return not self.contains_exact_text(baseString, subString)
|
|
177
|
+
|
|
178
|
+
def does_not_match_without_case_to(self, leftText, rightText):
|
|
179
|
+
"""Performs a case insensitive check whether the left and right side texts are different
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
| `Check that` | random text | `does not match without case to` | my text |
|
|
183
|
+
"""
|
|
184
|
+
return not self.matches_without_case_to(leftText, rightText)
|
|
185
|
+
|
|
186
|
+
def does_not_match_with_case_to(self, leftText, rightText):
|
|
187
|
+
"""Performs a case sensitive check whether the left and right side texts are different
|
|
188
|
+
|
|
189
|
+
Examples:
|
|
190
|
+
| `Check that` | random text | `does not match with case to` | my text |
|
|
191
|
+
| `Check that` | random text | `does not match with case to` | RANDOM text |
|
|
192
|
+
"""
|
|
193
|
+
return not self.matches_with_case_to(leftText, rightText)
|
|
194
|
+
|
|
195
|
+
################################################################################################
|
|
196
|
+
# Operators that work on lists or other sequences
|
|
197
|
+
def is_empty(self, sequence):
|
|
198
|
+
"""Checks whether the sequence on the left does not contain any items.
|
|
199
|
+
|
|
200
|
+
Example:
|
|
201
|
+
| Take a new suitcase |
|
|
202
|
+
| `Check that` | suitcase | `is empty` |
|
|
203
|
+
_Assumes a 'box' type to be defined with associated action and observation keywords._
|
|
204
|
+
"""
|
|
205
|
+
return len(sequence) == 0
|
|
206
|
+
|
|
207
|
+
@keyword("contains ${n} items")
|
|
208
|
+
def contains_n_items(self, n:int, sequence):
|
|
209
|
+
"""Checks whether the sequence on the left contains ${n} items. Uses python's len-operator
|
|
210
|
+
to count the number of items.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
| `Check precondition` | suitcase | `is empty` |
|
|
214
|
+
| Put toothbrush into suitcase |
|
|
215
|
+
| `Check that` | suitcase | `contains 1 item` |
|
|
216
|
+
| Put t-shirt into suitcase |
|
|
217
|
+
| `Check that` | suitcase | `contains 2 items` |
|
|
218
|
+
_Assumes a 'suitcase' type to be defined with associated action and observation keywords._
|
|
219
|
+
"""
|
|
220
|
+
count = len(sequence)
|
|
221
|
+
BuiltIn().log(f"Counted {count} items")
|
|
222
|
+
return count == n
|
|
223
|
+
|
|
224
|
+
def contains_1_item(self, sequence):
|
|
225
|
+
return self.contains_n_items(1, sequence)
|
|
226
|
+
contains_1_item.__doc__ = contains_n_items.__doc__
|
|
227
|
+
|
|
228
|
+
def contains(self, sequence, part):
|
|
229
|
+
"""Checks whether part is present in sequence. Uses Python's primitive in-operator.
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
| Put toothbrush into suitcase |
|
|
233
|
+
| Put t-shirt into suitcase |
|
|
234
|
+
| `Check that` | suitcase | `contains` | toothbrush |
|
|
235
|
+
| `Check that` | suitcase | `contains` | t-shirt |
|
|
236
|
+
_Assumes a 'suitcase' type to be defined with associated action and observation keywords._
|
|
237
|
+
"""
|
|
238
|
+
return part in sequence
|
|
239
|
+
|
|
240
|
+
def does_not_contain(self, sequence, part):
|
|
241
|
+
"""Checks whether part is present in sequence. Uses Python's primitive 'not in' operator.
|
|
242
|
+
|
|
243
|
+
Example:
|
|
244
|
+
| `Check precondition` | suitcase | `is empty` |
|
|
245
|
+
| Put toothbrush into suitcase |
|
|
246
|
+
| `Check that` | suitcase | `does not contain` | t-shirt |
|
|
247
|
+
_Assumes a 'suitcase' type to be defined with associated action and observation keywords._
|
|
248
|
+
"""
|
|
249
|
+
return part not in sequence
|
|
250
|
+
|
|
251
|
+
def contains_item(self, sequence, part):
|
|
252
|
+
"""Checks whether the right side item(s) is/are part of the sequence on the left side.
|
|
253
|
+
|
|
254
|
+
`Contains item` and the plural `Contains items` are aliases. The difference with `Contains`
|
|
255
|
+
is that these iterate over the sequence and applies *automatic Robot type conversion*
|
|
256
|
+
between elements when applicable. Duplicate items from the right side can match a single
|
|
257
|
+
item from the left side.
|
|
258
|
+
|
|
259
|
+
Example:
|
|
260
|
+
| Put toothbrush into suitcase |
|
|
261
|
+
| Put t-shirt into suitcase |
|
|
262
|
+
| `Check that` | suitcase | `contains item` | toothbrush |
|
|
263
|
+
| `Check that` | suitcase | `contains items` | t-shirt | toothbrush |
|
|
264
|
+
| `Check that` | suitcase's lock code | `contains items` | 7 | ${2} |
|
|
265
|
+
_Assumes a 'suitcase' type to be defined with associated action and observation keywords._
|
|
266
|
+
"""
|
|
267
|
+
if not is_list_like(part):
|
|
268
|
+
part = [part]
|
|
269
|
+
for elem in part:
|
|
270
|
+
BuiltIn().log(f"Processing '{elem}' from right side")
|
|
271
|
+
for item in sequence:
|
|
272
|
+
if self.equals(elem, item):
|
|
273
|
+
BuiltIn().log("Matched")
|
|
274
|
+
break
|
|
275
|
+
BuiltIn().log("No match")
|
|
276
|
+
else:
|
|
277
|
+
BuiltIn().log(f"{elem} not present in left side list")
|
|
278
|
+
return False
|
|
279
|
+
return True
|
|
280
|
+
|
|
281
|
+
# Alias for plural form
|
|
282
|
+
contains_items = contains_item
|
|
283
|
+
|
|
284
|
+
def contains_exactly_the_items_from(self, sequence, sequence_right):
|
|
285
|
+
"""
|
|
286
|
+
Checks whether the sequence on the right side contains all items of the left side and vice versa.
|
|
287
|
+
Iterates over sequence on the left and matches each element with a single element on the right.
|
|
288
|
+
The items can be in any order. Automatic Robot type conversion is applied when applicable.
|
|
289
|
+
|
|
290
|
+
Example:
|
|
291
|
+
| `Check precondition` | suitcase | `is empty` |
|
|
292
|
+
| Put toothbrush into suitcase |
|
|
293
|
+
| Put t-shirt into suitcase |
|
|
294
|
+
| `Check that` | suitcase | `contains exactly the items from` | toothbrush | t-shirt |
|
|
295
|
+
| `Check that` | suitcase | `contains exactly the items from` | t-shirt | toothbrush |
|
|
296
|
+
| `Check that` | my packing list | `contains exactly the items from` | toothbrush | t-shirt |
|
|
297
|
+
| `Check that` | suitcase | `contains exactly the items from` | my packing list |
|
|
298
|
+
_Assumes a 'suitcase' type to be defined with associated action and observation keywords._
|
|
299
|
+
"""
|
|
300
|
+
if isinstance(sequence_right, str):
|
|
301
|
+
sequence_right = [sequence_right]
|
|
302
|
+
sequence_right = [*sequence_right]
|
|
303
|
+
for item in sequence:
|
|
304
|
+
BuiltIn().log(f"Processing '{item}' from left side list")
|
|
305
|
+
for i in range(len(sequence_right)):
|
|
306
|
+
if self.equals(item, sequence_right[i]):
|
|
307
|
+
sequence_right.pop(i)
|
|
308
|
+
break
|
|
309
|
+
else:
|
|
310
|
+
BuiltIn().log(f"Item '{item}' from left side is not found in the list on the right side")
|
|
311
|
+
return False
|
|
312
|
+
if len(sequence_right) > 0:
|
|
313
|
+
BuiltIn().log(f"Not all items from right side list are present: {sequence_right}")
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
return True
|
|
317
|
+
|
|
318
|
+
def does_not_contain_item(self, sequence, part):
|
|
319
|
+
"""
|
|
320
|
+
Checks whether the right side item is not part of the sequence on the left side.
|
|
321
|
+
Iterates over the sequence and applies automatic Robot type conversion where applicable.
|
|
322
|
+
|
|
323
|
+
No plural variant is available for this keyword due to ambiguity with List-like values. When
|
|
324
|
+
writing "[2, 4, 6] does not contain items [4, 5, 6]", one could expect a pass, because the
|
|
325
|
+
set [4, 5, 6] is not contained, but also a fail because item 4 *is* part of the left side set.
|
|
326
|
+
|
|
327
|
+
Example:
|
|
328
|
+
| `Check precondition` | suitcase | `is empty` |
|
|
329
|
+
| Put toothbrush into suitcase |
|
|
330
|
+
| `Check that` | suitcase | `does not contain item` | t-shirt |
|
|
331
|
+
_Assumes a 'suitcase' type to be defined with associated action and observation keywords._
|
|
332
|
+
"""
|
|
333
|
+
if is_list_like(part):
|
|
334
|
+
raise TypeError("List-like items not accepted as right side value")
|
|
335
|
+
return not self.contains_item(sequence, part)
|
|
336
|
+
|
|
337
|
+
# Add operator keywords that do not comply to Python's identifier syntax
|
|
338
|
+
setattr(CheckOperator, "=", CheckOperator.equals)
|
|
339
|
+
setattr(CheckOperator, "<", CheckOperator.is_less_than)
|
|
340
|
+
setattr(CheckOperator, ">", CheckOperator.is_greater_than)
|
|
341
|
+
setattr(CheckOperator, "≤", CheckOperator.is_less_than_or_equal_to)
|
|
342
|
+
setattr(CheckOperator, "≥", CheckOperator.is_greater_than_or_equal_to)
|
|
343
|
+
setattr(CheckOperator, "≠", CheckOperator.does_not_equal)
|
|
344
|
+
|
|
345
|
+
class OperatorProxy:
|
|
346
|
+
"""
|
|
347
|
+
Proxy class for mapping generic Robot comparison keywords to Python operators
|
|
348
|
+
"""
|
|
349
|
+
def __init__(self, s_operator):
|
|
350
|
+
self.__s_Operator = s_operator
|
|
351
|
+
|
|
352
|
+
@staticmethod
|
|
353
|
+
def __typeCastRobotStringValue(leadingValue, otherValue, name):
|
|
354
|
+
"""
|
|
355
|
+
When Robot files are parsed, arguments are always passed as string even when comparing
|
|
356
|
+
numbers or other objects. When a string argument is detected the other argument is
|
|
357
|
+
deemed leading. This function converts the other argument to the leading value's type
|
|
358
|
+
when possible. Otherwise the value is kept unchanged, inevitably leading to a mismatch
|
|
359
|
+
in comparison.
|
|
360
|
+
|
|
361
|
+
Precondition: otherValue is of types str and supports .casefold()
|
|
362
|
+
returns type casted otherValue
|
|
363
|
+
"""
|
|
364
|
+
CastedOther = otherValue # By default leave untouched
|
|
365
|
+
converter = TypeConverter.converter_for(TypeInfo.from_type(type(leadingValue)))
|
|
366
|
+
if converter:
|
|
367
|
+
try:
|
|
368
|
+
CastedOther = converter.convert(otherValue, name)
|
|
369
|
+
except ValueError as err:
|
|
370
|
+
BuiltIn().log(err, level='DEBUG')
|
|
371
|
+
BuiltIn().log(f"Comparing as {converter.type_name} values")
|
|
372
|
+
|
|
373
|
+
if isinstance(CastedOther, str):
|
|
374
|
+
# By default compare as case insensitive Unicode. Note that it already was a string.
|
|
375
|
+
BuiltIn().log(f"Interpreting {name} '{otherValue}' as string (case insensitive)")
|
|
376
|
+
CastedOther = str(otherValue).casefold()
|
|
377
|
+
|
|
378
|
+
return CastedOther
|
|
379
|
+
|
|
380
|
+
def basicOperator(self, lvalue, rvalue):
|
|
381
|
+
# Local argument copies for assignment
|
|
382
|
+
lValue = lvalue
|
|
383
|
+
rValue = rvalue
|
|
384
|
+
|
|
385
|
+
if type(lvalue) is str:
|
|
386
|
+
lValue = OperatorProxy.__typeCastRobotStringValue(rvalue, lvalue, "left operand")
|
|
387
|
+
|
|
388
|
+
if type(rvalue) is str:
|
|
389
|
+
rValue = OperatorProxy.__typeCastRobotStringValue(lvalue, rvalue, "right operand")
|
|
390
|
+
|
|
391
|
+
if lValue is lvalue and rValue is rvalue:
|
|
392
|
+
BuiltIn().log("Comparing values as is")
|
|
393
|
+
|
|
394
|
+
return eval(f"lValue {self.__s_Operator} rValue")
|
|
@@ -38,6 +38,7 @@ except ImportError:
|
|
|
38
38
|
tkinter = False
|
|
39
39
|
|
|
40
40
|
from robot.libraries.BuiltIn import BuiltIn
|
|
41
|
+
from robot.running import RUN_KW_REGISTER
|
|
41
42
|
from robot.utils import timestr_to_secs, secs_to_timestr
|
|
42
43
|
from .inline_keywords import is_keyword
|
|
43
44
|
|
|
@@ -77,6 +78,7 @@ class RobotChecks:
|
|
|
77
78
|
except CheckFailed as failure:
|
|
78
79
|
failure.ROBOT_CONTINUE_ON_FAILURE = False
|
|
79
80
|
raise failure
|
|
81
|
+
RUN_KW_REGISTER.register_run_keyword('robotnl', check_precondition.__name__, args_to_process=0, deprecation_warning=False)
|
|
80
82
|
|
|
81
83
|
def check_postcondition(self, *args):
|
|
82
84
|
"""
|
|
@@ -93,6 +95,7 @@ class RobotChecks:
|
|
|
93
95
|
except CheckFailed as failure:
|
|
94
96
|
failure.ROBOT_CONTINUE_ON_FAILURE = False
|
|
95
97
|
raise failure
|
|
98
|
+
RUN_KW_REGISTER.register_run_keyword('robotnl', check_postcondition.__name__, args_to_process=0, deprecation_warning=False)
|
|
96
99
|
|
|
97
100
|
def check_that(self, *args):
|
|
98
101
|
"""
|
|
@@ -113,13 +116,13 @@ class RobotChecks:
|
|
|
113
116
|
|
|
114
117
|
Examples:
|
|
115
118
|
| `Check that` | 3 | `=` | 3 |
|
|
116
|
-
| `Check that` |
|
|
117
|
-
| `Check that` |
|
|
118
|
-
| `Check that` |
|
|
119
|
+
| `Check that` | _Two times_ | 6 | `equals` | 12 |
|
|
120
|
+
| `Check that` | _Two times_ | 5 | `≠` | _Two times_ | 7 |
|
|
121
|
+
| `Check that` | _Earth exists_ |
|
|
119
122
|
|
|
120
123
|
'Two times' in these examples is assumed to be defined as a Robot keyword that takes one
|
|
121
|
-
argument and multiplies it by 2. Check that will pass if the evaluated result of
|
|
122
|
-
equals the fixed expected value 12.
|
|
124
|
+
argument and multiplies it by 2. `Check that` will pass if the evaluated result of _Two
|
|
125
|
+
times_ 6 equals the fixed expected value 12.
|
|
123
126
|
|
|
124
127
|
*Adding time constraints*:\n
|
|
125
128
|
Any check can be extended with an additional timing constraint by adding ``within``
|
|
@@ -127,15 +130,16 @@ class RobotChecks:
|
|
|
127
130
|
the specified time has passed. In the latter case the test case will fail.
|
|
128
131
|
|
|
129
132
|
Example with time constraint:
|
|
130
|
-
| `Check that` |
|
|
133
|
+
| `Check that` | _condition is true_ | within | 1 minute 30 seconds |
|
|
131
134
|
|
|
132
135
|
Elevator example:
|
|
133
|
-
| `Check that` |
|
|
134
|
-
|
|
|
135
|
-
| `Check that` |
|
|
136
|
-
| `Check that` |
|
|
136
|
+
| `Check that` | _elevator doors are closed_ |
|
|
137
|
+
| _Request elevator at floor_ | 3 |
|
|
138
|
+
| `Check that` | _elevator floor_ | `equals` | 3 | within | 20 seconds |
|
|
139
|
+
| `Check that` | _offset to floor level in mm_ | `≤` | 5 | within | 3 seconds |
|
|
137
140
|
"""
|
|
138
141
|
return RobotChecks.__execute_check("Requirement", *args)
|
|
142
|
+
RUN_KW_REGISTER.register_run_keyword('robotnl', check_that.__name__, args_to_process=0, deprecation_warning=False)
|
|
139
143
|
|
|
140
144
|
def check_manual(self, checkRequestText=""):
|
|
141
145
|
"""
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
from robot.libraries.BuiltIn import BuiltIn
|
|
34
34
|
from robot.api.deco import keyword as robot_keyword
|
|
35
|
-
from robot.
|
|
36
|
-
from robot.utils import type_repr
|
|
35
|
+
from robot.utils import type_name
|
|
37
36
|
from robot.running.arguments import TypeConverter
|
|
38
37
|
|
|
39
38
|
from functools import wraps
|
|
@@ -81,7 +80,6 @@ class InlineKeyword(Generic[TypeVar('T')]):
|
|
|
81
80
|
Inline keywords are keywords that are used as argument to other keywords. The keyword will be
|
|
82
81
|
evaluated and its return value used as the actual argument.
|
|
83
82
|
"""
|
|
84
|
-
_name = 'inline keyword'
|
|
85
83
|
|
|
86
84
|
|
|
87
85
|
# work around for Libdoc
|
|
@@ -97,24 +95,21 @@ class InlineKeywordConverter(TypeConverter):
|
|
|
97
95
|
type = InlineKeyword
|
|
98
96
|
type_name = 'InlineKeyword'
|
|
99
97
|
|
|
100
|
-
def __init__(self,
|
|
101
|
-
super().__init__(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
self.converter = None
|
|
105
|
-
else:
|
|
106
|
-
self.type_name = type_repr(used_type)
|
|
107
|
-
self.converter = self.converter_for(types[0], custom_converters, languages)
|
|
98
|
+
def __init__(self, type_info, custom_converters=None, languages=None):
|
|
99
|
+
super().__init__(type_info, custom_converters, languages)
|
|
100
|
+
self.converter = self.converter_for(self.type_info.nested[0])
|
|
101
|
+
self.type_name = f"keyword returning {type_name(self.type_info.nested[0].type)}"
|
|
108
102
|
|
|
109
|
-
def _convert(self, value
|
|
103
|
+
def _convert(self, value):
|
|
110
104
|
if not is_keyword(value):
|
|
111
105
|
raise ValueError
|
|
112
106
|
BuiltIn().log(f"Evaluating argument as keyword [{value}]")
|
|
113
107
|
result = BuiltIn().run_keyword(value)
|
|
114
108
|
BuiltIn().log(f"{value} → {result}")
|
|
115
109
|
if self.converter:
|
|
110
|
+
# Convert the return type of the keyword to the expected type
|
|
116
111
|
try:
|
|
117
|
-
result = self.converter.convert(f"result of: {value}"
|
|
112
|
+
result = self.converter.convert(result, f"result of: {value}")
|
|
118
113
|
except ValueError as err:
|
|
119
114
|
BuiltIn().log(f"Incorrect keyword return type: {err}")
|
|
120
115
|
raise
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = '3.1.0'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
robotframework>=6.0
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
# BSD 3-Clause License
|
|
4
|
-
#
|
|
5
|
-
# Copyright (c) 2021, J. Foederer
|
|
6
|
-
# All rights reserved.
|
|
7
|
-
#
|
|
8
|
-
# Redistribution and use in source and binary forms, with or without
|
|
9
|
-
# modification, are permitted provided that the following conditions are met:
|
|
10
|
-
#
|
|
11
|
-
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
12
|
-
# list of conditions and the following disclaimer.
|
|
13
|
-
#
|
|
14
|
-
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
15
|
-
# this list of conditions and the following disclaimer in the documentation
|
|
16
|
-
# and/or other materials provided with the distribution.
|
|
17
|
-
#
|
|
18
|
-
# 3. Neither the name of the copyright holder nor the names of its
|
|
19
|
-
# contributors may be used to endorse or promote products derived from
|
|
20
|
-
# this software without specific prior written permission.
|
|
21
|
-
#
|
|
22
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
23
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
24
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
25
|
-
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
26
|
-
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
27
|
-
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
28
|
-
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
29
|
-
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
30
|
-
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
31
|
-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
-
|
|
33
|
-
from robot.libraries.BuiltIn import BuiltIn
|
|
34
|
-
from robot.running.arguments import TypeConverter
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class CheckOperator:
|
|
38
|
-
"""
|
|
39
|
-
This class defines a set of commonly used operators for use by 'Check that'
|
|
40
|
-
and other check keywords
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
################################################################################################
|
|
44
|
-
# Generic operators that can work on basically any object type
|
|
45
|
-
def equals(self, lValue, rValue):
|
|
46
|
-
"""Checks whether the left and right side are equal to each other [=]"""
|
|
47
|
-
return OperatorProxy("==").basicOperator(lValue, rValue)
|
|
48
|
-
|
|
49
|
-
def is_less_than(self, lValue, rValue):
|
|
50
|
-
"""Checks whether the left side is less than or smaller than the right side [<]"""
|
|
51
|
-
return OperatorProxy("<").basicOperator(lValue, rValue)
|
|
52
|
-
|
|
53
|
-
def is_greater_than(self, lValue, rValue):
|
|
54
|
-
"""Checks whether the left side is greater than or larger than the right side [>]"""
|
|
55
|
-
return OperatorProxy(">").basicOperator(lValue, rValue)
|
|
56
|
-
|
|
57
|
-
def is_less_than_or_equal_to(self, lValue, rValue):
|
|
58
|
-
"""Checks whether the left side is less than or equal to the right side [≤]"""
|
|
59
|
-
return OperatorProxy("<=").basicOperator(lValue, rValue)
|
|
60
|
-
|
|
61
|
-
def is_greater_than_or_equal_to(self, lValue, rValue):
|
|
62
|
-
"""Checks whether the left side is greater than or equal to the right side [≥]"""
|
|
63
|
-
return OperatorProxy(">=").basicOperator(lValue, rValue)
|
|
64
|
-
|
|
65
|
-
def does_not_equal(self, lValue, rValue):
|
|
66
|
-
"""Checks whether the left side is different from the right side [≠]"""
|
|
67
|
-
return OperatorProxy("!=").basicOperator(lValue, rValue)
|
|
68
|
-
|
|
69
|
-
################################################################################################
|
|
70
|
-
# Operators that work on text items str() or Unicode()
|
|
71
|
-
def contains_text(self, baseString, subString):
|
|
72
|
-
"""Performs a case insensitive check whether the right side is a substring of the left side"""
|
|
73
|
-
try:
|
|
74
|
-
return self.contains_exact_text(baseString.lower(), subString.lower())
|
|
75
|
-
except:
|
|
76
|
-
return False
|
|
77
|
-
|
|
78
|
-
def contains_exact_text(self, baseString, subString):
|
|
79
|
-
"""Performs a case sensitive check whether the right side is a substring of the left side"""
|
|
80
|
-
try:
|
|
81
|
-
return subString in baseString
|
|
82
|
-
except:
|
|
83
|
-
return False
|
|
84
|
-
|
|
85
|
-
def matches_without_case_to(self, leftText, rightText):
|
|
86
|
-
"""Performs a case insensitive check whether the left and right side texts are equal"""
|
|
87
|
-
try:
|
|
88
|
-
return self.matches_with_case_to(leftText.lower(), rightText.lower())
|
|
89
|
-
except:
|
|
90
|
-
return False
|
|
91
|
-
|
|
92
|
-
def matches_with_case_to(self, leftText, rightText):
|
|
93
|
-
"""Performs a case sensitive check whether the left and right side texts are equal"""
|
|
94
|
-
return leftText == rightText
|
|
95
|
-
|
|
96
|
-
def does_not_contain_text(self, baseString, subString):
|
|
97
|
-
"""Performs a case insensitive check whether the right side is not a substring of the left side"""
|
|
98
|
-
return not self.contains_text(baseString, subString)
|
|
99
|
-
|
|
100
|
-
def does_not_contain_exact_text(self, baseString, subString):
|
|
101
|
-
"""Performs a case sensitive check whether the right side is not a substring of the left side"""
|
|
102
|
-
return not self.contains_exact_text(baseString, subString)
|
|
103
|
-
|
|
104
|
-
def does_not_match_without_case_to(self, leftText, rightText):
|
|
105
|
-
"""Performs a case insensitive check whether the left and right side texts are different"""
|
|
106
|
-
return not self.matches_without_case_to(leftText, rightText)
|
|
107
|
-
|
|
108
|
-
def does_not_match_with_case_to(self, leftText, rightText):
|
|
109
|
-
"""Performs a case sensitive check whether the left and right side texts are different"""
|
|
110
|
-
return not self.matches_with_case_to(leftText, rightText)
|
|
111
|
-
|
|
112
|
-
################################################################################################
|
|
113
|
-
# Operators that work on lists or other sequences
|
|
114
|
-
def is_empty(self, iterable):
|
|
115
|
-
return len(iterable) == 0
|
|
116
|
-
|
|
117
|
-
def contains(self, iterable, part):
|
|
118
|
-
"""Checks whether part is present in iterable. Uses primitive 'in' operator."""
|
|
119
|
-
return part in iterable
|
|
120
|
-
|
|
121
|
-
def does_not_contain(self, iterable, part):
|
|
122
|
-
"""Checks whether part is present in iterable. Uses primitive 'not in' operator."""
|
|
123
|
-
return part not in iterable
|
|
124
|
-
|
|
125
|
-
def contains_item(self, sequence, part):
|
|
126
|
-
"""
|
|
127
|
-
Checks whether the right side item is part of the sequence on the left side.
|
|
128
|
-
Iterates over the sequence to apply automatic Robot type conversion where applicable.
|
|
129
|
-
"""
|
|
130
|
-
for item in sequence:
|
|
131
|
-
BuiltIn().log(f"Processing '{item}' from list")
|
|
132
|
-
if self.equals(part, item):
|
|
133
|
-
BuiltIn().log("Matched")
|
|
134
|
-
return True
|
|
135
|
-
BuiltIn().log("No match")
|
|
136
|
-
return False
|
|
137
|
-
|
|
138
|
-
def contains_exactly_the_items_from(self, sequence, sequence_right):
|
|
139
|
-
"""
|
|
140
|
-
Checks whether the sequence on the right side contains all items of the left side and vice versa.
|
|
141
|
-
Iterates over sequence on the left and matches each element with a single element on the right.
|
|
142
|
-
"""
|
|
143
|
-
if isinstance(sequence_right, str):
|
|
144
|
-
sequence_right = [sequence_right]
|
|
145
|
-
sequence_right = [*sequence_right]
|
|
146
|
-
for item in sequence:
|
|
147
|
-
item_found_in_parts = False
|
|
148
|
-
BuiltIn().log(f"Processing '{item}' from left side list")
|
|
149
|
-
for i in range(len(sequence_right)):
|
|
150
|
-
if self.equals(item, sequence_right[i]):
|
|
151
|
-
sequence_right.pop(i)
|
|
152
|
-
item_found_in_parts = True
|
|
153
|
-
break
|
|
154
|
-
if not item_found_in_parts:
|
|
155
|
-
BuiltIn().log(f"Item '{item}' from left side is not found in the list on the right side")
|
|
156
|
-
return False
|
|
157
|
-
if len(sequence_right) > 0:
|
|
158
|
-
BuiltIn().log(f"Not all items from right side list are present: {sequence_right}")
|
|
159
|
-
return False
|
|
160
|
-
|
|
161
|
-
return True
|
|
162
|
-
|
|
163
|
-
def does_not_contain_item(self, sequence, part):
|
|
164
|
-
"""
|
|
165
|
-
Checks whether the right side item is not part of the sequence on the left side.
|
|
166
|
-
Iterates over the sequence to apply automatic Robot type conversion where applicable.
|
|
167
|
-
"""
|
|
168
|
-
return not self.contains_item(sequence, part)
|
|
169
|
-
|
|
170
|
-
# Add operator keywords that do not comply to Python's identifier syntax
|
|
171
|
-
setattr(CheckOperator, "=", CheckOperator.equals)
|
|
172
|
-
setattr(CheckOperator, "<", CheckOperator.is_less_than)
|
|
173
|
-
setattr(CheckOperator, ">", CheckOperator.is_greater_than)
|
|
174
|
-
setattr(CheckOperator, "≤", CheckOperator.is_less_than_or_equal_to)
|
|
175
|
-
setattr(CheckOperator, "≥", CheckOperator.is_greater_than_or_equal_to)
|
|
176
|
-
setattr(CheckOperator, "≠", CheckOperator.does_not_equal)
|
|
177
|
-
|
|
178
|
-
class OperatorProxy:
|
|
179
|
-
"""
|
|
180
|
-
Proxy class for mapping generic Robot comparison keywords to Python operators
|
|
181
|
-
"""
|
|
182
|
-
def __init__(self, s_operator):
|
|
183
|
-
self.__s_Operator = s_operator
|
|
184
|
-
|
|
185
|
-
@staticmethod
|
|
186
|
-
def __typeCastRobotStringValue(leadingValue, otherValue, name):
|
|
187
|
-
"""
|
|
188
|
-
When Robot files are parsed, arguments are always passed as string even when comparing
|
|
189
|
-
numbers or other objects. When a string argument is detected the other argument is
|
|
190
|
-
deemed leading. This function converts the other argument to the leading value's type
|
|
191
|
-
when possible. Otherwise the value is kept unchanged, inevitably leading to a mismatch
|
|
192
|
-
in comparison.
|
|
193
|
-
|
|
194
|
-
Precondition: otherValue is of types str and supports .casefold()
|
|
195
|
-
returns type casted otherValue
|
|
196
|
-
"""
|
|
197
|
-
CastedOther = otherValue # By default leave untouched
|
|
198
|
-
converter = TypeConverter.converter_for(type(leadingValue))
|
|
199
|
-
if converter:
|
|
200
|
-
try:
|
|
201
|
-
CastedOther = converter.convert(name, otherValue, explicit_type=False)
|
|
202
|
-
except ValueError as err:
|
|
203
|
-
BuiltIn().log(err, level='DEBUG')
|
|
204
|
-
BuiltIn().log(f"Comparing as {converter.type_name} values")
|
|
205
|
-
|
|
206
|
-
if isinstance(CastedOther, str):
|
|
207
|
-
# By default compare as case insensitive Unicode. Note that it already was a string.
|
|
208
|
-
BuiltIn().log(f"Interpreting {name} '{otherValue}' as string (case insensitive)")
|
|
209
|
-
CastedOther = str(otherValue).casefold()
|
|
210
|
-
|
|
211
|
-
return CastedOther
|
|
212
|
-
|
|
213
|
-
def basicOperator(self, lvalue, rvalue):
|
|
214
|
-
# Local argument copies for assignment
|
|
215
|
-
lValue = lvalue
|
|
216
|
-
rValue = rvalue
|
|
217
|
-
|
|
218
|
-
if type(lvalue) is str:
|
|
219
|
-
lValue = OperatorProxy.__typeCastRobotStringValue(rvalue, lvalue, "left operand")
|
|
220
|
-
|
|
221
|
-
if type(rvalue) is str:
|
|
222
|
-
rValue = OperatorProxy.__typeCastRobotStringValue(lvalue, rvalue, "right operand")
|
|
223
|
-
|
|
224
|
-
if lValue is lvalue and rValue is rvalue:
|
|
225
|
-
BuiltIn().log("Comparing values as is")
|
|
226
|
-
|
|
227
|
-
return eval(f"lValue {self.__s_Operator} rValue")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = '2.2.1'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotframework_nl.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{robotframework-nl-2.2.1 → robotframework_nl-3.1.0}/robotframework_nl.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|