qastudio-pytest 1.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,284 @@
1
+ """Utility functions for QAStudio pytest plugin."""
2
+
3
+ import re
4
+ from datetime import datetime
5
+ from typing import Any, List, Optional, TypeVar
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ def extract_test_case_id(item: Any) -> Optional[str]:
11
+ """
12
+ Extract QAStudio test case ID from pytest item.
13
+
14
+ Checks in order:
15
+ 1. @pytest.mark.qastudio_id marker
16
+ 2. Test name pattern (test_QA123_name or test_name_QA123)
17
+ 3. Docstring pattern (QAStudio ID: QA-123)
18
+ """
19
+ # Check for qastudio_id marker
20
+ marker = item.get_closest_marker("qastudio_id")
21
+ if marker and marker.args:
22
+ return str(marker.args[0])
23
+
24
+ # Check test name for ID pattern
25
+ name = item.name
26
+ # Pattern: QA-123 or QA123
27
+ match = re.search(r"QA[-_]?(\d+)", name, re.IGNORECASE)
28
+ if match:
29
+ return f"QA-{match.group(1)}"
30
+
31
+ # Check docstring
32
+ if item.function.__doc__:
33
+ doc = item.function.__doc__
34
+ match = re.search(r"QAStudio\s+ID:\s*(QA[-_]?\d+)", doc, re.IGNORECASE)
35
+ if match:
36
+ test_id = match.group(1).upper()
37
+ # Normalize to QA-123 format
38
+ return re.sub(r"QA[-_]?(\d+)", r"QA-\1", test_id)
39
+
40
+ return None
41
+
42
+
43
+ def get_full_test_name(item: Any) -> str:
44
+ """
45
+ Get full test name including class and module hierarchy.
46
+
47
+ Examples:
48
+ test_example.py::test_function
49
+ test_example.py::TestClass::test_method
50
+ test_example.py::TestClass::test_method[param]
51
+ """
52
+ parts = []
53
+
54
+ # Add module/file name
55
+ if hasattr(item, "fspath"):
56
+ parts.append(item.fspath.basename)
57
+
58
+ # Add class name if exists
59
+ if item.cls:
60
+ parts.append(item.cls.__name__)
61
+
62
+ # Add test function name
63
+ parts.append(item.name)
64
+
65
+ return "::".join(parts)
66
+
67
+
68
+ def generate_test_run_name() -> str:
69
+ """Generate default test run name with timestamp."""
70
+ now = datetime.now()
71
+ date_str = now.strftime("%Y-%m-%d")
72
+ time_str = now.strftime("%H-%M-%S")
73
+ return f"pytest Run - {date_str} {time_str}"
74
+
75
+
76
+ def batch_list(items: List[T], batch_size: int) -> List[List[T]]:
77
+ """Split list into batches of specified size."""
78
+ return [items[i : i + batch_size] for i in range(0, len(items), batch_size)]
79
+
80
+
81
+ def format_duration(seconds: float) -> str:
82
+ """Format duration in human-readable format."""
83
+ if seconds < 1:
84
+ return f"{int(seconds * 1000)}ms"
85
+
86
+ total_seconds = int(seconds)
87
+ hours = total_seconds // 3600
88
+ minutes = (total_seconds % 3600) // 60
89
+ secs = total_seconds % 60
90
+
91
+ if hours > 0:
92
+ return f"{hours}h {minutes}m {secs}s"
93
+ elif minutes > 0:
94
+ return f"{minutes}m {secs}s"
95
+ else:
96
+ return f"{secs}s"
97
+
98
+
99
+ def strip_ansi(text: str) -> str:
100
+ """
101
+ Strip ANSI escape codes from string.
102
+
103
+ Removes color codes and terminal formatting that can interfere with API calls.
104
+ """
105
+ if not text:
106
+ return text
107
+
108
+ # Remove ANSI escape sequences
109
+ text = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", text)
110
+ # Remove bracket codes
111
+ text = re.sub(r"\[\d+m", "", text)
112
+ # Remove multi-digit bracket codes
113
+ text = re.sub(r"\[\d+;\d+m", "", text)
114
+
115
+ return text.strip()
116
+
117
+
118
+ def sanitize_string(text: Optional[str]) -> Optional[str]:
119
+ """Sanitize string by removing ANSI codes."""
120
+ if text is None:
121
+ return None
122
+ return strip_ansi(text)
123
+
124
+
125
+ def validate_config(config: Any) -> None:
126
+ """
127
+ Validate required configuration options.
128
+
129
+ Raises:
130
+ ValueError: If required config is missing
131
+ """
132
+ if not config.api_url:
133
+ raise ValueError("QAStudio API URL is required")
134
+
135
+ if not config.api_key:
136
+ raise ValueError("QAStudio API key is required")
137
+
138
+ if not config.project_id:
139
+ raise ValueError("QAStudio project ID is required")
140
+
141
+ # Validate URL format
142
+ if not config.api_url.startswith(("http://", "https://")):
143
+ raise ValueError(f"Invalid API URL format: {config.api_url}")
144
+
145
+
146
+ def extract_error_snippet(report: Any) -> Optional[str]:
147
+ """
148
+ Extract code snippet showing where the error occurred.
149
+
150
+ Args:
151
+ report: pytest test report
152
+
153
+ Returns:
154
+ Code snippet with context around the error line, or None if not available
155
+ """
156
+ if not hasattr(report, "longrepr") or not report.longrepr:
157
+ return None
158
+
159
+ try:
160
+ # Try to extract from longrepr
161
+ longrepr_str = str(report.longrepr)
162
+
163
+ # Look for code sections in the traceback (lines starting with >)
164
+ lines = longrepr_str.split("\n")
165
+ snippet_lines = []
166
+ in_code_section = False
167
+
168
+ for line in lines:
169
+ # Detect code lines (often prefixed with spaces or >)
170
+ if line.strip().startswith(">") or (in_code_section and line.startswith(" " * 4)):
171
+ snippet_lines.append(line)
172
+ in_code_section = True
173
+ elif in_code_section and line.strip():
174
+ # Continue collecting until we hit a non-code line
175
+ if not line.startswith("E "):
176
+ snippet_lines.append(line)
177
+ else:
178
+ break
179
+
180
+ if snippet_lines:
181
+ return "\n".join(snippet_lines[:10]) # Limit to 10 lines
182
+
183
+ except Exception:
184
+ pass
185
+
186
+ return None
187
+
188
+
189
+ def extract_error_location(report: Any) -> Optional[dict]:
190
+ """
191
+ Extract precise error location (file, line, column).
192
+
193
+ Args:
194
+ report: pytest test report
195
+
196
+ Returns:
197
+ Dictionary with file, line, and column information, or None if not available
198
+ """
199
+ if not hasattr(report, "longrepr") or not report.longrepr:
200
+ return None
201
+
202
+ try:
203
+ # Try to get location from reprcrash
204
+ if hasattr(report.longrepr, "reprcrash"):
205
+ crash = report.longrepr.reprcrash
206
+ return {
207
+ "file": str(crash.path) if hasattr(crash, "path") else None,
208
+ "line": crash.lineno if hasattr(crash, "lineno") else None,
209
+ "column": 0, # pytest doesn't provide column info
210
+ }
211
+
212
+ # Try to parse from longrepr string
213
+ longrepr_str = str(report.longrepr)
214
+ # Look for file:line pattern
215
+ match = re.search(r"([^\s]+\.py):(\d+):", longrepr_str)
216
+ if match:
217
+ return {"file": match.group(1), "line": int(match.group(2)), "column": 0}
218
+
219
+ except Exception:
220
+ pass
221
+
222
+ return None
223
+
224
+
225
+ def extract_console_output(report: Any) -> Optional[dict]:
226
+ """
227
+ Extract console output (stdout/stderr) from test execution.
228
+
229
+ Args:
230
+ report: pytest test report
231
+
232
+ Returns:
233
+ Dictionary with stdout and stderr, or None if no output
234
+ """
235
+ stdout = None
236
+ stderr = None
237
+
238
+ try:
239
+ # Extract captured stdout
240
+ if hasattr(report, "capstdout") and report.capstdout:
241
+ stdout = sanitize_string(report.capstdout)
242
+
243
+ # Extract captured stderr
244
+ if hasattr(report, "capstderr") and report.capstderr:
245
+ stderr = sanitize_string(report.capstderr)
246
+
247
+ # Also try sections
248
+ if hasattr(report, "sections"):
249
+ for section_name, section_content in report.sections:
250
+ if "stdout" in section_name.lower():
251
+ stdout_text = sanitize_string(section_content)
252
+ if stdout_text:
253
+ stdout = stdout_text
254
+ elif "stderr" in section_name.lower():
255
+ stderr_text = sanitize_string(section_content)
256
+ if stderr_text:
257
+ stderr = stderr_text
258
+
259
+ except Exception:
260
+ pass
261
+
262
+ if stdout or stderr:
263
+ return {"stdout": stdout, "stderr": stderr}
264
+
265
+ return None
266
+
267
+
268
+ def extract_test_steps(report: Any) -> Optional[List[dict]]:
269
+ """
270
+ Extract test execution steps.
271
+
272
+ Note: pytest doesn't have built-in step tracking like Playwright.
273
+ This is a placeholder that returns None for now.
274
+ Users can implement custom step tracking using pytest plugins if needed.
275
+
276
+ Args:
277
+ report: pytest test report
278
+
279
+ Returns:
280
+ List of step dictionaries, or None if not available
281
+ """
282
+ # pytest doesn't have native step tracking
283
+ # This could be enhanced with pytest-bdd or custom plugins in the future
284
+ return None
@@ -0,0 +1,310 @@
1
+ Metadata-Version: 2.4
2
+ Name: qastudio-pytest
3
+ Version: 1.0.4
4
+ Summary: pytest plugin for QAStudio.dev test management platform
5
+ Author-email: QAStudio <ben@qastudio.dev>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/QAStudio-Dev/playwright-reporter-python
8
+ Project-URL: Repository, https://github.com/QAStudio-Dev/playwright-reporter-python
9
+ Project-URL: Issues, https://github.com/QAStudio-Dev/playwright-reporter-python/issues
10
+ Project-URL: Documentation, https://github.com/QAStudio-Dev/playwright-reporter-python#readme
11
+ Keywords: pytest,testing,test-management,qa,qastudio
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Framework :: Pytest
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Software Development :: Testing
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: pytest>=7.0.0
28
+ Requires-Dist: requests>=2.28.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
31
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
32
+ Requires-Dist: black>=23.0.0; extra == "dev"
33
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
34
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
35
+ Requires-Dist: types-requests>=2.28.0; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # QAStudio pytest Plugin
39
+
40
+ A pytest plugin that integrates with [QAStudio.dev](https://qastudio.dev) test management platform.
41
+
42
+ ## Features
43
+
44
+ - 🔄 Automatic test result reporting to QAStudio.dev
45
+ - 📊 Real-time test run tracking
46
+ - 🏷️ Test case linking via markers or test IDs
47
+ - 📸 Screenshot and attachment support
48
+ - 🔧 Configurable via pytest.ini, command line, or environment variables
49
+ - 🎯 Batch result submission for performance
50
+ - 🛡️ Silent mode - won't fail tests if API is unavailable
51
+ - 📝 Error code snippets with context around failure points
52
+ - 📍 Precise error location tracking (file, line, column)
53
+ - 📤 Console output capture (stdout/stderr)
54
+ - 🔍 Enhanced debugging context for test failures
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ pip install qastudio-pytest
60
+ ```
61
+
62
+ ## Quick Start
63
+
64
+ ### 1. Configure via pytest.ini
65
+
66
+ ```ini
67
+ [pytest]
68
+ qastudio_api_url = https://qastudio.dev/api
69
+ qastudio_api_key = your-api-key
70
+ qastudio_project_id = your-project-id
71
+ qastudio_environment = CI
72
+ ```
73
+
74
+ ### 2. Configure via Environment Variables
75
+
76
+ ```bash
77
+ export QASTUDIO_API_URL=https://qastudio.dev/api
78
+ export QASTUDIO_API_KEY=your-api-key
79
+ export QASTUDIO_PROJECT_ID=your-project-id
80
+ export QASTUDIO_ENVIRONMENT=CI
81
+ ```
82
+
83
+ ### 3. Configure via Command Line
84
+
85
+ ```bash
86
+ pytest --qastudio-api-url=https://qastudio.dev/api \
87
+ --qastudio-api-key=your-api-key \
88
+ --qastudio-project-id=your-project-id
89
+ ```
90
+
91
+ ## Usage
92
+
93
+ ### Linking Tests to QAStudio Test Cases
94
+
95
+ #### Method 1: Using pytest markers
96
+
97
+ ```python
98
+ import pytest
99
+
100
+ @pytest.mark.qastudio_id("QA-123")
101
+ def test_login():
102
+ assert user.login("user", "pass")
103
+ ```
104
+
105
+ #### Method 2: Using test ID in test name
106
+
107
+ ```python
108
+ def test_QA123_login():
109
+ """Test case QA-123"""
110
+ assert user.login("user", "pass")
111
+ ```
112
+
113
+ #### Method 3: Using docstring
114
+
115
+ ```python
116
+ def test_login():
117
+ """
118
+ Test user login functionality
119
+
120
+ QAStudio ID: QA-123
121
+ """
122
+ assert user.login("user", "pass")
123
+ ```
124
+
125
+ ### Adding Custom Metadata
126
+
127
+ ```python
128
+ import pytest
129
+
130
+ @pytest.mark.qastudio_id("QA-456")
131
+ @pytest.mark.qastudio_priority("high")
132
+ @pytest.mark.qastudio_tags("smoke", "authentication")
133
+ def test_important_feature():
134
+ assert True
135
+ ```
136
+
137
+ ## Configuration Options
138
+
139
+ | Option | Environment Variable | Description | Default |
140
+ |--------|---------------------|-------------|---------|
141
+ | `qastudio_api_url` | `QASTUDIO_API_URL` | QAStudio.dev API URL | `https://qastudio.dev/api` |
142
+ | `qastudio_api_key` | `QASTUDIO_API_KEY` | API authentication key | Required |
143
+ | `qastudio_project_id` | `QASTUDIO_PROJECT_ID` | Project ID | Required |
144
+ | `qastudio_environment` | `QASTUDIO_ENVIRONMENT` | Environment name | `default` |
145
+ | `qastudio_test_run_name` | `QASTUDIO_TEST_RUN_NAME` | Custom test run name | Auto-generated |
146
+ | `qastudio_test_run_id` | `QASTUDIO_TEST_RUN_ID` | Existing test run ID | None |
147
+ | `qastudio_create_test_run` | `QASTUDIO_CREATE_TEST_RUN` | Create new test run | `true` |
148
+ | `qastudio_batch_size` | `QASTUDIO_BATCH_SIZE` | Results batch size | `10` |
149
+ | `qastudio_silent` | `QASTUDIO_SILENT` | Fail silently on API errors | `true` |
150
+ | `qastudio_verbose` | `QASTUDIO_VERBOSE` | Enable verbose logging | `false` |
151
+ | `qastudio_include_error_snippet` | `QASTUDIO_INCLUDE_ERROR_SNIPPET` | Include error code snippet | `true` |
152
+ | `qastudio_include_error_location` | `QASTUDIO_INCLUDE_ERROR_LOCATION` | Include precise error location | `true` |
153
+ | `qastudio_include_test_steps` | `QASTUDIO_INCLUDE_TEST_STEPS` | Include test execution steps | `true` |
154
+ | `qastudio_include_console_output` | `QASTUDIO_INCLUDE_CONSOLE_OUTPUT` | Include console output | `false` |
155
+
156
+ ## Error Context and Debugging
157
+
158
+ The plugin automatically captures rich debugging context for failed tests:
159
+
160
+ ### Error Code Snippets
161
+
162
+ When a test fails, the plugin captures the relevant code snippet showing where the error occurred:
163
+
164
+ ```python
165
+ def test_calculation():
166
+ result = calculate(5, 0) # This line will be captured in the error snippet
167
+ assert result == 10
168
+ ```
169
+
170
+ Disable with:
171
+ ```ini
172
+ [pytest]
173
+ qastudio_include_error_snippet = false
174
+ ```
175
+
176
+ ### Error Location
177
+
178
+ Precise error location information (file path, line number) is automatically captured:
179
+
180
+ ```json
181
+ {
182
+ "errorLocation": {
183
+ "file": "tests/test_example.py",
184
+ "line": 42,
185
+ "column": 0
186
+ }
187
+ }
188
+ ```
189
+
190
+ Disable with:
191
+ ```ini
192
+ [pytest]
193
+ qastudio_include_error_location = false
194
+ ```
195
+
196
+ ### Console Output
197
+
198
+ Capture stdout and stderr from test execution (disabled by default to avoid sensitive data):
199
+
200
+ ```ini
201
+ [pytest]
202
+ qastudio_include_console_output = true
203
+ ```
204
+
205
+ Or via command line:
206
+ ```bash
207
+ pytest --qastudio-include-console-output
208
+ ```
209
+
210
+ ## Advanced Usage
211
+
212
+ ### Using with pytest-xdist (Parallel Testing)
213
+
214
+ ```bash
215
+ pytest -n auto --qastudio-api-key=your-api-key
216
+ ```
217
+
218
+ The plugin handles parallel test execution automatically.
219
+
220
+ ### CI/CD Integration
221
+
222
+ #### GitHub Actions
223
+
224
+ ```yaml
225
+ - name: Run Tests
226
+ run: |
227
+ pytest --junitxml=report.xml
228
+ env:
229
+ QASTUDIO_API_KEY: ${{ secrets.QASTUDIO_API_KEY }}
230
+ QASTUDIO_PROJECT_ID: ${{ secrets.QASTUDIO_PROJECT_ID }}
231
+ QASTUDIO_ENVIRONMENT: CI
232
+ ```
233
+
234
+ #### GitLab CI
235
+
236
+ ```yaml
237
+ test:
238
+ script:
239
+ - pytest
240
+ variables:
241
+ QASTUDIO_API_KEY: $QASTUDIO_API_KEY
242
+ QASTUDIO_PROJECT_ID: $QASTUDIO_PROJECT_ID
243
+ QASTUDIO_ENVIRONMENT: CI
244
+ ```
245
+
246
+ ## Examples
247
+
248
+ ### Playwright Example
249
+
250
+ A complete Playwright test framework example is available in `examples/playwright_tests/`:
251
+
252
+ ```bash
253
+ # Navigate to example directory
254
+ cd examples/playwright_tests
255
+
256
+ # Install dependencies
257
+ pip install -r requirements.txt
258
+ playwright install chromium
259
+
260
+ # Run the tests
261
+ ./run_tests.sh
262
+
263
+ # Or run directly with pytest
264
+ pytest -v
265
+ ```
266
+
267
+ The example demonstrates:
268
+ - ✅ Testing the QAStudio.dev website
269
+ - ✅ Automatic screenshot capture
270
+ - ✅ Playwright trace recording (`.zip` files)
271
+ - ✅ Integration with qastudio-pytest reporter
272
+ - ✅ Test case linking with `@pytest.mark.qastudio_id()`
273
+
274
+ See [`examples/playwright_tests/README.md`](examples/playwright_tests/README.md) for detailed documentation.
275
+
276
+ ## Development
277
+
278
+ ```bash
279
+ # Clone repository
280
+ git clone https://github.com/QAStudio-Dev/playwright-reporter-python.git
281
+ cd playwright-reporter-python
282
+
283
+ # Create virtual environment
284
+ python -m venv venv
285
+ source venv/bin/activate # On Windows: venv\Scripts\activate
286
+
287
+ # Install in development mode
288
+ pip install -e .[dev]
289
+
290
+ # Run tests
291
+ pytest tests/
292
+
293
+ # Run linting
294
+ black src/ tests/
295
+ flake8 src/ tests/
296
+ mypy src/
297
+
298
+ # Build package
299
+ python -m build
300
+ ```
301
+
302
+ ## License
303
+
304
+ MIT License - see [LICENSE](LICENSE) file for details.
305
+
306
+ ## Support
307
+
308
+ - 📧 Email: ben@qastudio.dev
309
+ - 🐛 Issues: https://github.com/QAStudio-Dev/playwright-reporter-python/issues
310
+ - 📖 Documentation: https://qastudio.dev/docs
@@ -0,0 +1,11 @@
1
+ qastudio_pytest/__init__.py,sha256=YqS1t8gG-dWNmvgD_GvIlLkdC-xSReTO9FYZKA6Kiyc,293
2
+ qastudio_pytest/api_client.py,sha256=kTspiUPHd23V89cD8P-02q12LChLFz8FQlTQzYLBrZA,11433
3
+ qastudio_pytest/models.py,sha256=yfSr-kCCgb8Tq6ZMe8lzIJMLyUiM8ay26WHE9bFXlOs,9083
4
+ qastudio_pytest/plugin.py,sha256=fuNeGNeDus7iTRwupTh9jZtDxcukt10YBFd7mADuQnE,16604
5
+ qastudio_pytest/utils.py,sha256=2dqb5hbmC-PrXd_sPzN87AdLqWdgEgkGZ7m4dOabsvo,8170
6
+ qastudio_pytest-1.0.4.dist-info/licenses/LICENSE,sha256=in3Nfu01STBi3qLqTzYIcPufjpn2Ii5b6JqvW0L3xeY,1065
7
+ qastudio_pytest-1.0.4.dist-info/METADATA,sha256=zU8617ApJe2VC1GUkDo8Sn1Cz3MU2U9CUb8J-FphGXY,8246
8
+ qastudio_pytest-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ qastudio_pytest-1.0.4.dist-info/entry_points.txt,sha256=uaS6HwPI7xFBYraAVHljAqxa-tk87FH17celN75EsZA,45
10
+ qastudio_pytest-1.0.4.dist-info/top_level.txt,sha256=oBnv6a1ZsPrv_bgzcl-LXLna-M92zf1YYFkyzzSlYW0,16
11
+ qastudio_pytest-1.0.4.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [pytest11]
2
+ qastudio = qastudio_pytest.plugin
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 QAStudio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ qastudio_pytest