diff --git a/README.md b/README.md index f89d5ea..11ff633 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,7 @@ You can use environment variable to control certain features of testomat.io | TESTOMATIO_CI_DOWNSTREAM | If set, pytestomatio will not set or update build url for a test run. This is useful in scenarios where build url is already set in the test run by Testomat.io for test runs that a created directly on Testomat.io. | TESTOMATIO_CI_DOWNSTREAM=true pytest --testomatio report | | TESTOMATIO_URL | Customize testomat.io url | TESTOMATIO_URL=https://custom.com/ pytest --testomatio report | | BUILD_URL | Overrides build url run tests | BUILD_URL=http://custom.com/ pytest --testomatio report | + | TESTOMATIO_NO_TIMESTAMP | Disable automatic timestamp generation for test results. Use this option if you run tests in parallel on different machines where time is not synchronized | TESTOMATIO_NO_TIMESTAMP=True pytest --testomatio report | | TESTOMATIO_MAX_REQUEST_FAILURES | Sets the max number of attempts to send a request to the Testomat.io API. Default is 5 attempts. | TESTOMATIO_MAX_REQUEST_FAILURES=10 pytest --testomatio report | | TESTOMATIO_REQUEST_INTERVAL | Sets the interval between API requests in seconds. Default is 5 sec. | TESTOMATIO_REQUEST_INTERVAL=2 pytest --testomatio report | diff --git a/pytestomatio/connect/connector.py b/pytestomatio/connect/connector.py index 73ae847..2f14760 100644 --- a/pytestomatio/connect/connector.py +++ b/pytestomatio/connect/connector.py @@ -254,6 +254,7 @@ def update_test_status(self, run_id: str, test_id: str, message: str, stack: str, + timestamp: float, run_time: float, artifacts: list[str], steps: str, @@ -271,6 +272,7 @@ def update_test_status(self, run_id: str, "test_id": test_id, "message": message, "stack": stack, + "timestamp": timestamp, "run_time": run_time, "example": example, "artifacts": artifacts, diff --git a/pytestomatio/main.py b/pytestomatio/main.py index 6552885..5868244 100644 --- a/pytestomatio/main.py +++ b/pytestomatio/main.py @@ -203,6 +203,7 @@ def pytest_runtest_makereport(item: Item, call: CallInfo): 'artifacts': test_item.artifacts, 'steps': None, 'code': None, + 'timestamp': None, 'overwrite': None, 'rid': rid, 'meta': pytest.testomatio.test_run_config.meta @@ -228,6 +229,9 @@ def pytest_runtest_makereport(item: Item, call: CallInfo): if hasattr(item, 'callspec'): request['example'] = test_item.safe_params(item.callspec.params) + if not pytest.testomatio.test_run_config.disable_timestamp: + request['timestamp'] = time.time() + step_manager = _step_managers.get(item.nodeid) if step_manager: request['steps'] = step_manager.get_steps() diff --git a/pytestomatio/testomatio/testRunConfig.py b/pytestomatio/testomatio/testRunConfig.py index 19960f1..1d78f9b 100644 --- a/pytestomatio/testomatio/testRunConfig.py +++ b/pytestomatio/testomatio/testRunConfig.py @@ -14,6 +14,7 @@ def __init__(self): disable_batch_upload = os.environ.get('TESTOMATIO_DISABLE_BATCH_UPLOAD') in ['True', 'true', '1'] batch_size = os.environ.get('TESTOMATIO_BATCH_SIZE', '') shared_run = os.environ.get('TESTOMATIO_SHARED_RUN') in ['True', 'true', '1'] + disable_timestamp = os.environ.get('TESTOMATIO_NO_TIMESTAMP') in ['True', 'true', '1'] update_code = os.environ.get('TESTOMATIO_UPDATE_CODE', False) in ['True', 'true', '1'] exclude_skipped = os.environ.get('TESTOMATIO_EXCLUDE_SKIPPED', False) in ['True', 'true', '1'] shared_run_timeout = os.environ.get('TESTOMATIO_SHARED_RUN_TIMEOUT', '') @@ -23,6 +24,7 @@ def __init__(self): self.disable_batch = disable_batch_upload self.batch_size = int(batch_size) if (batch_size.isdigit() and int(batch_size) <= 100) else DEFAULT_BATCH_SIZE self.environment = safe_string_list(os.environ.get('TESTOMATIO_ENV')) + self.disable_timestamp = disable_timestamp self.exclude_skipped = exclude_skipped self.label = safe_string_list(os.environ.get('TESTOMATIO_LABEL')) self.group_title = os.environ.get('TESTOMATIO_RUNGROUP_TITLE') diff --git a/tests/test_connect/test_connector.py b/tests/test_connect/test_connector.py index 0e37630..1d3d697 100644 --- a/tests/test_connect/test_connector.py +++ b/tests/test_connect/test_connector.py @@ -430,6 +430,7 @@ def test_update_test_status_success(self, mock_post, connector): test_id="test_789", message=None, stack=None, + timestamp=123222.21, run_time=1.5, artifacts=["screenshot.png"], steps="Step 1\nStep 2", @@ -469,6 +470,7 @@ def test_update_test_status_should_raise_report_failed_on_403_status_code(self, suite_title="Auth Suite", suite_id="suite_456", test_id="test_789", + timestamp=213.5, message=None, stack=None, run_time=1.5, @@ -526,6 +528,7 @@ def test_update_test_status_filters_none_values(self, mock_post, connector): suite_title="Auth Suite", suite_id="suite_456", test_id="test_789", + timestamp=123222.21, message=None, stack=None, run_time=1.5, @@ -548,6 +551,7 @@ def test_update_test_status_filters_none_values(self, mock_post, connector): assert payload['title'] == 'Test Login' assert payload['run_time'] == 1.5 assert payload['artifacts'] == ["screenshot.png"] + assert payload['timestamp'] == 123222.21 assert 'message' not in payload assert 'code' not in payload assert 'overwrite' not in payload diff --git a/tests/test_main.py b/tests/test_main.py index c959bbd..0c99683 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -742,6 +742,72 @@ def test_code_field_for_bdd_when_update_code_option_enabled(self, mock_call, sin assert request['code'] is None assert request['overwrite'] is None + def test_adds_timestamp_to_request(self, mock_call, single_test_item): + """Test timestamp added to request by default""" + item = single_test_item.copy()[0] + item.config.option.testomatio = 'report' + + mock_call.duration = 1.5 + mock_call.when = 'call' + mock_call.excinfo = None + + pytest.testomatio = Mock() + pytest.testomatio.test_run_config = Mock() + pytest.testomatio.test_run_config.disable_timestamp = False + pytest.testomatio.test_run_config.test_run_id = 'run_123' + pytest.testomatio.test_run_config.status_request = {} + + main.pytest_runtest_makereport(item, mock_call) + + assert item.nodeid in pytest.testomatio.test_run_config.status_request + request = pytest.testomatio.test_run_config.status_request[item.nodeid] + + expected_keys = [ + 'status', 'title', 'run_time', 'suite_title', 'suite_id', 'timestamp', + 'test_id', 'message', 'stack', 'example', 'artifacts', 'steps', 'code' + ] + for key in expected_keys: + assert key in request + + assert request['title'] == 'Addition' + assert request['run_time'] == 1.5 + assert request['suite_title'] == item.path.name + assert request['test_id'] == '12345678' + assert request.get('timestamp') is not None + + def test_timestamp_not_updated_when_option_enabled(self, mock_call, single_test_item): + """Test timestamp is None if TESTOMATIO_NO_TIMESTAMP env set""" + item = single_test_item.copy()[0] + item.config.option.testomatio = 'report' + + mock_call.duration = 1.5 + mock_call.when = 'call' + mock_call.excinfo = None + + pytest.testomatio = Mock() + pytest.testomatio.test_run_config = Mock() + pytest.testomatio.test_run_config.disable_timestamp = True + pytest.testomatio.test_run_config.test_run_id = 'run_123' + pytest.testomatio.test_run_config.status_request = {} + + main.pytest_runtest_makereport(item, mock_call) + + assert item.nodeid in pytest.testomatio.test_run_config.status_request + request = pytest.testomatio.test_run_config.status_request[item.nodeid] + + expected_keys = [ + 'status', 'title', 'run_time', 'suite_title', 'suite_id', + 'test_id', 'message', 'stack', 'example', 'artifacts', 'steps', 'code' + ] + for key in expected_keys: + assert key in request + + assert request['title'] == 'Addition' + assert request['run_time'] == 1.5 + assert request['suite_title'] == item.path.name + assert request['test_id'] == '12345678' + assert request.get('timestamp') is None + @pytest.mark.smoke class TestPytestUnconfigure: diff --git a/tests/test_testomatio/test_testRunConfig.py b/tests/test_testomatio/test_testRunConfig.py index e265039..77cc6a2 100644 --- a/tests/test_testomatio/test_testRunConfig.py +++ b/tests/test_testomatio/test_testRunConfig.py @@ -20,6 +20,7 @@ def test_init_default_values(self): assert config.test_run_id is None assert config.title == "test run at 2024-01-15 10:30:45" assert config.environment is None + assert config.disable_timestamp is False assert config.exclude_skipped is False assert config.disable_batch is False assert config.batch_size == DEFAULT_BATCH_SIZE @@ -41,6 +42,7 @@ def test_init_with_env_variables(self): 'TESTOMATIO_ENV': 'linux,browser:chrome,1920x1080', 'TESTOMATIO_LABEL': 'smoke,regression', 'TESTOMATIO_RUNGROUP_TITLE': 'Release 2.0', + 'TESTOMATIO_NO_TIMESTAMP': '1', 'TESTOMATIO_JIRA_ID': 'TES-1', 'TESTOMATIO_UPDATE_CODE': '1', 'TESTOMATIO_PUBLISH': '1', @@ -55,6 +57,7 @@ def test_init_with_env_variables(self): assert config.access_event == 'publish' assert config.test_run_id == 'run_12345' assert config.title == 'Custom Test Run' + assert config.disable_timestamp is True assert config.environment == 'linux,browser:chrome,1920x1080' assert config.exclude_skipped is True assert config.disable_batch is True @@ -87,6 +90,22 @@ def test_init_shared_run_false_variations(self, value): assert config.shared_run_timeout is None assert config.parallel is True + @pytest.mark.parametrize('value', ['True', 'true', '1']) + def test_init_disable_timestamp_true_variations(self, value): + """Test different true values for TESTOMATIO_NO_TIMESTAMP""" + with patch.dict(os.environ, {'TESTOMATIO_NO_TIMESTAMP': value}, clear=True): + config = TestRunConfig() + + assert config.disable_timestamp is True + + @pytest.mark.parametrize('value', ['False', 'false', '0', 'anything']) + def test_init_disable_timestamp_false_variations(self, value): + """Test different false values TESTOMATIO_NO_TIMESTAMP""" + with patch.dict(os.environ, {'TESTOMATIO_NO_TIMESTAMP': value}, clear=True): + config = TestRunConfig() + + assert config.disable_timestamp is False + @pytest.mark.parametrize('value', ['True', 'true', '1']) def test_init_update_code_true_variations(self, value): """Test different true values for TESTOMATIO_UPDATE_CODE"""