Coverage for src/pytest_xray/plugin.py: 96.0%

75 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-16 14:00 +0000

1# Modified in the parameter-details prototype; see NOTICE. 

2import os 

3from pathlib import Path 

4from typing import Union 

5 

6import pytest 

7from requests.auth import AuthBase 

8 

9from pytest_xray import hooks 

10from pytest_xray.case_report import CaseReportPlugin 

11from pytest_xray.comment_limits import DEFAULT_COMMENT_LIMIT 

12from pytest_xray.constant import ( 

13 JIRA_API_KEY, 

14 JIRA_CLIENT_SECRET_AUTH, 

15 JIRA_CLOUD, 

16 JIRA_XRAY_FLAG, 

17 TEST_EXECUTION_ENDPOINT, 

18 TEST_EXECUTION_ENDPOINT_CLOUD, 

19 XRAY_ADD_CAPTURES, 

20 XRAY_ALLOW_DUPLICATE_IDS, 

21 XRAY_EXECUTION_ID, 

22 XRAY_PLUGIN, 

23 XRAY_TEST_PLAN_ID, 

24 XRAYPATH, 

25) 

26from pytest_xray.file_publisher import FilePublisher 

27from pytest_xray.helper import get_api_key_auth, get_basic_auth, get_bearer_auth 

28from pytest_xray.xray_plugin import Publisher, XrayPlugin 

29from pytest_xray.xray_publisher import ApiKeyAuth, ClientSecretAuth, XrayPublisher 

30 

31 

32def pytest_addoption(parser: pytest.Parser) -> None: 

33 xray = parser.getgroup('Jira Xray report') 

34 xray.addoption( 

35 '--xray-comment-limit', 

36 type=int, 

37 default=DEFAULT_COMMENT_LIMIT, 

38 help='Maximum UTF-8 bytes per exported comment (default: 32000; minimum: 256)', 

39 ) 

40 xray.addoption( 

41 '--xray-full-report', 

42 metavar='path', 

43 default=None, 

44 help='Retain complete legacy Xray JSON locally before bounding comments', 

45 ) 

46 xray.addoption( 

47 '--xray-case-report', 

48 metavar='path', 

49 default=None, 

50 help='Write a transport-independent offline JSON case report', 

51 ) 

52 xray.addoption( 

53 '--xray-case-values', 

54 action='store_true', 

55 default=False, 

56 help='Opt in to potentially sensitive scalar values in the offline case report', 

57 ) 

58 xray.addoption( 

59 '--xray-case-redact-identities', 

60 action='store_true', 

61 default=False, 

62 help='Replace offline node and parameter IDs with per-run opaque aliases', 

63 ) 

64 xray.addoption( 

65 '--xray-parameter-details', 

66 action='store_true', 

67 default=False, 

68 help='Include parameter case identity, values and phase outcomes in test run comments', 

69 ) 

70 xray.addoption(JIRA_XRAY_FLAG, action='store_true', default=False, help='Upload test results to JIRA XRAY') 

71 xray.addoption(JIRA_CLOUD, action='store_true', default=False, help='Use with JIRA XRAY cloud server') 

72 xray.addoption( 

73 JIRA_API_KEY, 

74 action='store_true', 

75 default=False, 

76 help='Use Jira API Key authentication', 

77 ) 

78 xray.addoption( 

79 JIRA_CLIENT_SECRET_AUTH, 

80 action='store_true', 

81 default=False, 

82 help='Use client secret authentication', 

83 ) 

84 xray.addoption( 

85 XRAY_EXECUTION_ID, action='store', metavar='ExecutionId', default=None, help='XRAY Test Execution ID' 

86 ) 

87 xray.addoption(XRAY_TEST_PLAN_ID, action='store', metavar='TestplanId', default=None, help='XRAY Test Plan ID') 

88 xray.addoption( 

89 XRAYPATH, 

90 action='store', 

91 metavar='path', 

92 default=None, 

93 help='Do not upload to a server but create JSON report file at given path', 

94 ) 

95 xray.addoption( 

96 XRAY_ALLOW_DUPLICATE_IDS, 

97 action='store_true', 

98 default=False, 

99 help='Allow test ids to be present on multiple pytest tests', 

100 ) 

101 xray.addoption( 

102 XRAY_ADD_CAPTURES, 

103 action='store_true', 

104 default=False, 

105 help='Add captures from log, stdout or/and stderr, to the report comment field', 

106 ) 

107 

108 

109def pytest_addhooks(pluginmanager): 

110 pluginmanager.add_hookspecs(hooks) 

111 

112 

113def pytest_configure(config: pytest.Config) -> None: 

114 config.addinivalue_line('markers', 'xray(JIRA_ID): mark test with JIRA XRAY test case ID') 

115 if config.option.collectonly: 

116 return 

117 

118 full_path = config.getoption('--xray-full-report') 

119 if config.getoption('--xray-comment-limit') < 256: 

120 raise pytest.UsageError('--xray-comment-limit must be at least 256 bytes') 

121 if full_path and not config.getoption(JIRA_XRAY_FLAG): 

122 raise pytest.UsageError('--xray-full-report requires --jira-xray') 

123 paths = [(config.getoption('--xray-case-report'), False), (config.getoption(XRAYPATH), True), (full_path, True)] 

124 normalized = [Path(os.path.expandvars(p) if expand else p).expanduser().resolve() for p, expand in paths if p] 

125 if len(set(normalized)) != len(normalized): 

126 raise pytest.UsageError('Report paths must be different: --xray-case-report, --xraypath, --xray-full-report') 

127 case_path = config.getoption('--xray-case-report') 

128 redact_ids = config.getoption('--xray-case-redact-identities') 

129 if redact_ids and not case_path: 129 ↛ 130line 129 didn't jump to line 130 because the condition on line 129 was never true

130 raise pytest.UsageError('--xray-case-redact-identities requires --xray-case-report') 

131 if redact_ids and config.getoption('--xray-case-values'): 

132 raise pytest.UsageError('--xray-case-redact-identities cannot be combined with --xray-case-values') 

133 if config.getoption('--xray-case-values') and not case_path: 

134 raise pytest.UsageError('--xray-case-values requires --xray-case-report') 

135 if case_path: 

136 xray_path = config.getoption(XRAYPATH) 

137 if ( 137 ↛ 141line 137 didn't jump to line 141 because the condition on line 137 was never true

138 xray_path 

139 and Path(case_path).expanduser().resolve() == Path(os.path.expandvars(xray_path)).expanduser().resolve() 

140 ): 

141 raise pytest.UsageError('--xray-case-report and --xraypath must use different files') 

142 config.pluginmanager.register( 

143 CaseReportPlugin(config, case_path, config.getoption('--xray-case-values'), redact_ids), 

144 name='xray-offline-cases', 

145 ) 

146 

147 if not config.getoption(JIRA_XRAY_FLAG): 

148 return 

149 

150 xray_path = config.getoption(XRAYPATH) 

151 

152 if xray_path: 

153 publisher: Publisher = FilePublisher(xray_path) 

154 else: 

155 endpoint = TEST_EXECUTION_ENDPOINT_CLOUD if config.getoption(JIRA_CLOUD) else TEST_EXECUTION_ENDPOINT 

156 

157 if config.getoption(JIRA_CLIENT_SECRET_AUTH): 

158 options = get_bearer_auth() 

159 auth: Union[AuthBase, tuple[str, str]] = ClientSecretAuth( 

160 options['BASE_URL'], options['CLIENT_ID'], options['CLIENT_SECRET'], options['VERIFY'] 

161 ) 

162 elif config.getoption(JIRA_API_KEY): 

163 options = get_api_key_auth() 

164 auth = ApiKeyAuth(options['API_KEY']) 

165 else: 

166 options = get_basic_auth() 

167 auth = (options['USER'], options['PASSWORD']) 

168 

169 publisher = XrayPublisher(base_url=options['BASE_URL'], endpoint=endpoint, auth=auth, verify=options['VERIFY']) 

170 

171 plugin = XrayPlugin(config, publisher) 

172 config.pluginmanager.register(plugin=plugin, name=XRAY_PLUGIN)