Skip to content

Commit 198b810

Browse files
authored
Unicode ignore order snapshot comparator (#4)
* Unicode ignore order snapshot comparator * Handle non-string values
1 parent 6ad7e94 commit 198b810

File tree

4 files changed

+144
-2
lines changed

4 files changed

+144
-2
lines changed

.github/workflows/php.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ jobs:
3232
run: composer run-script phpstan
3333

3434
- name: Run tests
35-
run: composer run-script tests
35+
run: composer run-script test

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
],
1212
"scripts": {
1313
"phpstan": "vendor/bin/phpstan analyse src tests",
14-
"tests": "vendor/bin/phpunit tests",
14+
"test": "vendor/bin/phpunit tests",
1515
"cs": "vendor/bin/php-cs-fixer fix src"
1616
},
1717
"suggest": {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Riverwaysoft\ApiTools\Testing;
6+
7+
use PHPUnit\Framework\Assert;
8+
use Spatie\Snapshots\Driver;
9+
use Spatie\Snapshots\Exceptions\CantBeSerialized;
10+
11+
/**
12+
* This driver is responsible for 3 main things:
13+
*
14+
* 1) Show unicode characters unescaped in json, so you'll see Привет instead of \u041F\u0440\u0438\u0432\u0435\u0442
15+
* 2) Ignore property order. Example equal json {a: 1, b: 2} and {b: 2, a: 1}
16+
* 3) Ignore order of array elements in json. Example equal json arrays [{a: 1}, {b: 2}] and [{b: 2}, {a: 1}]
17+
*/
18+
class UnicodeIgnoreOrderJsonDriver implements Driver
19+
{
20+
public function serialize($data): string
21+
{
22+
if (is_string($data)) {
23+
$data = json_decode($data);
24+
}
25+
26+
if (is_resource($data)) {
27+
throw new CantBeSerialized('Resources can not be serialized to json');
28+
}
29+
30+
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)."\n";
31+
}
32+
33+
public function extension(): string
34+
{
35+
return 'json';
36+
}
37+
38+
public static function sortJsonRecursively(mixed $array): mixed
39+
{
40+
ksort($array);
41+
42+
foreach ($array as $key => &$value) {
43+
if (is_array($value)) {
44+
if (count($value) > 0 && isset($value[0]) && is_array($value[0])) {
45+
foreach ($value as &$v) {
46+
$v = self::sortJsonRecursively($v);
47+
}
48+
usort($value, function ($a, $b) {
49+
return strcmp(json_encode($a), json_encode($b));
50+
});
51+
} else {
52+
$value = self::sortJsonRecursively($value);
53+
}
54+
}
55+
}
56+
57+
return $array;
58+
}
59+
60+
public function match($expected, $actual): void
61+
{
62+
if (!is_string($actual)) {
63+
$actual = json_encode($actual);
64+
}
65+
66+
$actual = json_decode($actual, true, 512, JSON_THROW_ON_ERROR);
67+
$expected = json_decode($expected, true, 512, JSON_THROW_ON_ERROR);
68+
69+
$actual = self::sortJsonRecursively($actual);
70+
$expected = self::sortJsonRecursively($expected);
71+
72+
Assert::assertJsonStringEqualsJsonString(json_encode($expected), json_encode($actual));
73+
}
74+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace Riverwaysoft\ApiTools\Tests\Testing;
4+
use PHPUnit\Framework\Assert;
5+
use PHPUnit\Framework\TestCase;
6+
use Riverwaysoft\ApiTools\Testing\UnicodeIgnoreOrderJsonDriver;
7+
use Spatie\Snapshots\MatchesSnapshots;
8+
9+
class UnicodeIgnoreOrderJsonDriverTest extends TestCase
10+
{
11+
use MatchesSnapshots;
12+
13+
/**
14+
* @dataProvider jsonProvider
15+
*/
16+
public function testCompare(mixed $json1, mixed $json2, bool $areEqual): void
17+
{
18+
$expected = UnicodeIgnoreOrderJsonDriver::sortJsonRecursively($json1);
19+
$actual = UnicodeIgnoreOrderJsonDriver::sortJsonRecursively($json2);
20+
21+
if ($areEqual) {
22+
Assert::assertJsonStringEqualsJsonString(json_encode($expected), json_encode($actual));
23+
} else {
24+
Assert::assertJsonStringNotEqualsJsonString(json_encode($expected), json_encode($actual));
25+
}
26+
}
27+
28+
public function jsonProvider(): mixed
29+
{
30+
return [
31+
[
32+
['b' => 1, 'a' => 1, 'c' => [['a' => 1], ['b' => 2]]],
33+
['b' => 1, 'a' => 1, 'c' => [['b' => 2], ['a' => 1]]],
34+
true,
35+
],
36+
[
37+
['b' => 1, 'a' => 1],
38+
['a' => 1],
39+
false,
40+
],
41+
[
42+
['b' => 1, 'a' => 1],
43+
['a' => 1, 'b' => 1, 'c' => 2],
44+
false,
45+
],
46+
[
47+
['b' => 2, 'a' => 1],
48+
['a' => 1, 'b' => 2],
49+
true,
50+
],
51+
[
52+
json_decode('{"key": [{"a": 1}, {"b": 3}]}', true),
53+
json_decode('{"key": [{"b": 2}, {"a": 1}]}', true),
54+
false,
55+
],
56+
[
57+
json_decode('{"key": [{"a": 1}, {"b": {"c": 2, "d": 3}}, {"e": 4}]}', true),
58+
json_decode('{"key": [{"b": {"d": 3, "c": 2}}, {"e": 4}, {"a": 1}]}', true),
59+
true
60+
],
61+
[
62+
json_decode('{"key": [{"a": 1}, {"b": {"c": 2, "d": 3}}, {"e": 5}]}', true),
63+
json_decode('{"key": [{"b": {"d": 3, "c": 2}}, {"e": 4}, {"a": 1}]}', true),
64+
false
65+
],
66+
];
67+
}
68+
}

0 commit comments

Comments
 (0)