Skip to content

Commit 87e1dd9

Browse files
committed
feat(discover): rework discover commands
- show statistics on all commands - better differention of tests and tasks - new command `tasks` to show only the tasks - command tests show only the tests - new arguments for `tags` command `--tests` and `--tags` - show the type of test or task in test explorer description
1 parent 3603ff6 commit 87e1dd9

File tree

4 files changed

+167
-62
lines changed

4 files changed

+167
-62
lines changed

.vscode/launch.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"module": "robotcode.cli",
2222
"justMyCode": false,
2323
//"cwd": "${workspaceFolder}/tests/robotcode/language_server/robotframework/parts/data/tests",
24-
"cwd": "${workspaceFolder}/..",
24+
//"cwd": "${workspaceFolder}/..",
25+
"cwd": "E:/source/uvtestprj",
2526
//"cwd": "C:\\develop\\robot\\robotframework",
2627
// "env": {
2728
// "ROBOTCODE_COLOR": "1",
@@ -56,12 +57,13 @@
5657
// ".."
5758
// "config",
5859
// "show",
59-
"repl",
60+
"discover",
61+
"tests"
6062
// "-d", "output",
6163
// "-i",
6264
// "-v", "CMD_LINE_VAR:cmd_line_var",
6365
// "E:\\source\\uvtestprj\\tests\\first.robotrepl"
64-
"./robotcode/language-configuration.json"
66+
6567
]
6668
},
6769
{

packages/runner/src/robotcode/runner/cli/discover/discover.py

Lines changed: 149 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ class TestItem:
216216
range: Optional[Range] = None
217217
tags: Optional[List[str]] = None
218218
error: Optional[str] = None
219+
rpa: Optional[bool] = None
219220

220221

221222
@dataclass
@@ -228,7 +229,9 @@ class ResultItem:
228229
class Statistics:
229230
suites: int = 0
230231
suites_with_tests: int = 0
232+
suites_with_tasks: int = 0
231233
tests: int = 0
234+
tasks: int = 0
232235

233236

234237
def get_rel_source(source: Optional[str]) -> Optional[str]:
@@ -254,7 +257,7 @@ def __init__(self) -> None:
254257
)
255258
self._current = self.all
256259
self.suites: List[TestItem] = []
257-
self.tests: List[TestItem] = []
260+
self.test_and_tasks: List[TestItem] = []
258261
self.tags: Dict[str, List[TestItem]] = defaultdict(list)
259262
self.normalized_tags: Dict[str, List[TestItem]] = defaultdict(list)
260263
self.statistics = Statistics()
@@ -294,6 +297,7 @@ def visit_suite(self, suite: TestSuite) -> None:
294297
),
295298
children=[],
296299
error=suite.error_message if isinstance(suite, ErroneousTestSuite) else None,
300+
rpa=suite.rpa,
297301
)
298302
except ValueError as e:
299303
raise ValueError(f"Error while parsing suite {suite.source}: {e}") from e
@@ -313,7 +317,10 @@ def visit_suite(self, suite: TestSuite) -> None:
313317

314318
self.statistics.suites += 1
315319
if suite.tests:
316-
self.statistics.suites_with_tests += 1
320+
if suite.rpa:
321+
self.statistics.suites_with_tasks += 1
322+
else:
323+
self.statistics.suites_with_tests += 1
317324

318325
def end_suite(self, _suite: TestSuite) -> None:
319326
self._collected.pop()
@@ -332,7 +339,7 @@ def visit_test(self, test: TestCase) -> None:
332339
try:
333340
absolute_path = normalized_path(Path(test.source)) if test.source is not None else None
334341
item = TestItem(
335-
type="test",
342+
type="task" if self._current.rpa else "test",
336343
id=f"{absolute_path or ''};{test.longname};{test.lineno}",
337344
name=test.name,
338345
longname=test.longname,
@@ -344,6 +351,7 @@ def visit_test(self, test: TestCase) -> None:
344351
end=Position(line=test.lineno - 1, character=0),
345352
),
346353
tags=list(set(normalize(str(t), ignore="_") for t in test.tags)) if test.tags else None,
354+
rpa=self._current.rpa,
347355
)
348356
except ValueError as e:
349357
raise ValueError(f"Error while parsing suite {test.source}: {e}") from e
@@ -352,10 +360,12 @@ def visit_test(self, test: TestCase) -> None:
352360
self.tags[str(tag)].append(item)
353361
self.normalized_tags[normalize(str(tag), ignore="_")].append(item)
354362

355-
self.tests.append(item)
363+
self.test_and_tasks.append(item)
356364
self._current.children.append(item)
357-
358-
self.statistics.tests += 1
365+
if self._current.rpa:
366+
self.statistics.tasks += 1
367+
else:
368+
self.statistics.tests += 1
359369

360370

361371
@click.group(invoke_without_command=False)
@@ -543,6 +553,28 @@ def handle_options(
543553
raise UnknownError("Unexpected error happened.")
544554

545555

556+
def print_statistics(app: Application, suite: TestSuite, collector: Collector) -> None:
557+
def print() -> Iterable[str]:
558+
yield click.style("Statistics:", underline=True, fg="blue")
559+
yield os.linesep
560+
yield click.style(" - Suites: ", underline=True, bold=True, fg="blue")
561+
yield f"{collector.statistics.suites}{os.linesep}"
562+
if collector.statistics.suites_with_tests:
563+
yield click.style(" - Suites with tests: ", underline=True, bold=True, fg="blue")
564+
yield f"{collector.statistics.suites_with_tests}{os.linesep}"
565+
if collector.statistics.suites_with_tasks:
566+
yield click.style(" - Suites with tasks: ", underline=True, bold=True, fg="blue")
567+
yield f"{collector.statistics.suites_with_tasks}{os.linesep}"
568+
if collector.statistics.tests:
569+
yield click.style(" - Tests: ", underline=True, bold=True, fg="blue")
570+
yield f"{collector.statistics.tests}{os.linesep}"
571+
if collector.statistics.tasks:
572+
yield click.style(" - Tasks: ", underline=True, bold=True, fg="blue")
573+
yield f"{collector.statistics.tasks}{os.linesep}"
574+
575+
app.echo_via_pager(print())
576+
577+
546578
@discover.command(
547579
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
548580
add_help_option=True,
@@ -551,7 +583,7 @@ def handle_options(
551583
@click.option(
552584
"--tags / --no-tags",
553585
"show_tags",
554-
default=False,
586+
default=True,
555587
show_default=True,
556588
help="Show the tags that are present.",
557589
)
@@ -591,48 +623,70 @@ def all(
591623

592624
if collector.all.children:
593625
if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT:
594-
tests_or_tasks = "Task" if suite.rpa else "Test"
595626

596627
def print(item: TestItem, indent: int = 0) -> Iterable[str]:
597-
type = click.style(
598-
item.type.capitalize() if item.type == "suite" else tests_or_tasks.capitalize(),
599-
fg="green",
600-
)
601-
602-
if item.type == "test":
628+
if item.type in ["test", "task"]:
603629
yield " "
604-
yield type
605-
yield click.style(f": {item.longname}", bold=True)
630+
yield click.style(f"{item.type.capitalize()}: ", fg="blue")
631+
yield click.style(item.longname, bold=True)
606632
yield click.style(
607633
f" ({item.source if full_paths else item.rel_source}"
608634
f":{item.range.start.line + 1 if item.range is not None else 1}){os.linesep}"
609635
)
610636
if show_tags and item.tags:
611-
yield click.style(" Tags:", bold=True, fg="green")
637+
yield click.style(" Tags:", bold=True, fg="yellow")
612638
yield f" {', '. join(normalize(str(tag), ignore='_') for tag in sorted(item.tags))}{os.linesep}"
613639
else:
614-
yield type
615-
yield f": {item.longname}"
640+
yield click.style(f"{item.type.capitalize()}: ", fg="green")
641+
yield click.style(item.longname, bold=True)
616642
yield click.style(f" ({item.source if full_paths else item.rel_source}){os.linesep}")
617643
for child in item.children or []:
618644
yield from print(child, indent + 2)
619645

620-
if indent == 0:
621-
yield os.linesep
622-
623-
yield click.style("Suites: ", underline=True, bold=True, fg="blue")
624-
yield f"{collector.statistics.suites}{os.linesep}"
625-
yield click.style(f"Suites with {tests_or_tasks}: ", underline=True, bold=True, fg="blue")
626-
yield f"{collector.statistics.suites_with_tests}{os.linesep}"
627-
yield click.style(f"{tests_or_tasks}: ", underline=True, bold=True, fg="blue")
628-
yield f"{collector.statistics.tests}{os.linesep}"
629-
630646
app.echo_via_pager(print(collector.all.children[0]))
647+
print_statistics(app, suite, collector)
631648

632649
else:
633650
app.print_data(ResultItem([collector.all], diagnostics), remove_defaults=True)
634651

635652

653+
def _test_or_tasks(
654+
selected_type: str,
655+
app: Application,
656+
full_paths: bool,
657+
show_tags: bool,
658+
by_longname: Tuple[str, ...],
659+
exclude_by_longname: Tuple[str, ...],
660+
robot_options_and_args: Tuple[str, ...],
661+
) -> None:
662+
suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
663+
664+
if collector.all.children:
665+
if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT:
666+
667+
def print(items: List[TestItem]) -> Iterable[str]:
668+
for item in items:
669+
if item.type != selected_type:
670+
continue
671+
672+
yield click.style(f"{item.type.capitalize()}: ", fg="blue")
673+
yield click.style(item.longname, bold=True)
674+
yield click.style(
675+
f" ({item.source if full_paths else item.rel_source}"
676+
f":{item.range.start.line + 1 if item.range is not None else 1}){os.linesep}"
677+
)
678+
if show_tags and item.tags:
679+
yield click.style(" Tags:", bold=True, fg="yellow")
680+
yield f" {', '. join(normalize(str(tag), ignore='_') for tag in sorted(item.tags))}{os.linesep}"
681+
682+
if collector.test_and_tasks:
683+
app.echo_via_pager(print(collector.test_and_tasks))
684+
print_statistics(app, suite, collector)
685+
686+
else:
687+
app.print_data(ResultItem(collector.test_and_tasks, diagnostics), remove_defaults=True)
688+
689+
636690
@discover.command(
637691
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
638692
add_help_option=True,
@@ -677,34 +731,53 @@ def tests(
677731
```
678732
"""
679733

680-
suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
681-
682-
if collector.all.children:
683-
if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT:
734+
_test_or_tasks("test", app, full_paths, show_tags, by_longname, exclude_by_longname, robot_options_and_args)
684735

685-
tests_or_tasks = "Task" if suite.rpa else "Test"
686736

687-
def print(items: List[TestItem]) -> Iterable[str]:
688-
for item in items:
689-
type = click.style(
690-
item.type.capitalize() if item.type == "suite" else tests_or_tasks.capitalize(),
691-
fg="blue",
692-
)
693-
yield type
694-
yield click.style(f": {item.longname}", bold=True)
695-
yield click.style(
696-
f" ({item.source if full_paths else item.rel_source}"
697-
f":{item.range.start.line + 1 if item.range is not None else 1}){os.linesep}"
698-
)
699-
if show_tags and item.tags:
700-
yield click.style(" Tags:", bold=True, fg="green")
701-
yield f" {', '. join(normalize(str(tag), ignore='_') for tag in sorted(item.tags))}{os.linesep}"
737+
@discover.command(
738+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
739+
add_help_option=True,
740+
epilog="Use `-- --help` to see `robot` help.",
741+
)
742+
@click.option(
743+
"--tags / --no-tags",
744+
"show_tags",
745+
default=False,
746+
show_default=True,
747+
help="Show the tags that are present.",
748+
)
749+
@click.option(
750+
"--full-paths / --no-full-paths",
751+
"full_paths",
752+
default=False,
753+
show_default=True,
754+
help="Show full paths instead of releative.",
755+
)
756+
@add_options(*ROBOT_OPTIONS)
757+
@pass_application
758+
def tasks(
759+
app: Application,
760+
full_paths: bool,
761+
show_tags: bool,
762+
by_longname: Tuple[str, ...],
763+
exclude_by_longname: Tuple[str, ...],
764+
robot_options_and_args: Tuple[str, ...],
765+
) -> None:
766+
"""\
767+
Discover tasks with the selected configuration, profiles, options and
768+
arguments.
702769
703-
if collector.tests:
704-
app.echo_via_pager(print(collector.tests))
770+
You can use all known `robot` arguments to filter for example by tags or to use pre-run-modifier.
705771
706-
else:
707-
app.print_data(ResultItem(collector.tests, diagnostics), remove_defaults=True)
772+
\b
773+
Examples:
774+
```
775+
robotcode discover tasks
776+
robotcode --profile regression discover tasks
777+
robotcode --profile regression discover tasks --include regression --exclude wipANDnotready
778+
```
779+
"""
780+
_test_or_tasks("task", app, full_paths, show_tags, by_longname, exclude_by_longname, robot_options_and_args)
708781

709782

710783
@discover.command(
@@ -743,7 +816,7 @@ def suites(
743816
```
744817
"""
745818

746-
_suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
819+
suite, collector, diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
747820

748821
if collector.all.children:
749822
if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT:
@@ -760,6 +833,8 @@ def print(items: List[TestItem]) -> Iterable[str]:
760833
if collector.suites:
761834
app.echo_via_pager(print(collector.suites))
762835

836+
print_statistics(app, suite, collector)
837+
763838
else:
764839
app.print_data(ResultItem(collector.suites, diagnostics), remove_defaults=True)
765840

@@ -788,6 +863,13 @@ class TagsResult:
788863
show_default=True,
789864
help="Show tests where the tag is present.",
790865
)
866+
@click.option(
867+
"--tasks / --no-tasks",
868+
"show_tasks",
869+
default=False,
870+
show_default=True,
871+
help="Show tasks where the tag is present.",
872+
)
791873
@click.option(
792874
"--full-paths / --no-full-paths",
793875
"full_paths",
@@ -801,6 +883,7 @@ def tags(
801883
app: Application,
802884
normalized: bool,
803885
show_tests: bool,
886+
show_tasks: bool,
804887
full_paths: bool,
805888
by_longname: Tuple[str, ...],
806889
exclude_by_longname: Tuple[str, ...],
@@ -822,7 +905,7 @@ def tags(
822905
```
823906
"""
824907

825-
_suite, collector, _diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
908+
suite, collector, _diagnostics = handle_options(app, by_longname, exclude_by_longname, robot_options_and_args)
826909

827910
if collector.all.children:
828911
if app.config.output_format is None or app.config.output_format == OutputFormat.TEXT:
@@ -832,18 +915,26 @@ def print(tags: Dict[str, List[TestItem]]) -> Iterable[str]:
832915
yield click.style(
833916
f"{tag}{os.linesep}",
834917
bold=show_tests,
835-
fg="green" if show_tests else None,
918+
fg="yellow" if show_tests else None,
836919
)
837-
if show_tests:
920+
if show_tests or show_tasks:
838921
for t in items:
839-
yield click.style(f" {t.longname}", bold=True) + click.style(
922+
if show_tests != show_tasks:
923+
if show_tests and t.type != "test":
924+
continue
925+
if show_tasks and t.type != "task":
926+
continue
927+
yield click.style(f" {t.type.capitalize()}: ", fg="blue")
928+
yield click.style(t.longname, bold=True) + click.style(
840929
f" ({t.source if full_paths else t.rel_source}"
841930
f":{t.range.start.line + 1 if t.range is not None else 1}){os.linesep}"
842931
)
843932

844933
if collector.normalized_tags:
845934
app.echo_via_pager(print(collector.normalized_tags if normalized else collector.tags))
846935

936+
print_statistics(app, suite, collector)
937+
847938
else:
848939
app.print_data(TagsResult(collector.normalized_tags), remove_defaults=True)
849940

0 commit comments

Comments
 (0)