wgc-runner 4.0.1__tar.gz

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.
Files changed (37) hide show
  1. wgc_runner-4.0.1/PKG-INFO +118 -0
  2. wgc_runner-4.0.1/README.md +96 -0
  3. wgc_runner-4.0.1/pyproject.toml +39 -0
  4. wgc_runner-4.0.1/setup.cfg +4 -0
  5. wgc_runner-4.0.1/src/wgc_runner/__init__.py +1 -0
  6. wgc_runner-4.0.1/src/wgc_runner/fixtures/__init__.py +5 -0
  7. wgc_runner-4.0.1/src/wgc_runner/fixtures/allure.py +12 -0
  8. wgc_runner-4.0.1/src/wgc_runner/fixtures/arsenal_fixtures.py +392 -0
  9. wgc_runner-4.0.1/src/wgc_runner/fixtures/autouse_fixtures.py +47 -0
  10. wgc_runner-4.0.1/src/wgc_runner/fixtures/browser.py +12 -0
  11. wgc_runner-4.0.1/src/wgc_runner/fixtures/cmd_helper.py +12 -0
  12. wgc_runner-4.0.1/src/wgc_runner/fixtures/config.py +15 -0
  13. wgc_runner-4.0.1/src/wgc_runner/fixtures/dgbdiag.py +12 -0
  14. wgc_runner-4.0.1/src/wgc_runner/fixtures/encrypt.py +12 -0
  15. wgc_runner-4.0.1/src/wgc_runner/fixtures/event_listener.py +23 -0
  16. wgc_runner-4.0.1/src/wgc_runner/fixtures/fake_games.py +17 -0
  17. wgc_runner-4.0.1/src/wgc_runner/fixtures/firewall.py +12 -0
  18. wgc_runner-4.0.1/src/wgc_runner/fixtures/log.py +15 -0
  19. wgc_runner-4.0.1/src/wgc_runner/fixtures/os.py +12 -0
  20. wgc_runner-4.0.1/src/wgc_runner/fixtures/registry.py +12 -0
  21. wgc_runner-4.0.1/src/wgc_runner/fixtures/screen.py +12 -0
  22. wgc_runner-4.0.1/src/wgc_runner/fixtures/soft_assert.py +22 -0
  23. wgc_runner-4.0.1/src/wgc_runner/fixtures/ui_map_parser.py +16 -0
  24. wgc_runner-4.0.1/src/wgc_runner/fixtures/wgc_client.py +15 -0
  25. wgc_runner-4.0.1/src/wgc_runner/fixtures/wgc_installer.py +12 -0
  26. wgc_runner-4.0.1/src/wgc_runner/fixtures/wgc_settings.py +15 -0
  27. wgc_runner-4.0.1/src/wgc_runner/fixtures/wgc_uninstaller.py +12 -0
  28. wgc_runner-4.0.1/src/wgc_runner/fixtures/wgni_users_db.py +12 -0
  29. wgc_runner-4.0.1/src/wgc_runner/fixtures/wnc.py +12 -0
  30. wgc_runner-4.0.1/src/wgc_runner/plugin.py +1128 -0
  31. wgc_runner-4.0.1/src/wgc_runner/scripts.py +105 -0
  32. wgc_runner-4.0.1/src/wgc_runner.egg-info/PKG-INFO +118 -0
  33. wgc_runner-4.0.1/src/wgc_runner.egg-info/SOURCES.txt +35 -0
  34. wgc_runner-4.0.1/src/wgc_runner.egg-info/dependency_links.txt +1 -0
  35. wgc_runner-4.0.1/src/wgc_runner.egg-info/entry_points.txt +2 -0
  36. wgc_runner-4.0.1/src/wgc_runner.egg-info/requires.txt +10 -0
  37. wgc_runner-4.0.1/src/wgc_runner.egg-info/top_level.txt +1 -0
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: wgc-runner
3
+ Version: 4.0.1
4
+ Summary: Pytest plugin and fixtures for WGC QA framework
5
+ Author-email: Mykola Kovhanko <thuesdays@gmail.com>
6
+ Project-URL: Homepage, https://github.com/thuesdays/wgc-runner
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: Microsoft :: Windows
9
+ Classifier: Framework :: Pytest
10
+ Requires-Python: >=3.7
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: wgc-pages>=4.0.0
13
+ Requires-Dist: wgc-client>=4.0.0
14
+ Requires-Dist: wgc-mocks>=4.0.0
15
+ Requires-Dist: wgc-helpers>=4.0.0
16
+ Requires-Dist: wgc-core>=4.0.0
17
+ Requires-Dist: wgc-clippy>=1.0.0
18
+ Requires-Dist: wgc-third-party>=1.0.0
19
+ Requires-Dist: pytest>=7.0
20
+ Requires-Dist: pytest-timeout==2.3.1
21
+ Requires-Dist: pyhamcrest==2.0.2
22
+
23
+ # wgc-runner
24
+
25
+ Pytest plugin and fixtures for running WGC QA tests. Manages the full test lifecycle: build download, mock server startup, WGC installation, test execution, artifact collection.
26
+
27
+ ## Pytest Plugin
28
+
29
+ Registered as `wgc_plugin` via `pytest11` entry point. Activated automatically when installed.
30
+
31
+ ### CLI Options
32
+
33
+ ```bash
34
+ py.test tests/ \
35
+ --wgc "\\share\builds\wgc\latest" \
36
+ --wgc-build "12345" \
37
+ --wgc-branch "release/1.0" \
38
+ --wgc-arch "x64" \
39
+ --wgc-publisher "wargaming" \
40
+ --video-capture \
41
+ --no-sanity-check
42
+ ```
43
+
44
+ | Option | Description |
45
+ |---|---|
46
+ | `--wgc` | Path to WGC build on shared drive |
47
+ | `--wgc-build` | Build number |
48
+ | `--wgc-branch` | Git branch |
49
+ | `--wgc-arch` | Architecture: `win32` or `x64` |
50
+ | `--wgc-publisher` | Publisher: `wargaming`, `steam`, `qihoo` |
51
+ | `--video-capture` | Enable screen recording for each test |
52
+ | `--no-sanity-check` | Skip WGC sanity check before test session |
53
+ | `--testrail` | Enable TestRail reporting |
54
+
55
+ ### Lifecycle Hooks
56
+
57
+ | Hook | What it does |
58
+ |---|---|
59
+ | `pytest_cmdline_main` | Download WGC build, start mock server, install WGC |
60
+ | `pytest_configure` | Initialize TestRail API, setup config |
61
+ | `pytest_runtest_setup` | Prepare environment before each test |
62
+ | `pytest_runtest_teardown` | Crash detection, collect logs/video/screenshots, terminate WGC |
63
+ | `pytest_collection_modifyitems` | Auto-assign `folder_*` markers by test directory |
64
+
65
+ ### Key Features
66
+
67
+ - **Auto build download** from shared drive
68
+ - **Mock server** startup/shutdown around test session
69
+ - **Video recording** — each test recorded via OpenCV
70
+ - **Crash detection** — automatic WGC crash check after every test
71
+ - **Sanity check** — WGC startup verification before test session
72
+ - **Artifact upload** — logs, video, screenshots uploaded to share
73
+ - **Auto markers** — `folder_arsenal_login1`, `folder_game_install2`, etc.
74
+
75
+ ## Fixtures
76
+
77
+ All fixtures are session or function-scoped and available automatically:
78
+
79
+ | Fixture | Scope | Provides |
80
+ |---|---|---|
81
+ | `wgc_client` | session | `WGCClient` instance |
82
+ | `config` | session | `WGCConfig` instance |
83
+ | `wgc_settings` | session | `WGCSettingsHelper` |
84
+ | `fake_games` | session | `FakeGamesHelper` |
85
+ | `os_helper` | session | `OSHelper` |
86
+ | `registry` | session | `RegistryHelper` |
87
+ | `browser` | session | `BrowserHelper` |
88
+ | `screen` | session | `ScreenHelper` |
89
+ | `firewall` | session | `FireWallHelper` |
90
+ | `encrypt` | session | `EncryptHelper` |
91
+ | `cmd_helper` | session | `CmdHelper` |
92
+ | `event_listener` | session | `EventHelper` |
93
+ | `wgc_installer` | session | `WGCInstaller` |
94
+ | `wgc_uninstaller` | session | `WGCUninstaller` |
95
+ | `soft_assert` | function | `SoftAssert` |
96
+ | `wgni_users_db` | session | `WGNIUsersDB` |
97
+
98
+ ### Arsenal Fixtures
99
+
100
+ Additional fixtures for Arsenal UI tests (shop, products, billing):
101
+
102
+ ```python
103
+ def test_buy_item(arsenal_shop_page, arsenal_product_list):
104
+ # arsenal_shop_page and arsenal_product_list are auto-configured
105
+ arsenal_shop_page.open_product(arsenal_product_list[0])
106
+ ```
107
+
108
+ ## Install
109
+
110
+ ```bash
111
+ pip install wgc-runner
112
+ ```
113
+
114
+ This will pull all WGC packages transitively.
115
+
116
+ ## Dependencies
117
+
118
+ All WGC packages + `pytest>=7.0`, `pytest-timeout`, `pyhamcrest`
@@ -0,0 +1,96 @@
1
+ # wgc-runner
2
+
3
+ Pytest plugin and fixtures for running WGC QA tests. Manages the full test lifecycle: build download, mock server startup, WGC installation, test execution, artifact collection.
4
+
5
+ ## Pytest Plugin
6
+
7
+ Registered as `wgc_plugin` via `pytest11` entry point. Activated automatically when installed.
8
+
9
+ ### CLI Options
10
+
11
+ ```bash
12
+ py.test tests/ \
13
+ --wgc "\\share\builds\wgc\latest" \
14
+ --wgc-build "12345" \
15
+ --wgc-branch "release/1.0" \
16
+ --wgc-arch "x64" \
17
+ --wgc-publisher "wargaming" \
18
+ --video-capture \
19
+ --no-sanity-check
20
+ ```
21
+
22
+ | Option | Description |
23
+ |---|---|
24
+ | `--wgc` | Path to WGC build on shared drive |
25
+ | `--wgc-build` | Build number |
26
+ | `--wgc-branch` | Git branch |
27
+ | `--wgc-arch` | Architecture: `win32` or `x64` |
28
+ | `--wgc-publisher` | Publisher: `wargaming`, `steam`, `qihoo` |
29
+ | `--video-capture` | Enable screen recording for each test |
30
+ | `--no-sanity-check` | Skip WGC sanity check before test session |
31
+ | `--testrail` | Enable TestRail reporting |
32
+
33
+ ### Lifecycle Hooks
34
+
35
+ | Hook | What it does |
36
+ |---|---|
37
+ | `pytest_cmdline_main` | Download WGC build, start mock server, install WGC |
38
+ | `pytest_configure` | Initialize TestRail API, setup config |
39
+ | `pytest_runtest_setup` | Prepare environment before each test |
40
+ | `pytest_runtest_teardown` | Crash detection, collect logs/video/screenshots, terminate WGC |
41
+ | `pytest_collection_modifyitems` | Auto-assign `folder_*` markers by test directory |
42
+
43
+ ### Key Features
44
+
45
+ - **Auto build download** from shared drive
46
+ - **Mock server** startup/shutdown around test session
47
+ - **Video recording** — each test recorded via OpenCV
48
+ - **Crash detection** — automatic WGC crash check after every test
49
+ - **Sanity check** — WGC startup verification before test session
50
+ - **Artifact upload** — logs, video, screenshots uploaded to share
51
+ - **Auto markers** — `folder_arsenal_login1`, `folder_game_install2`, etc.
52
+
53
+ ## Fixtures
54
+
55
+ All fixtures are session or function-scoped and available automatically:
56
+
57
+ | Fixture | Scope | Provides |
58
+ |---|---|---|
59
+ | `wgc_client` | session | `WGCClient` instance |
60
+ | `config` | session | `WGCConfig` instance |
61
+ | `wgc_settings` | session | `WGCSettingsHelper` |
62
+ | `fake_games` | session | `FakeGamesHelper` |
63
+ | `os_helper` | session | `OSHelper` |
64
+ | `registry` | session | `RegistryHelper` |
65
+ | `browser` | session | `BrowserHelper` |
66
+ | `screen` | session | `ScreenHelper` |
67
+ | `firewall` | session | `FireWallHelper` |
68
+ | `encrypt` | session | `EncryptHelper` |
69
+ | `cmd_helper` | session | `CmdHelper` |
70
+ | `event_listener` | session | `EventHelper` |
71
+ | `wgc_installer` | session | `WGCInstaller` |
72
+ | `wgc_uninstaller` | session | `WGCUninstaller` |
73
+ | `soft_assert` | function | `SoftAssert` |
74
+ | `wgni_users_db` | session | `WGNIUsersDB` |
75
+
76
+ ### Arsenal Fixtures
77
+
78
+ Additional fixtures for Arsenal UI tests (shop, products, billing):
79
+
80
+ ```python
81
+ def test_buy_item(arsenal_shop_page, arsenal_product_list):
82
+ # arsenal_shop_page and arsenal_product_list are auto-configured
83
+ arsenal_shop_page.open_product(arsenal_product_list[0])
84
+ ```
85
+
86
+ ## Install
87
+
88
+ ```bash
89
+ pip install wgc-runner
90
+ ```
91
+
92
+ This will pull all WGC packages transitively.
93
+
94
+ ## Dependencies
95
+
96
+ All WGC packages + `pytest>=7.0`, `pytest-timeout`, `pyhamcrest`
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "wgc-runner"
7
+ version = "4.0.1"
8
+ authors = [
9
+ { name="Mykola Kovhanko", email="thuesdays@gmail.com" },
10
+ ]
11
+ description = "Pytest plugin and fixtures for WGC QA framework"
12
+ readme = "README.md"
13
+ requires-python = ">=3.7"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Operating System :: Microsoft :: Windows",
17
+ "Framework :: Pytest",
18
+ ]
19
+ dependencies = [
20
+ "wgc-pages>=4.0.0",
21
+ "wgc-client>=4.0.0",
22
+ "wgc-mocks>=4.0.0",
23
+ "wgc-helpers>=4.0.0",
24
+ "wgc-core>=4.0.0",
25
+ "wgc-clippy>=1.0.0",
26
+ "wgc-third-party>=1.0.0",
27
+ "pytest>=7.0",
28
+ "pytest-timeout==2.3.1",
29
+ "pyhamcrest==2.0.2",
30
+ ]
31
+
32
+ [project.entry-points.pytest11]
33
+ wgc_plugin = "wgc_runner.plugin:WGCRunnerPlugin"
34
+
35
+ [project.urls]
36
+ "Homepage" = "https://github.com/thuesdays/wgc-runner"
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ __author__ = 'm_beloborodko@wargaming.net'
4
+
5
+ # all files in this folder will be loaded and registered as fixture modules.
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ __author__ = 'n_kovganko@wargaming.net'
4
+
5
+ from pytest import fixture
6
+
7
+ from wgc_helpers.artifacts_helper import ArtifactsHelper
8
+
9
+
10
+ @fixture(scope='session')
11
+ def attach_helper():
12
+ return ArtifactsHelper
@@ -0,0 +1,392 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ Shared fixtures for Arsenal test suites (buy_items1, buy_items2, etc.).
5
+
6
+ Usage in conftest.py of each test suite:
7
+ from wgc_runner.fixtures.arsenal_fixtures import *
8
+
9
+ This eliminates fixture duplication across multiple arsenal test directories.
10
+ """
11
+
12
+ __author__ = 'n_kovganko@wargaming.net'
13
+
14
+ import asyncio
15
+ import pytest
16
+ from os import path
17
+
18
+ from aiohttp import web
19
+ from hamcrest import assert_that, equal_to, has_length, is_not, empty
20
+
21
+ from wgc_helpers.waiter import Waiter
22
+ from wgc_mocks import GameMocks
23
+ from wgc_mocks.wgcps.commerce_fetch_product_list_personal import CommerceFetchProductListPersonal
24
+ from wgc_mocks.wgcps.fetch_client_payment_methods import FetchClientPaymentMethods
25
+ from wgc_mocks.wgcps.fetch_storefront_categories_v2 import CommerceFetchStorefrontCategoriesV2
26
+ from wgc_mocks.wgcps.fetch_storefront_categories_v7 import CommerceFetchStorefrontCategoriesV7
27
+ from wgc_mocks.wgcps.get_account_tsv_method import GetAccountTsvMethod
28
+ from wgc_mocks.wgcps.get_game_page_category import GetGamePageCategory
29
+ from wgc_mocks.wgcps.get_geo_data import GetGeoData
30
+ from wgc_mocks.wgcps.purchase_product_commit import PurchaseProductCommit
31
+ from wgc_mocks.wgcps.purchase_product_prepare import PurchaseProductPrepare
32
+ from wgc_mocks.wgcps.purchase_zero_product_prepare import PurchaseZeroProductPrepare
33
+ from wgc_mocks.wgcps.set_billing_address import SetBillingAddress
34
+ from wgc_mocks.wgni.methods.account import AccountInfoV3
35
+ from wgc_pages import PageArsenalCommon, PageArsenalInstalledGame, PageArsenalProfile
36
+ from wgc_pages.page_arsenal_shop import PageArsenalShop
37
+
38
+
39
+ # ============================================================================
40
+ # Error Fixture Factory
41
+ # ============================================================================
42
+
43
+ def _make_error_fixture(target_cls, status, response_body):
44
+ """
45
+ Factory for creating mock error fixtures.
46
+
47
+ Creates a pytest fixture that patches target_cls._on_post to return
48
+ a fixed error response, and restores the original after the test.
49
+
50
+ Args:
51
+ target_cls: The mock class whose _on_post will be patched.
52
+ status: HTTP status code to return.
53
+ response_body: dict to return as JSON response.
54
+
55
+ Returns:
56
+ A pytest fixture function.
57
+ """
58
+ @pytest.fixture
59
+ def _fixture():
60
+ original = target_cls._on_post
61
+
62
+ async def error_handler(ins):
63
+ return web.json_response(response_body, status=status)
64
+
65
+ target_cls._on_post = error_handler
66
+ yield original
67
+ target_cls._on_post = original
68
+
69
+ return _fixture
70
+
71
+
72
+ def _make_platform_error_fixture(target_cls, result_code, status=500):
73
+ """
74
+ Factory for platform_error-type fixtures.
75
+
76
+ Creates a fixture that returns a standardized WGCPS platform error
77
+ with the specified result_code.
78
+ """
79
+ return _make_error_fixture(target_cls, status, {
80
+ "status": "error",
81
+ "data": {
82
+ "header": {
83
+ "message-id": "c6976f5b-61d9-4407-9637-b926d00cb1e6",
84
+ "tracking-id": "cf9ffc0b-a6a6-46f9-ab20-67eeb43d9a36"
85
+ }
86
+ },
87
+ "errors": [{
88
+ "code": "platform_error",
89
+ "context": {"result_code": result_code}
90
+ }]
91
+ })
92
+
93
+
94
+ # ============================================================================
95
+ # Standard error body constants
96
+ # ============================================================================
97
+
98
+ UNAUTHORIZED_BODY = {'status': 'error', 'errors': [{'code': 'unauthorized'}]}
99
+
100
+ INTERNAL_ERROR_BODY = {"status": "error", "errors": [{
101
+ "code": "platform_error", "context": {"result_code": "INTERNAL_ERROR"}}]}
102
+
103
+ PAYMENT_NOT_EXIST_BODY = {"status": "error", "errors": [{
104
+ "code": "platform_error", "context": {"result_code": "PAYMENT_METHOD_DOESNOT_EXIST"}}]}
105
+
106
+
107
+ # ============================================================================
108
+ # 401 Unauthorized fixtures (generated via factory)
109
+ # ============================================================================
110
+
111
+ error_401_commerce_info = _make_error_fixture(
112
+ GetAccountTsvMethod, 401, UNAUTHORIZED_BODY)
113
+
114
+ error_401_prepare_purchase = _make_error_fixture(
115
+ PurchaseProductPrepare, 401, UNAUTHORIZED_BODY)
116
+
117
+ error_401_commit_purchase = _make_error_fixture(
118
+ PurchaseProductCommit, 401, UNAUTHORIZED_BODY)
119
+
120
+ error_401_geo_data = _make_error_fixture(
121
+ GetGeoData, 401, UNAUTHORIZED_BODY)
122
+
123
+ error_401_set_billing = _make_error_fixture(
124
+ SetBillingAddress, 401, UNAUTHORIZED_BODY)
125
+
126
+
127
+ # ============================================================================
128
+ # 500 Platform Error fixtures (generated via factory)
129
+ # ============================================================================
130
+
131
+ invalid_otp = _make_platform_error_fixture(
132
+ PurchaseProductCommit, 'TSV_INVALID_OTP_CODE')
133
+
134
+ # Alias for backward compatibility (identical to invalid_otp)
135
+ banner_error_otp = _make_platform_error_fixture(
136
+ PurchaseProductCommit, 'TSV_INVALID_OTP_CODE')
137
+
138
+ user_profile_not_completed = _make_platform_error_fixture(
139
+ PurchaseProductPrepare, 'USER_PROFILE_NOT_COMPLETED')
140
+
141
+ order_error = _make_platform_error_fixture(
142
+ PurchaseProductCommit, 'ORDER_PROCESSING_ERROR')
143
+
144
+ payment_method_not_exist = _make_error_fixture(
145
+ FetchClientPaymentMethods, 500, PAYMENT_NOT_EXIST_BODY)
146
+
147
+ prepare_zero_500 = _make_error_fixture(
148
+ PurchaseZeroProductPrepare, 500, INTERNAL_ERROR_BODY)
149
+
150
+ prepare_zero_401 = _make_error_fixture(
151
+ PurchaseZeroProductPrepare, 401, UNAUTHORIZED_BODY)
152
+
153
+
154
+ # ============================================================================
155
+ # get_game_page_category_500 — specific to buy_items2 but kept here for sharing
156
+ # ============================================================================
157
+
158
+ @pytest.fixture
159
+ def get_game_page_category_500():
160
+ on_post = GetGamePageCategory._on_post
161
+
162
+ async def new_on_post(ins):
163
+ await asyncio.sleep(4)
164
+ return web.json_response(
165
+ {"status": "error", "errors": [{"code": "retry", "context": {"interval": 5}}]},
166
+ status=500)
167
+
168
+ GetGamePageCategory._on_post = new_on_post
169
+ yield on_post
170
+ GetGamePageCategory._on_post = on_post
171
+
172
+
173
+ # ============================================================================
174
+ # Restore fixtures
175
+ # ============================================================================
176
+
177
+ @pytest.fixture
178
+ def restore_prepare():
179
+ """Saves and restores PurchaseProductPrepare._on_post."""
180
+ on_post = PurchaseProductPrepare._on_post
181
+ yield on_post
182
+ PurchaseProductPrepare._on_post = on_post
183
+
184
+
185
+ @pytest.fixture
186
+ def restore():
187
+ """
188
+ Comprehensive restore fixture covering all commonly patched handlers.
189
+
190
+ Saves original handlers and restores them after the test.
191
+ Yields tuple: (getAccountTsvMethod, preparePurchase, commitPurchase)
192
+ for backward compatibility with existing tests.
193
+ """
194
+ getAccountTsvMethod = GetAccountTsvMethod._on_post
195
+ preparePurchase = PurchaseProductPrepare._on_post
196
+ commitPurchase = PurchaseProductCommit._on_post
197
+ page_category = GetGamePageCategory._on_post
198
+ default_info = AccountInfoV3._on_post
199
+ def_billing = SetBillingAddress._on_post
200
+ fetch_categories_v2 = CommerceFetchStorefrontCategoriesV2._on_post
201
+ fetch_categories_v7 = CommerceFetchStorefrontCategoriesV7._on_post
202
+ fetch_products = CommerceFetchProductListPersonal._on_post
203
+
204
+ yield getAccountTsvMethod, preparePurchase, commitPurchase
205
+
206
+ GetAccountTsvMethod._on_post = getAccountTsvMethod
207
+ PurchaseProductPrepare._on_post = preparePurchase
208
+ PurchaseProductCommit._on_post = commitPurchase
209
+ GetGamePageCategory._on_post = page_category
210
+ AccountInfoV3._on_post = default_info
211
+ SetBillingAddress._on_post = def_billing
212
+ CommerceFetchStorefrontCategoriesV2._on_post = fetch_categories_v2
213
+ CommerceFetchStorefrontCategoriesV7._on_post = fetch_categories_v7
214
+ CommerceFetchProductListPersonal._on_post = fetch_products
215
+
216
+
217
+ # ============================================================================
218
+ # Helper class for shared test setup and assertion patterns
219
+ # ============================================================================
220
+
221
+ class ArsenalTestHelpers:
222
+ """
223
+ Shared setup and assertion helpers for Arsenal tests.
224
+
225
+ Eliminates duplicated setup blocks and assertion patterns
226
+ across test files.
227
+ """
228
+
229
+ @staticmethod
230
+ def setup_standard_product(wgni_users_db, account, count=1, **kwargs):
231
+ """
232
+ Creates standard buy products + entitlements.
233
+
234
+ Args:
235
+ wgni_users_db: Users DB fixture.
236
+ account: Account object.
237
+ count: Number of products to create.
238
+ **kwargs: Additional product params (price_type, limited_quantity, etc.)
239
+
240
+ Returns:
241
+ list: Created product objects.
242
+ """
243
+ products = []
244
+ defaults = dict(
245
+ buy_item=True,
246
+ title_code=GameMocks.title_id,
247
+ product_code='BUY_WOT_ITEM'
248
+ )
249
+ defaults.update(kwargs)
250
+
251
+ for i in range(count):
252
+ name = kwargs.get('name', f'World of {i} tanks')
253
+ lang = kwargs.get('lang', 'en')
254
+ app_id = kwargs.get('app_id', 'WOT.RU.PRODUCTION')
255
+
256
+ product = wgni_users_db.add_product(
257
+ name, lang, app_id,
258
+ GameMocks.game_update_url,
259
+ **{k: v for k, v in defaults.items()
260
+ if k not in ('name', 'lang', 'app_id')})
261
+ products.append(product)
262
+
263
+ storefront = kwargs.get('storefront_name', None)
264
+ wgni_users_db.add_entitlements(
265
+ account.id, 'shop', product.link_id,
266
+ GameMocks.title_id, storefront)
267
+
268
+ return products
269
+
270
+ @staticmethod
271
+ def setup_billing(wgni_users_db, applicable=True, mandatory=False,
272
+ available=True):
273
+ """
274
+ Configures billing settings.
275
+
276
+ Args:
277
+ wgni_users_db: Users DB fixture.
278
+ applicable: Whether billing is applicable.
279
+ mandatory: Whether billing is mandatory.
280
+ available: Whether billing info is available.
281
+ """
282
+ wgni_users_db.billing_info_available = available
283
+ wgni_users_db.billing_applicable = applicable
284
+ wgni_users_db.billing_mandatory = mandatory
285
+
286
+ @staticmethod
287
+ def launch_and_open_premium_shop(wgc_client, fake_games, os_helper,
288
+ account, game_path):
289
+ """
290
+ Standard flow: launch WGC → select game → verify login →
291
+ play → wait for game → open storefront.
292
+
293
+ This is the repetitive ~30-line setup block that appears
294
+ in 20+ tests in test_arsenal_premium_shop.py.
295
+
296
+ Args:
297
+ wgc_client: WGC client fixture.
298
+ fake_games: Fake games fixture.
299
+ os_helper: OS helper fixture.
300
+ account: Account object.
301
+ game_path: Path to the fake game directory.
302
+ """
303
+ wgc_client.create_user_info(account)
304
+ wgc_client.run_game_center()
305
+ PageArsenalCommon.select_game_page('WOT')
306
+ PageArsenalCommon.open_user_profile_page()
307
+ Waiter.poll(20, PageArsenalProfile.is_account_balance_loaded,
308
+ 'Expected that shown account balance.')
309
+ Waiter.poll(3, PageArsenalProfile.is_username_in_profile_displayed,
310
+ 'Expected that User is logged in.')
311
+ PageArsenalCommon.close_dialog()
312
+ PageArsenalInstalledGame.click_play()
313
+ Waiter.poll(20, fake_games.is_fake_game_running,
314
+ 'Expected that dummy game will be running.')
315
+ Waiter.poll(15, lambda: 'StoreRememberMeToken return WG_S_OK' in
316
+ os_helper.get_file_content(
317
+ path.join(game_path, 'dummy_game.log')),
318
+ 'Expected that game has been launched.')
319
+ assert_that(fake_games.start_dummy(
320
+ game_path, copy_exe=False,
321
+ params=' --open-storefront',
322
+ wait_return_code=True), equal_to('WG_S_OK'))
323
+ Waiter.poll(10, PageArsenalShop.is_storefront_displayed,
324
+ 'Expected that storefront is displayed.')
325
+
326
+ @staticmethod
327
+ def standard_purchase_flow(product_code, select_method=0):
328
+ """
329
+ Performs the standard purchase flow:
330
+ click item → payment page → select method → continue → continue(confirm).
331
+
332
+ Args:
333
+ product_code: Product code to purchase.
334
+ select_method: Index of payment method to select (default 0).
335
+ """
336
+ PageArsenalShop.click_purchase_item_in_storefront(product_code)
337
+ Waiter.poll(7, PageArsenalShop.is_payment_page_displayed,
338
+ 'Expected that opened payment page.')
339
+ PageArsenalShop.select_payment_method(select_method)
340
+ PageArsenalShop.click_continue_or_purchase()
341
+ Waiter.poll(7, PageArsenalShop.is_payment_page_displayed)
342
+ PageArsenalShop.click_continue_or_purchase()
343
+
344
+ @staticmethod
345
+ def assert_choose_product_stats(soft_assert=None, expected_count=1,
346
+ redirected=False, result='success',
347
+ game_app_id='WOT.RU.PRODUCTION'):
348
+ """
349
+ Asserts wgc_choose_game_product_v1 statistics request.
350
+
351
+ With soft_assert: records non-fatal failures.
352
+ Without soft_assert: uses hard assert_that.
353
+
354
+ Args:
355
+ soft_assert: Optional SoftAssert instance for soft checks.
356
+ expected_count: Expected number of requests.
357
+ redirected: Expected redirected_to_web value.
358
+ result: Expected result value.
359
+ game_app_id: Expected game_app_id value.
360
+
361
+ Returns:
362
+ list: Found requests.
363
+ """
364
+ requests = Waiter.poll(3, lambda: GameMocks.find_requests(
365
+ '/statistic/v2/wgc_choose_game_product_v1'))
366
+
367
+ if soft_assert:
368
+ soft_assert.check(requests, has_length(expected_count),
369
+ 'wgc_choose_game_product_v1 request count')
370
+ if requests:
371
+ body = requests[0].params.get('body')
372
+ soft_assert.check(body.get('product_id'), is_not(empty()),
373
+ 'product_id should not be empty')
374
+ soft_assert.check(body.get('redirected_to_web'), equal_to(redirected),
375
+ 'redirected_to_web check')
376
+ soft_assert.check(body.get('result'), equal_to(result),
377
+ 'result check')
378
+ soft_assert.check(body.get('game_app_id'), equal_to(game_app_id),
379
+ 'game_app_id check')
380
+ soft_assert.check(body.get('buy_transaction_id'), is_not(empty()),
381
+ 'buy_transaction_id should not be empty')
382
+ else:
383
+ assert_that(requests, has_length(expected_count))
384
+ if requests:
385
+ body = requests[0].params.get('body')
386
+ assert_that(body.get('product_id'), is_not(empty()))
387
+ assert_that(body.get('redirected_to_web'), equal_to(redirected))
388
+ assert_that(body.get('result'), equal_to(result))
389
+ assert_that(body.get('game_app_id'), equal_to(game_app_id))
390
+ assert_that(body.get('buy_transaction_id'), is_not(empty()))
391
+
392
+ return requests