Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new: added run_id to reports and to the ctx object #40

Merged
merged 3 commits into from
Jul 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/pytest_quilla/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import uuid

import pytest
from _pytest.config import Config
from _pytest.config.argparsing import Parser
Expand Down Expand Up @@ -37,4 +39,7 @@ def pytest_load_initial_conftests(early_config: Config, parser: Parser):


def pytest_collect_file(parent: pytest.Session, path):
return collect_file(parent, path, parent.config.getini('quilla-prefix'))
return collect_file(parent, path, parent.config.getini('quilla-prefix'), run_id)


run_id = str(uuid.uuid4())
25 changes: 21 additions & 4 deletions src/pytest_quilla/pytest_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
from quilla.reports.report_summary import ReportSummary


def collect_file(parent: pytest.Session, path: LocalPath, prefix: str):
def collect_file(parent: pytest.Session, path: LocalPath, prefix: str, run_id: str):
'''
Collects files if their path ends with .json and starts with the prefix
Args:
parent: The session object performing the collection
path: The path to the file that might be collected
prefix: The prefix for files that should be collected
run_id: The run ID of the quilla tests
Returns:
A quilla file object if the path matches, None otherwise
Expand All @@ -26,10 +27,14 @@ def collect_file(parent: pytest.Session, path: LocalPath, prefix: str):
# TODO: change "path" to be "fspath" when pytest 6.3 is released:
# https://docs.pytest.org/en/latest/_modules/_pytest/hookspec.html#pytest_collect_file
if path.ext == '.json' and path.basename.startswith(prefix):
return QuillaFile.from_parent(parent, fspath=path)
return QuillaFile.from_parent(parent, fspath=path, run_id=run_id)


class QuillaFile(pytest.File):
def __init__(self, *args, run_id: str = '', **kwargs) -> None:
super().__init__(*args, **kwargs)
self.quilla_run_id = run_id

def collect(self):
'''
Loads the JSON test data from the path and creates the test instance
Expand All @@ -38,13 +43,19 @@ def collect(self):
A quilla item configured from the JSON data
'''
test_data = self.fspath.open().read()
yield QuillaItem.from_parent(self, name=self.fspath.purebasename, test_data=test_data)
yield QuillaItem.from_parent(
self,
name=self.fspath.purebasename,
test_data=test_data,
run_id=self.quilla_run_id
)


class QuillaItem(pytest.Item):
def __init__(self, name: str, parent: QuillaFile, test_data: str):
def __init__(self, name: str, parent: QuillaFile, test_data: str, run_id: str):
super(QuillaItem, self).__init__(name, parent)
self.test_data = test_data
self.quilla_run_id = run_id
json_data = json.loads(test_data)
markers = json_data.get('markers', [])
for marker in markers:
Expand All @@ -59,6 +70,12 @@ def runtest(self):
[*self.config.getoption('--quilla-opts').split(), ''],
str(self.config.rootpath)
)
if not (
'-i' in self.config.getoption('--quilla-opts') or
'--run-id' in self.config.getoption('--quilla-opts')
):
ctx.run_id = self.quilla_run_id

ctx.json = self.test_data
results = execute(ctx)
self.results = results
Expand Down
75 changes: 49 additions & 26 deletions src/quilla/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,20 @@ def make_parser() -> argparse.ArgumentParser: # pragma: no cover
'''
parser = argparse.ArgumentParser(
prog='quilla',
usage='%(prog)s [options] [-f] JSON',
description='''
Program to provide a report of UI validations given a json representation
of the validations or given the filename containing a json document describing
the validations
''',
)

parser.add_argument(
'--version',
action='store_true',
help='Prints the version of the software and quits'
)

parser.add_argument(
'-f',
'--file',
Expand All @@ -48,25 +55,32 @@ def make_parser() -> argparse.ArgumentParser: # pragma: no cover
'json',
help='The json file name or raw json string',
)
parser.add_argument(
'--debug',
action='store_true',
help='Enable debug mode',

config_group = parser.add_argument_group(title='Configuration options')
config_group.add_argument(
'-i',
'--run-id',
action='store',
metavar='run_id',
default=None,
help='A run ID for quilla, if manually passed in.'
'Used to set many quilla tests to have the same run ID'
yucui-avengers marked this conversation as resolved.
Show resolved Hide resolved
)
parser.add_argument(
config_group.add_argument(
'-d',
'--definitions',
action='append',
metavar='file',
help='A file with definitions for the \'Definitions\' context object'
)
config_group.add_argument(
'--driver-dir',
dest='drivers_path',
action='store',
default='.',
help='The directory where browser drivers are stored',
)
parser.add_argument(
'-P',
'--pretty',
action='store_true',
help='Set this flag to have the output be pretty-printed'
)
parser.add_argument(
config_group.add_argument(
'--no-sandbox',
dest='no_sandbox',
action='store_true',
Expand All @@ -75,26 +89,35 @@ def make_parser() -> argparse.ArgumentParser: # pragma: no cover
Useful for running in docker containers'
'''
)
parser.add_argument(
'-d',
'--definitions',
action='append',
metavar='file',
help='A file with definitions for the \'Definitions\' context object'

output_group = parser.add_argument_group(title='Output Options')
output_group.add_argument(
'-P',
'--pretty',
action='store_true',
help='Set this flag to have the output be pretty-printed'
)
parser.add_argument(
output_group.add_argument(
'--indent',
type=int,
default=4,
help='How much space each indent level should have when pretty-printing the report'
)

debug_group = parser.add_argument_group(title='Debug Options')
debug_group.add_argument(
'--debug',
action='store_true',
help='Enable debug mode',
)
debug_group.add_argument(
'-v',
'--verbose',
action='count',
help='Flag to increase the verbosity of the outputs. '
'Log outputs are directed to stderr by default.',
default=0
)
parser.add_argument(
'--version',
action='store_true',
help='Prints the version of the software and quits'
)

return parser

Expand Down Expand Up @@ -257,6 +280,8 @@ def setup_context(args: List[str], plugin_root: str = '.') -> Context:
parsed_args.no_sandbox,
parsed_args.definitions,
logger=logger,
run_id=parsed_args.run_id,
indent=parsed_args.indent,
)

logger.info('Running "quilla_configure" hook')
Expand All @@ -279,8 +304,6 @@ def run():
ctx.logger.debug('Finished generating reports')

out = reports.to_dict()
if ctx._context_data['Outputs']:
out['Outputs'] = ctx._context_data['Outputs']

if ctx.pretty:
print(json.dumps(
Expand Down
29 changes: 26 additions & 3 deletions src/quilla/ctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
NullHandler,
)
import json
import uuid

from pluggy import PluginManager
import pydeepmerge as pdm
Expand All @@ -39,6 +40,7 @@ class Context(DriverHolder):
is_file: Whether a file was originally passed in or if the raw json was passed in
no_sandbox: Whether to pass the '--no-sandbox' arg to Chrome and Edge
logger: An optional configured logger instance.
run_id: A string that uniquely identifies the run of Quilla.
Attributes:
Expand All @@ -54,6 +56,8 @@ class Context(DriverHolder):
no_sandbox: Whether to pass the '--no-sandbox' arg to Chrome and Edge
logger: A logger instance. If None was passed in for the 'logger' argument, will create
one with the default logger.
run_id: A string that uniquely identifies the run of Quilla.
pretty_print_indent: How many spaces to use for indentation when pretty-printing the output
'''
default_context: Optional['Context'] = None
_drivers_path: str
Expand All @@ -65,7 +69,6 @@ class Context(DriverHolder):
r'([a-zA-Z][a-zA-Z0-9_]+)(\.[a-zA-Z_][a-zA-Z0-9_]+)+'
)
_output_browser: str = 'Firefox'
pretty_print_indent: int = 4

def __init__(
self,
Expand All @@ -77,7 +80,9 @@ def __init__(
is_file: bool = False,
no_sandbox: bool = False,
definitions: List[str] = [],
logger: Optional[Logger] = None
logger: Optional[Logger] = None,
run_id: Optional[str] = None,
yucui-avengers marked this conversation as resolved.
Show resolved Hide resolved
indent: int = 4,
):
super().__init__()
self.pm = plugin_manager
Expand All @@ -87,6 +92,7 @@ def __init__(
self.json = json_data
self.is_file = is_file
self.no_sandbox = no_sandbox
self.pretty_print_indent = indent
path = Path(drivers_path)

if logger is None:
Expand All @@ -95,10 +101,22 @@ def __init__(
else:
self.logger = logger

if run_id is None:
self.run_id = str(uuid.uuid4()) # Generate a random UUID
cryptaliagy marked this conversation as resolved.
Show resolved Hide resolved
else:
self.run_id = run_id

self.drivers_path = str(path.resolve())
self._context_data: Dict[str, dict] = {'Validation': {}, 'Outputs': {}, 'Definitions': {}}
self._load_definition_files(definitions)

@property
def outputs(self) -> dict:
'''
A dictionary of all outputs created by the steps for the current Quilla test
'''
return self._context_data['Outputs']

@property
def is_debug(self) -> bool:
'''
Expand Down Expand Up @@ -329,7 +347,9 @@ def get_default_context(
no_sandbox: bool = False,
definitions: List[str] = [],
recreate_context: bool = False,
logger: Optional[Logger] = None
logger: Optional[Logger] = None,
run_id: Optional[str] = None,
indent: int = 4,
) -> Context:
'''
Gets the default context, creating a new one if necessary.
Expand All @@ -350,6 +370,7 @@ def get_default_context(
recreate_context: Whether a new context object should be created or not
logger: An optional logger instance. If None, one will be created
with the NullHandler.
run_id: A string that uniquely identifies the run of Quilla.
Returns
Application context shared for the entire application
Expand All @@ -368,5 +389,7 @@ def get_default_context(
no_sandbox,
definitions,
logger,
run_id,
indent,
)
return Context.default_context
4 changes: 2 additions & 2 deletions src/quilla/reports/base_report.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json
from abc import (
abstractclassmethod,
abstractmethod,
)
from typing import Dict
Expand Down Expand Up @@ -29,7 +28,8 @@ def __init__(self, report_type: ReportType, browser: str, action: UITestActions,
self.msg: str = msg
self.report_type: ReportType = report_type

@abstractclassmethod
@classmethod
@abstractmethod
def from_dict(cls, report: Dict[str, Dict[str, str]]) -> 'BaseReport':
'''
Converts a dictionary report into a valid Report object
Expand Down
Loading