PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.27
ووسلام – همگام سازی ووکامرس و باسلام v1.10.27
1.1.26 1.10.27 1.10.24 1.10.25 1.10.23 1.10.22 1.10.21 1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 All 60 releases
sync-basalam / tests / Admin / ProductAttributeSuffixTest.php

ProductAttributeSuffixTest.php in ووسلام – همگام سازی ووکامرس و باسلام 1.10.27, at tests/Admin/ProductAttributeSuffixTest.php

326 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SyncBasalam\Admin\Product\Data\Services {
4 function syncBasalamSettings()
5 {
6 return $GLOBALS['attribute_suffix_test_settings'] ?? \syncBasalamSettings();
7 }
8
9 function wc_attribute_label($name)
10 {
11 return $GLOBALS['attribute_suffix_test_labels'][$name] ?? $name;
12 }
13
14 function wc_get_product_terms($productId, $taxonomy, $args)
15 {
16 $GLOBALS['attribute_suffix_test_term_calls'][] = [$productId, $taxonomy, $args];
17 return $GLOBALS['attribute_suffix_test_terms'][$taxonomy] ?? [];
18 }
19 }
20
21 namespace SyncBasalam\Admin\Components {
22 function syncBasalamSettings()
23 {
24 return $GLOBALS['attribute_suffix_test_settings'] ?? \syncBasalamSettings();
25 }
26
27 function esc_attr($value)
28 {
29 return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
30 }
31 }
32
33 namespace SyncBasalam\Tests\Admin {
34 use PHPUnit\Framework\TestCase;
35 use ReflectionClass;
36 use ReflectionProperty;
37 use SyncBasalam\Admin\Components\SettingPageComponents;
38 use SyncBasalam\Admin\Product\Data\Handlers\SimpleProductHandler;
39 use SyncBasalam\Admin\Product\Data\Handlers\VariableProductHandler;
40 use SyncBasalam\Admin\Product\Data\Services\AttributeService;
41 use SyncBasalam\Admin\Settings\SettingsConfig;
42
43 require_once dirname(__DIR__, 2) . '/includes/Admin/Settings/SettingsConfig.php';
44 require_once dirname(__DIR__, 2) . '/includes/Admin/Components/SettingPageComponents.php';
45 require_once dirname(__DIR__, 2) . '/includes/Admin/Product/Data/Services/MobileDataHandler.php';
46 require_once dirname(__DIR__, 2) . '/includes/Admin/Product/Data/Services/GoldDataHandler.php';
47 require_once dirname(__DIR__, 2) . '/includes/Admin/Product/Data/Services/AttributeService.php';
48 require_once dirname(__DIR__, 2) . '/includes/Admin/Product/Data/Handlers/ProductDataHandlerInterface.php';
49 require_once dirname(__DIR__, 2) . '/includes/Admin/Product/Data/Handlers/SimpleProductHandler.php';
50 require_once dirname(__DIR__, 2) . '/includes/Admin/Product/Data/Handlers/VariableProductHandler.php';
51
52 class ProductAttributeSuffixTest extends TestCase
53 {
54 private array $settings;
55
56 protected function setUp(): void
57 {
58 $this->configure('ناشر, نویسنده');
59 $GLOBALS['attribute_suffix_test_labels'] = [];
60 $GLOBALS['attribute_suffix_test_terms'] = [];
61 $GLOBALS['attribute_suffix_test_term_calls'] = [];
62 }
63
64 protected function tearDown(): void
65 {
66 unset(
67 $GLOBALS['attribute_suffix_test_settings'],
68 $GLOBALS['attribute_suffix_test_labels'],
69 $GLOBALS['attribute_suffix_test_terms'],
70 $GLOBALS['attribute_suffix_test_term_calls']
71 );
72 }
73
74 /** @dataProvider suffixCases */
75 public function testBuildsSuffixFromConfiguredAttributeLabels($configured, array $attributes, ?string $expected): void
76 {
77 $this->configure($configured);
78
79 self::assertSame($expected, (new AttributeService())->getAttributeSuffix($this->product($attributes)));
80 }
81
82 public function suffixCases(): array
83 {
84 $book = ['ناشر' => ['نشر چش�
85 ه'], 'نویسنده' => ['صادق هدایت']];
86
87 return [
88 'existing single attribute' => ['ناشر', $book, 'نشر چش�
89 ه'],
90 'two attributes with English comma' => ['ناشر, نویسنده', $book, 'نشر چش�
91 ه, صادق هدایت'],
92 'two attributes with Persian comma' => ['ناشر، نویسنده', $book, 'نشر چش�
93 ه, صادق هدایت'],
94 'configured order differs from product order' => ['نویسنده, ناشر', $book, 'صادق هدایت, نشر چش�
95 ه'],
96 'whitespace around names and values' => [" \tناشر , نویسنده\n", [' ناشر ' => [' نشر چش�
97 ه '], 'نویسنده' => [' صادق هدایت ']], 'نشر چش�
98 ه, صادق هدایت'],
99 'empty entries and mixed separators' => [', ناشر، , نویسنده،', $book, 'نشر چش�
100 ه, صادق هدایت'],
101 'duplicate names do not repeat values' => ['ناشر, ناشر، نویسنده,ناشر', $book, 'نشر چش�
102 ه, صادق هدایت'],
103 'first attribute missing' => ['�
104 ترج�
105 , ناشر', $book, 'نشر چش�
106 ه'],
107 'second attribute missing' => ['ناشر, �
108 ترج�
109 ', $book, 'نشر چش�
110 ه'],
111 'all attributes missing' => ['�
112 ترج�
113 , زبان', $book, null],
114 'first attribute empty' => ['ناشر, نویسنده', ['ناشر' => [], 'نویسنده' => ['صادق هدایت']], 'صادق هدایت'],
115 'second attribute empty' => ['ناشر, نویسنده', ['ناشر' => ['نشر چش�
116 ه'], 'نویسنده' => ['']], 'نشر چش�
117 ه'],
118 'whitespace value skipped' => ['ناشر, نویسنده', ['ناشر' => [" \t"], 'نویسنده' => ['صادق هدایت']], 'صادق هدایت'],
119 'all values empty' => ['ناشر, نویسنده', ['ناشر' => [''], 'نویسنده' => []], null],
120 'no product attributes' => ['ناشر, نویسنده', [], null],
121 'empty setting' => ['', $book, null],
122 'unset setting' => [null, $book, null],
123 'only separators and whitespace' => [" , ، \t,", $book, null],
124 'multiple values in one attribute' => ['ناشر, نویسنده', ['ناشر' => ['نشر چش�
125 ه'], 'نویسنده' => ['صادق هدایت', 'بزرگ علوی']], 'نشر چش�
126 ه, صادق هدایت, بزرگ علوی'],
127 'zero is a valid attribute value' => ['ش�
128 اره, ناشر', ['ش�
129 اره' => ['0'], 'ناشر' => ['نشر چش�
130 ه']], '0, نشر چش�
131 ه'],
132 'zero is a valid attribute label' => ['0, ناشر', ['0' => ['ویژگی صفر'], 'ناشر' => ['نشر چش�
133 ه']], 'ویژگی صفر, نشر چش�
134 ه'],
135 'identical values belong to distinct attributes' => ['ناشر, نویسنده', ['ناشر' => ['�
136 شترک'], 'نویسنده' => ['�
137 شترک']], '�
138 شترک, �
139 شترک'],
140 'three names and mixed commas' => ['ناشر، نویسنده, زبان', $book + ['زبان' => ['فارسی']], 'نشر چش�
141 ه, صادق هدایت, فارسی'],
142 'unselected attributes are excluded' => ['ناشر, نویسنده', ['زبان' => ['فارسی']] + $book, 'نشر چش�
143 ه, صادق هدایت'],
144 'labels remain exact matches' => ['ناش, نویسنده', $book, 'صادق هدایت'],
145 ];
146 }
147
148 /** @dataProvider disabledSettings */
149 public function testDisabledFeatureDoesNotReadProductAttributes($enabled): void
150 {
151 $this->configure('ناشر, نویسنده', $enabled);
152 $product = new class {
153 public function get_attributes()
154 {
155 throw new \RuntimeException('Disabled suffixes must not read attributes.');
156 }
157 };
158
159 self::assertNull((new AttributeService())->getAttributeSuffix($product));
160 }
161
162 public function disabledSettings(): array
163 {
164 return [['no'], [false], [null], [''], [true]];
165 }
166
167 public function testUsesGlobalTaxonomyLabelsAndProductTermNamesAlongsideCustomAttributes(): void
168 {
169 $GLOBALS['attribute_suffix_test_labels']['pa_publisher'] = 'ناشر';
170 $GLOBALS['attribute_suffix_test_terms']['pa_publisher'] = ['نشر چش�
171 ه', 'نشر نی'];
172 $product = $this->product(['نویسنده' => ['صادق هدایت'], 'pa_publisher' => []], ['pa_publisher']);
173
174 self::assertSame('نشر چش�
175 ه, نشر نی, صادق هدایت', (new AttributeService())->getAttributeSuffix($product));
176 self::assertSame([[42, 'pa_publisher', ['fields' => 'names']]], $GLOBALS['attribute_suffix_test_term_calls']);
177 }
178
179 public function testCombinesTwoGlobalAttributesInConfiguredOrder(): void
180 {
181 $GLOBALS['attribute_suffix_test_labels'] = ['pa_author' => 'نویسنده', 'pa_publisher' => 'ناشر'];
182 $GLOBALS['attribute_suffix_test_terms'] = ['pa_author' => ['صادق هدایت'], 'pa_publisher' => ['نشر چش�
183 ه']];
184 $product = $this->product(['pa_author' => [], 'pa_publisher' => []], ['pa_author', 'pa_publisher']);
185
186 self::assertSame('نشر چش�
187 ه, صادق هدایت', (new AttributeService())->getAttributeSuffix($product));
188 }
189
190 public function testSkipsTaxonomyAttributesWithNoAssignedTerms(): void
191 {
192 $GLOBALS['attribute_suffix_test_labels']['pa_publisher'] = 'ناشر';
193 $product = $this->product(['pa_publisher' => [], 'نویسنده' => ['صادق هدایت']], ['pa_publisher']);
194
195 self::assertSame('صادق هدایت', (new AttributeService())->getAttributeSuffix($product));
196 }
197
198 /** @dataProvider titleCases */
199 public function testGeneratedTitlesPreserveExistingFormattingForBothProductHandlers(
200 string $configured, array $attributes, string $prefix, string $suffix, string $baseName, string $expected
201 ): void {
202 $this->configure($configured);
203 $this->settings[SettingsConfig::PRODUCT_PREFIX_TITLE] = $prefix;
204 $this->settings[SettingsConfig::PRODUCT_SUFFIX_TITLE] = $suffix;
205
206 foreach ([SimpleProductHandler::class, VariableProductHandler::class] as $handlerClass) {
207 self::assertSame($expected, $this->handler($handlerClass)->getName($this->product($attributes, [], $baseName)));
208 }
209 }
210
211 public function titleCases(): array
212 {
213 $book = ['ناشر' => ['نشر چش�
214 ه'], 'نویسنده' => ['صادق هدایت']];
215
216 return [
217 'both values inside one pair of parentheses' => ['ناشر, نویسنده', $book, '', '', 'کتاب', 'کتاب (نشر چش�
218 ه, صادق هدایت)'],
219 'static prefix and suffix remain' => ['ناشر, نویسنده', $book, 'فروشگاه', 'اصل', 'کتاب', 'فروشگاه کتاب اصل (نشر چش�
220 ه, صادق هدایت)'],
221 'single setting remains compatible' => ['ناشر', $book, '', '', 'کتاب', 'کتاب (نشر چش�
222 ه)'],
223 'one missing attribute has no dangling separator' => ['�
224 ترج�
225 , ناشر', $book, '', '', 'کتاب', 'کتاب (نشر چش�
226 ه)'],
227 'no matches has no empty parentheses' => ['�
228 ترج�
229 , زبان', $book, 'فروشگاه', 'اصل', 'کتاب', 'فروشگاه کتاب اصل'],
230 'empty setting leaves title alone' => ['', $book, '', '', 'کتاب', 'کتاب'],
231 'standalone zero value is kept' => ['ش�
232 اره', ['ش�
233 اره' => ['0']], '', '', 'کالا', 'کالا (0)'],
234 'Persian title is capped at 120 characters' => ['ناشر, نویسنده', $book, '', '', str_repeat('ک', 115), str_repeat('ک', 115) . ' (نشر'],
235 ];
236 }
237
238 public function testDisabledSuffixDoesNotChangeGeneratedTitle(): void
239 {
240 $this->configure('ناشر, نویسنده', 'no');
241 self::assertSame('کتاب', $this->handler(SimpleProductHandler::class)->getName($this->product(['ناشر' => ['نشر چش�
242 ه']])));
243 }
244
245 public function testSettingsInputRetainsCommaSeparatedNamesAndShowsTheExample(): void
246 {
247 $this->configure('ناشر, نویسنده');
248 ob_start();
249 SettingPageComponents::renderAttributeSuffixPriority();
250 $html = ob_get_clean();
251
252 self::assertStringContainsString('value="ناشر, نویسنده"', $html);
253 self::assertStringContainsString('placeholder="�
254 ثال: ناشر, نویسنده"', $html);
255 self::assertStringNotContainsString('disabled', $html);
256 }
257
258 public function testDisabledSettingsInputRetainsAndEscapesItsValue(): void
259 {
260 $this->configure('ناشر, "نویسنده"', 'no');
261 ob_start();
262 SettingPageComponents::renderAttributeSuffixPriority();
263 $html = ob_get_clean();
264
265 self::assertStringContainsString('value="ناشر, &quot;نویسنده&quot;"', $html);
266 self::assertStringContainsString('disabled', $html);
267 }
268
269 private function configure($names, $enabled = 'yes'): void
270 {
271 $this->settings = array_merge(SettingsConfig::getDefaultSettings(), [
272 SettingsConfig::PRODUCT_ATTRIBUTE_SUFFIX_ENABLED => $enabled,
273 SettingsConfig::PRODUCT_ATTRIBUTE_SUFFIX_PRIORITY => $names,
274 ]);
275 $GLOBALS['attribute_suffix_test_settings'] = new class($this->settings) {
276 private array $settings;
277 public function __construct(array $settings) { $this->settings = $settings; }
278 public function getSettings($key = null) { return $key === null ? $this->settings : ($this->settings[$key] ?? null); }
279 };
280 }
281
282 private function product(array $attributes, array $taxonomies = [], string $name = 'کتاب')
283 {
284 $fixtures = [];
285 foreach ($attributes as $label => $options) {
286 $fixtures[] = new class((string) $label, $options, in_array($label, $taxonomies, true)) {
287 private string $name;
288 private array $options;
289 private bool $taxonomy;
290 public function __construct(string $name, array $options, bool $taxonomy)
291 {
292 $this->name = $name;
293 $this->options = $options;
294 $this->taxonomy = $taxonomy;
295 }
296 public function is_taxonomy(): bool { return $this->taxonomy; }
297 public function get_name(): string { return $this->name; }
298 public function get_options(): array { return $this->options; }
299 };
300 }
301
302 return new class($fixtures, $name) {
303 private array $attributes;
304 private string $name;
305 public function __construct(array $attributes, string $name) { $this->attributes = $attributes; $this->name = $name; }
306 public function get_attributes(): array { return $this->attributes; }
307 public function get_name(): string { return $this->name; }
308 public function get_id(): int { return 42; }
309 };
310 }
311
312 private function handler(string $class): SimpleProductHandler
313 {
314 // Name generation only needs settings and AttributeService; avoid
315 // constructing unrelated upload and category API dependencies.
316 $handler = (new ReflectionClass($class))->newInstanceWithoutConstructor();
317 foreach (['settings' => $this->settings, 'attributeService' => new AttributeService()] as $name => $value) {
318 $property = new ReflectionProperty(SimpleProductHandler::class, $name);
319 $property->setAccessible(true);
320 $property->setValue($handler, $value);
321 }
322 return $handler;
323 }
324 }
325 }
326