Skip to content

Commit e052f15

Browse files
authored
Add OrderSettings transformer (#101)
* add transformer OrderSettings
1 parent 3da34f3 commit e052f15

File tree

11 files changed

+421
-1
lines changed

11 files changed

+421
-1
lines changed

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
- New RemoveEmptySettings transformer for removing empty settings such like `Suite Setup` or `[Arguments]`. Settings that are overwriting suite settings (like empty `[Tags]` overwriting `Default Tags`) are preserved. See the docs for config options [#78](https://github.com/MarketSquare/robotframework-tidy/issues/78)
1313
- New SmartSortKeywords transformer (disabled by default) for sorting out keywords inside ``*** Keywords ***`` section [#52](https://github.com/MarketSquare/robotframework-tidy/issues/52)
1414
- New MergeAndOrderSections transformer for merging duplicated sections and ordering them (order is configurable) [#70](https://github.com/MarketSquare/robotframework-tidy/issues/70)
15-
-
15+
- New OrderSettings transformer for ordering settings like [Arguments], [Setup], [Return] inside Keywords and Test Cases [#59](https://github.com/MarketSquare/robotframework-tidy/issues/59)
1616

1717
### Features
1818
- New option ``--configure`` or ``-c`` for configuring transformer parameters. It works the same way configuring through ``--transform`` works. The benefit of using ``--configure`` is that you can configure selected transformers and still run all transformers [#96](https://github.com/MarketSquare/robotframework-tidy/issues/96)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import click
2+
from robot.api.parsing import (
3+
ModelTransformer,
4+
EmptyLine,
5+
Token
6+
)
7+
8+
from robotidy.decorators import check_start_end_line
9+
10+
11+
class OrderSettings(ModelTransformer):
12+
"""
13+
Order settings like [Arguments], [Setup], [Return] inside Keywords and Test Cases.
14+
15+
Keyword settings [Documentation], [Tags], [Timeout], [Arguments] are put before keyword body and
16+
settings like [Teardown], [Return] are moved to the end of keyword::
17+
18+
*** Keywords ***
19+
Keyword
20+
[Teardown] Keyword
21+
[Return] ${value}
22+
[Arguments] ${arg}
23+
[Documentation] this is
24+
... doc
25+
[Tags] sanity
26+
Pass
27+
28+
To::
29+
30+
*** Keywords ***
31+
Keyword
32+
[Documentation] this is
33+
... doc
34+
[Tags] sanity
35+
[Arguments] ${arg}
36+
Pass
37+
[Teardown] Keyword
38+
[Return] ${value}
39+
40+
Test case settings [Documentation], [Tags], [Template], [Timeout], [Setup] are put before test case body and
41+
[Teardown] is moved to the end of test case.
42+
43+
Default order can be changed using following parameters:
44+
- ``keyword_before = documentation,tags,timeout,arguments``
45+
- ``keyword_after = teardown,return``
46+
- ``test_before = documentation,tags,template,timeout,setup
47+
- ``test_after = teardown
48+
49+
Not all settings names need to be passed to given parameter. Missing setting names are not ordered. Example::
50+
51+
robotidy --configure OrderSettings:keyword_before=:keyword_after=
52+
53+
It will order only test cases because all setting names for keywords are missing.
54+
55+
Supports global formatting params: ``--startline`` and ``--endline``.
56+
"""
57+
def __init__(self, keyword_before: str = None, keyword_after: str = None, test_before: str = None,
58+
test_after: str = None):
59+
self.keyword_before, self.keyword_after, self.test_before, self.test_after = self.parse_order(
60+
keyword_before,
61+
keyword_after,
62+
test_before,
63+
test_after
64+
)
65+
self.keyword_settings = {
66+
*self.keyword_before,
67+
*self.keyword_after
68+
}
69+
self.test_settings = {
70+
*self.test_before,
71+
*self.test_after
72+
}
73+
74+
@staticmethod
75+
def get_order(order, default, name_map):
76+
if order is None:
77+
return default
78+
if not order:
79+
return []
80+
splitted = order.lower().split(',')
81+
parsed_order = []
82+
try:
83+
for split in splitted:
84+
parsed_order.append(name_map[split])
85+
return parsed_order
86+
except KeyError:
87+
raise click.BadOptionUsage(
88+
option_name='transform',
89+
message=f"Invalid configurable value: '{order}' for order for OrderSettings transformer."
90+
f" Custom order should be provided in comma separated list with valid setting names:\n"
91+
f"{sorted(name_map.keys())}")
92+
93+
def parse_order(self, keword_before, keyword_after, test_before, test_after):
94+
keyword_order_before = (
95+
Token.DOCUMENTATION,
96+
Token.TAGS,
97+
Token.TIMEOUT,
98+
Token.ARGUMENTS,
99+
)
100+
keyword_order_after = (
101+
Token.TEARDOWN,
102+
Token.RETURN,
103+
)
104+
testcase_order_before = (
105+
Token.DOCUMENTATION,
106+
Token.TAGS,
107+
Token.TEMPLATE,
108+
Token.TIMEOUT,
109+
Token.SETUP
110+
)
111+
testcase_order_after = (
112+
Token.TEARDOWN,
113+
)
114+
keyword_map = {
115+
'documentation': Token.DOCUMENTATION,
116+
'tags': Token.TAGS,
117+
'timeout': Token.TIMEOUT,
118+
'arguments': Token.ARGUMENTS,
119+
'return': Token.RETURN,
120+
'teardown': Token.TEARDOWN
121+
}
122+
test_map = {
123+
'documentation': Token.DOCUMENTATION,
124+
'tags': Token.TAGS,
125+
'timeout': Token.TIMEOUT,
126+
'template': Token.TEMPLATE,
127+
'setup': Token.SETUP,
128+
'teardown': Token.TEARDOWN
129+
}
130+
return (self.get_order(keword_before, keyword_order_before, keyword_map),
131+
self.get_order(keyword_after, keyword_order_after, keyword_map),
132+
self.get_order(test_before, testcase_order_before, test_map),
133+
self.get_order(test_after, testcase_order_after, test_map))
134+
135+
@check_start_end_line
136+
def visit_Keyword(self, node): # noqa
137+
return self.order_settings(node, self.keyword_settings, self.keyword_before, self.keyword_after)
138+
139+
@check_start_end_line
140+
def visit_TestCase(self, node): # noqa
141+
return self.order_settings(node, self.test_settings, self.test_before, self.test_after)
142+
143+
@staticmethod
144+
def order_settings(node, setting_types, before, after):
145+
if not node.body:
146+
return node
147+
settings = dict()
148+
rest = []
149+
new_body = []
150+
for child in node.body:
151+
if getattr(child, 'type', 'invalid') in setting_types:
152+
settings[child.type] = child
153+
else:
154+
rest.append(child)
155+
trailing_empty = []
156+
while rest and isinstance(rest[-1], EmptyLine):
157+
trailing_empty.append(rest.pop())
158+
for token_type in before:
159+
if token_type in settings:
160+
new_body.append(settings[token_type])
161+
new_body.extend(rest)
162+
for token_type in after:
163+
if token_type in settings:
164+
new_body.append(settings[token_type])
165+
new_body.extend(trailing_empty)
166+
node.body = new_body
167+
return node

robotidy/transformers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
'MergeAndOrderSections',
1919
'RemoveEmptySettings',
2020
'AssignmentNormalizer',
21+
'OrderSettings',
2122
'AlignSettingsSection',
2223
'AlignVariablesSection',
2324
'NormalizeNewLines',
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
*** Test Cases ***
2+
Test case 1
3+
Keyword
4+
Keyword
5+
[Documentation] this is
6+
... doc
7+
[Tags]
8+
... tag
9+
[Setup] Setup # comment
10+
[Teardown] Teardown
11+
12+
Test case 2
13+
Keyword
14+
[Timeout] timeout2 # this is error because it is duplicate
15+
[Template] Template
16+
[Timeout] timeout
17+
18+
*** Keywords ***
19+
Keyword
20+
Keyword
21+
No Operation
22+
IF ${condition}
23+
Log ${stuff}
24+
END
25+
FOR ${var} IN 1 2
26+
Log ${var}
27+
END
28+
Pass
29+
[Documentation] this is
30+
... doc
31+
[Tags] sanity
32+
[Arguments] ${arg}
33+
[Teardown] Keyword
34+
[Return] ${value}
35+
36+
Another Keyword ${var}
37+
No Operation
38+
[Timeout]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
*** Test Cases ***
2+
Test case 1
3+
[Documentation] this is
4+
... doc
5+
[Tags]
6+
... tag
7+
[Setup] Setup # comment
8+
Keyword
9+
Keyword
10+
[Teardown] Teardown
11+
12+
Test case 2
13+
[Template] Template
14+
[Timeout] timeout
15+
Keyword
16+
[Timeout] timeout2 # this is error because it is duplicate
17+
18+
*** Keywords ***
19+
Keyword
20+
[Documentation] this is
21+
... doc
22+
[Tags] sanity
23+
[Arguments] ${arg}
24+
Keyword
25+
No Operation
26+
IF ${condition}
27+
Log ${stuff}
28+
END
29+
FOR ${var} IN 1 2
30+
Log ${var}
31+
END
32+
Pass
33+
[Teardown] Keyword
34+
[Return] ${value}
35+
36+
Another Keyword ${var}
37+
[Timeout]
38+
No Operation
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
*** Test Cases ***
2+
Test case 1
3+
[Documentation] this is
4+
... doc
5+
[Tags]
6+
... tag
7+
[Setup] Setup # comment
8+
[Teardown] Teardown
9+
Keyword
10+
Keyword
11+
12+
Test case 2
13+
[Template] Template
14+
[Timeout] timeout
15+
Keyword
16+
[Timeout] timeout2 # this is error because it is duplicate
17+
18+
*** Keywords ***
19+
Keyword
20+
[Documentation] this is
21+
... doc
22+
[Tags] sanity
23+
[Arguments] ${arg}
24+
Keyword
25+
No Operation
26+
IF ${condition}
27+
Log ${stuff}
28+
END
29+
FOR ${var} IN 1 2
30+
Log ${var}
31+
END
32+
Pass
33+
[Teardown] Keyword
34+
[Return] ${value}
35+
36+
Another Keyword ${var}
37+
[Timeout]
38+
No Operation
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
*** Test Cases ***
2+
Test case 1
3+
[Documentation] this is
4+
... doc
5+
[Tags]
6+
... tag
7+
[Setup] Setup # comment
8+
Keyword
9+
Keyword
10+
[Teardown] Teardown
11+
12+
Test case 2
13+
[Template] Template
14+
[Timeout] timeout
15+
Keyword
16+
[Timeout] timeout2 # this is error because it is duplicate
17+
18+
*** Keywords ***
19+
Keyword
20+
[Documentation] this is
21+
... doc
22+
[Tags] sanity
23+
[Arguments] ${arg}
24+
Keyword
25+
No Operation
26+
IF ${condition}
27+
Log ${stuff}
28+
END
29+
FOR ${var} IN 1 2
30+
Log ${var}
31+
END
32+
Pass
33+
[Teardown] Keyword
34+
[Return] ${value}
35+
36+
Another Keyword ${var}
37+
[Timeout]
38+
No Operation
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
*** Test Cases ***
2+
Test case 1
3+
[Documentation] this is
4+
... doc
5+
[Teardown] Teardown
6+
Keyword
7+
[Tags]
8+
... tag
9+
[Setup] Setup # comment
10+
Keyword
11+
12+
Test case 2
13+
[Template] Template
14+
Keyword
15+
[Timeout] timeout
16+
[Timeout] timeout2 # this is error because it is duplicate
17+
18+
*** Keywords ***
19+
Keyword
20+
[Teardown] Keyword
21+
[Return] ${value}
22+
[Arguments] ${arg}
23+
[Documentation] this is
24+
... doc
25+
[Tags] sanity
26+
Keyword
27+
No Operation
28+
IF ${condition}
29+
Log ${stuff}
30+
END
31+
FOR ${var} IN 1 2
32+
Log ${var}
33+
END
34+
Pass
35+
36+
Another Keyword ${var}
37+
No Operation
38+
[Timeout]

tests/atest/transformers/conftest.py

Whitespace-only changes.

0 commit comments

Comments
 (0)