Skip to content

Commit

Permalink
Speedup collection of features
Browse files Browse the repository at this point in the history
* All features treated as local if no URL specific options were provided
  • Loading branch information
elchupanebrej committed Oct 5, 2024
1 parent 6a8cebc commit 2128bae
Show file tree
Hide file tree
Showing 24 changed files with 69 additions and 58 deletions.
4 changes: 2 additions & 2 deletions Features/Load/Scenario search from base url.feature
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ Feature: Feature files search is started from base directory
"""
And File "test_feature.py" with content:
"""python
from pytest_bdd import scenarios
from pytest_bdd import scenarios,FeaturePathType
test = scenarios('Passing.feature')
test = scenarios('Passing.feature', features_path_type=FeaturePathType.URL)
"""
Scenario:
Given File "pytest.ini" with fixture templated content:
Expand Down
2 changes: 1 addition & 1 deletion src/pytest_bdd/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""pytest-bdd public API."""
from pytest_bdd.packaging import get_distribution_version
from pytest_bdd.scenario import scenario, scenarios
from pytest_bdd.scenario import FeaturePathType, scenario, scenarios
from pytest_bdd.steps import given, step, then, when
from pytest_bdd.warning_types import PytestBDDStepDefinitionWarning

Expand Down
47 changes: 33 additions & 14 deletions src/pytest_bdd/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from importlib.util import module_from_spec
from pathlib import Path
from typing import Optional, Tuple, cast
from urllib.parse import urlparse
from uuid import uuid4

from pytest_bdd.compatibility.pytest import Module as PytestModule
from pytest_bdd.scenario import FeaturePathType as PathType
from pytest_bdd.scenario import scenarios
from pytest_bdd.steps import StepHandler
from pytest_bdd.utils import convert_str_to_python_name
Expand All @@ -22,16 +24,16 @@ class FeatureFileModule(Module):
def _getobj(self):
path: Path = self.get_path()
if ".url" == path.suffixes[-1]:
feature_pathlike, base_dir = self.get_feature_pathlike_from_url_file(path)
feature_pathlike, features_path_type, base_dir = self.get_feature_pathlike_from_url_file(path)
elif ".desktop" == path.suffixes[-1]:
feature_pathlike, base_dir = self.get_feature_pathlike_from_desktop_file(path), None
feature_pathlike, features_path_type, base_dir = self.get_feature_pathlike_from_desktop_file(path)
elif ".webloc" == path.suffixes[-1]:
feature_pathlike, base_dir = self.get_feature_pathlike_from_weblock_file(path), None
feature_pathlike, features_path_type, base_dir = self.get_feature_pathlike_from_weblock_file(path)
else:
feature_pathlike, base_dir = path, None
return self._build_test_module(feature_pathlike, base_dir)
feature_pathlike, features_path_type, base_dir = path, PathType.PATH, None
return self._build_test_module(feature_pathlike, features_path_type, base_dir)

def _build_test_module(self, path: Optional[Path], base_dir: Optional[Path]):
def _build_test_module(self, path: Optional[Path], features_path_type: PathType, base_dir: Optional[Path]):
module_name = convert_str_to_python_name(f"{path}_{uuid4()}")

module_spec = ModuleSpec(module_name, None)
Expand All @@ -43,28 +45,45 @@ def _build_test_module(self, path: Optional[Path], base_dir: Optional[Path]):
return_test_decorator=False,
parser_type=getattr(self, "parser_type", None),
features_base_dir=base_dir,
features_path_type=features_path_type,
)

return module

@staticmethod
def get_feature_pathlike_from_url_file(path: Path) -> Tuple[str, Optional[str]]:
def detect_uri_pathtype(path) -> Tuple[str, PathType]:
try:
parsed_url = urlparse(path)
except Exception:
features_path_type = PathType.UNDEFINED
else:
if parsed_url.scheme == "file":
features_path_type = PathType.PATH
path = parsed_url.path
elif parsed_url.scheme:
features_path_type = PathType.URL
else:
features_path_type = PathType.UNDEFINED
return path, features_path_type

@classmethod
def get_feature_pathlike_from_url_file(cls, path: Path) -> Tuple[str, PathType, Optional[str]]:
config_parser = ConfigParser()
config_parser.read(path)

config_data = config_parser["InternetShortcut"]
working_dir = config_data.get("WorkingDirectory", None)
url = config_data.get("URL", None)
return url, working_dir
return *cls.detect_uri_pathtype(url), working_dir # type: ignore[return-value]

@staticmethod
def get_feature_pathlike_from_desktop_file(path: Path) -> Optional[str]:
@classmethod
def get_feature_pathlike_from_desktop_file(cls, path: Path) -> Optional[str]:
config_parser = ConfigParser()
config_parser.read(path)

config_data = config_parser["Desktop Entry"]
return config_data["URL"] if config_data["Type"] == "Link" else None
return *cls.detect_uri_pathtype(config_data["URL"] if config_data["Type"] == "Link" else None), None # type: ignore[return-value]

@staticmethod
def get_feature_pathlike_from_weblock_file(path: Path) -> str:
return cast(str, webloc_read(str(path)))
@classmethod
def get_feature_pathlike_from_weblock_file(cls, path: Path) -> str:
return *cls.detect_uri_pathtype(cast(str, webloc_read(str(path)))), None # type: ignore[return-value]
23 changes: 8 additions & 15 deletions src/pytest_bdd/scenario.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
"""Scenario implementation.
The pytest will collect the test case and the steps will be executed
line by line.
Example:
test_publish_article = scenario(
feature_name="publish_article.feature",
scenario_name="Publishing the article",
)
"""
import collections
from enum import Enum
from pathlib import Path
Expand Down Expand Up @@ -73,7 +61,7 @@ def scenario(
encoding: str = "utf-8",
features_base_dir: Optional[Union[Path, str]] = None,
features_base_url=None,
features_path_type: Optional[Union[FeaturePathType, str]] = None,
features_path_type: Optional[Union[FeaturePathType, str]] = FeaturePathType.PATH,
features_mimetype: Optional[Mimetype] = None,
return_test_decorator=True,
parser_type: Optional[Type[ParserProtocol]] = None,
Expand Down Expand Up @@ -117,7 +105,7 @@ def scenarios(
encoding: str = "utf-8",
features_base_dir: Optional[Union[Path, str]] = None,
features_base_url: Optional[str] = None,
features_path_type: Optional[Union[FeaturePathType, str]] = None,
features_path_type: Optional[Union[FeaturePathType, str]] = FeaturePathType.PATH,
features_mimetype: Optional[Mimetype] = None,
parser_type: Optional[Type[ParserProtocol]] = None,
parse_args=Args((), {}),
Expand All @@ -135,11 +123,16 @@ def scenarios(
:param features_mimetype: Helps to select appropriate parser if non-standard file extension is used
:param return_test_decorator; Return test decorator or generated test
:param parser_type: Parser used to parse feature-like file
:param parser_type: Parser used to parse feature-like file
:param parse_args: args consumed by parser during parsing
:param return_test_decorator; Return test decorator or generated test
:param locators: Feature locators to load Features; Could be custom
"""
if features_base_dir and features_base_url:
raise ValueError('Both "features_base_dir" and "features_base_url" were specified')
if features_base_dir:
features_path_type = FeaturePathType.PATH
elif features_base_url:
features_path_type = FeaturePathType.URL

decorator = compose(
mark.pytest_bdd_scenario,
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/Features/Features could be tagged.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Feature tags.feature
URL=file:Feature tags.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Load/Scenario search from base directory.feature
URL=file:Load/Scenario search from base directory.feature
2 changes: 1 addition & 1 deletion tests/e2e/Features/Features load from base url.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Load/Scenario search from base url.feature
URL=file:Load/Scenario search from base url.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Feature tag conversion.feature
URL=file:Feature tag conversion.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Feature and scenario could have descriptions.feature
URL=file:Feature and scenario could have descriptions.feature
2 changes: 1 addition & 1 deletion tests/e2e/Features/Gherkin feature localization.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Feature localization.feature
URL=file:Feature localization.feature
2 changes: 1 addition & 1 deletion tests/e2e/Features/Gherkin features autoload.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Load/Autoload.feature
URL=file:Load/Autoload.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Load/Scenario function loader.feature
URL=file:Load/Scenario function loader.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Step definition/Step utilize fixtures.feature
URL=file:Step definition/Step utilize fixtures.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Step definition/Parameters/Conversion.feature
URL=file:Step definition/Parameters/Conversion.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Step definition/Parameters/Defaults.feature
URL=file:Step definition/Parameters/Defaults.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Step definition/Parameters/Injection as fixtures.feature
URL=file:Step definition/Parameters/Injection as fixtures.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Step definition/Parameters/Parsing by custom parser.feature
URL=file:Step definition/Parameters/Parsing by custom parser.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Step definition/Parameters/Parsing.feature
URL=file:Step definition/Parameters/Parsing.feature
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Steps to step definition bounding.feature
URL=file:Steps to step definition bounding.feature
2 changes: 1 addition & 1 deletion tests/e2e/Features/Gherkin steps used as fixture.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Step definition/Step define target fixtures.feature
URL=file:Step definition/Step define target fixtures.feature
2 changes: 1 addition & 1 deletion tests/e2e/Features/Steps could have data tables.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Steps could have data tables.feature
URL=file:Steps could have data tables.feature
2 changes: 1 addition & 1 deletion tests/e2e/Features/Steps could have doc strings.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Steps could have doc strings.feature
URL=file:Steps could have doc strings.feature
2 changes: 1 addition & 1 deletion tests/e2e/Features/Tutorial launch.desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[Desktop Entry]
Type=Link
URL=Tutorial launch.feature
URL=file:Tutorial launch.feature
13 changes: 6 additions & 7 deletions tests/feature/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_feature_load_by_http(testdir: "Testdir", httpserver: HTTPServer):
testdir.makepyfile(
# language=python
test_http=f"""\
from pytest_bdd import given, scenarios
from pytest_bdd import given, scenarios, FeaturePathType
from pytest_bdd.mimetypes import Mimetype
@given("I have {{cuckes_count}} cukes in my belly")
Expand All @@ -57,7 +57,8 @@ def results(cuckes_count):
test_cuckes = scenarios(
f"http://localhost:{httpserver.port}/feature",
features_mimetype=Mimetype.gherkin_plain
features_mimetype=Mimetype.gherkin_plain,
features_path_type=FeaturePathType.URL
)
"""
)
Expand Down Expand Up @@ -149,14 +150,15 @@ def test_struct_bdd_feature_load_by_http(testdir, httpserver: HTTPServer):
testdir.makepyfile(
# language=python
test_http=f"""\
from pytest_bdd import given, scenarios
from pytest_bdd import given, scenarios, FeaturePathType
@given("I have {{cuckes_count}} cukes in my belly")
def results(cuckes_count):
assert cuckes_count == '42'
test_cuckes = scenarios(
f"http://localhost:{httpserver.port}/feature",
features_path_type=FeaturePathType.URL
)
"""
)
Expand All @@ -174,7 +176,6 @@ def test_feature_load_by_http_with_base_url(testdir, httpserver: HTTPServer):
test_http=f"""\
from pytest_bdd import given, scenarios
from pytest_bdd.mimetypes import Mimetype
from pytest_bdd.scenario import FeaturePathType
@given("I have {{cuckes_count}} cukes in my belly")
def results(cuckes_count):
Expand All @@ -184,7 +185,6 @@ def results(cuckes_count):
f"/feature",
features_mimetype=Mimetype.gherkin_plain,
features_base_url="http://localhost:{httpserver.port}",
features_path_type=FeaturePathType.URL
)
"""
)
Expand All @@ -209,8 +209,7 @@ def test_feature_load_by_http_with_base_url_from_ini(testdir, httpserver: HTTPSe
testdir.makepyfile(
# language=python
test_http=f"""\
from pytest_bdd import given, scenarios
from pytest_bdd.scenario import FeaturePathType
from pytest_bdd import given, scenarios, FeaturePathType
@given("I have {{cuckes_count}} cukes in my belly")
def results(cuckes_count):
Expand Down

0 comments on commit 2128bae

Please sign in to comment.