xero-8.x-2.x-dev/tests/src/Kernel/Normalizer/XeroNestedNormalizerTest.php
tests/src/Kernel/Normalizer/XeroNestedNormalizerTest.php
<?php
namespace Drupal\Tests\xero\Kernel\Normalizer;
use Drupal\Core\TypedData\TypedDataTrait;
use Drupal\KernelTests\KernelTestBase;
use Drupal\serialization\Normalizer\ComplexDataNormalizer;
use Drupal\serialization\Normalizer\TypedDataNormalizer;
use Drupal\xero\Normalizer\XeroListNormalizer;
use Drupal\xero\Normalizer\XeroNormalizer;
use Symfony\Component\Serializer\Serializer;
/**
* Tests denormalizer with services enabled.
*
* @group xero
*/
class XeroNestedNormalizerTest extends KernelTestBase {
use TypedDataTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['user', 'serialization', 'xero'];
/**
* Asserts that denormalization works for deeply-nested objects.
*/
public function testDenormalize() {
$data = [
'TrackingCategories' => [
[
'Name' => 'Region',
'Status' => 'ACTIVE',
'TrackingCategoryID' => '351953c4-8127-4009-88c3-f9cd8c9cbe9f',
'Options' => [
['Name' => 'West Coast', 'Status' => 'ACTIVE'],
['Name' => 'Eastside', 'Status' => 'ACTIVE'],
],
],
],
];
$normalizer = new XeroNormalizer($this->getTypedDataManager());
$denormalizedData = $normalizer->denormalize(
$data,
'\Drupal\xero\Plugin\DataType\TrackingCategory',
'json',
['plugin_id' => 'xero_tracking']
);
$values = $denormalizedData->getValue();
$this->assertArrayHasKey('Options', $values[0]);
$this->assertArrayNotHasKey('Option', $values[0]['Options']);
$this->assertCount(2, $denormalizedData->get(0)->get('Options'));
}
/**
* Tests normalization of Xero contact data under different data scenarios.
*
* @param array $expected
* The expected normalized value.
* @param array $data
* The data for the typed data object.
* @param string $message
* An optional message to display for the test.
*
* @dataProvider contactProvider
*/
public function testNormalizeContact(array $expected, array $data, $message) {
$this->assertNormalizationSuccessful(
$expected, $message, 'xero_contact', 'Contact', $data, []
);
$this->assertNormalizationSuccessful(
$expected, $message, 'xero_contact', 'Contact', [], $data
);
}
/**
* Tests normalization of contact when it is updated with partial data.
*/
public function testNormalizePartialContact() {
$definition = $this->getTypedDataManager()
->createDataDefinition('xero_contact');
$normalizer = $this->getNormalizer();
$initial = [
'Name' => 'ABC Limited',
'Phones' => [
['PhoneType' => 'DEFAULT'],
['PhoneType' => 'MOBILE'],
],
];
$item = $this->getTypedDataManager()
->create($definition, $initial, 'Contact');
$phoneItem = $item->get('Phones')->get(0);
$phoneItem->get('PhoneNumber')->setValue('1111111');
$phoneItem->get('PhoneAreaCode')->setValue('04');
$phoneItem->get('PhoneCountryCode')->setValue('64');
$normalized = $normalizer->normalize($item, 'json', [
'plugin_id' => 'xero_contact',
]);
$expected = [
'Name' => 'ABC Limited',
'Phones' => [
[
'PhoneType' => 'DEFAULT',
'PhoneNumber' => '1111111',
'PhoneAreaCode' => '04',
'PhoneCountryCode' => '64',
],
[
'PhoneType' => 'MOBILE',
],
],
];
$this->assertSame($expected, $normalized);
}
/**
* Tests normalization of Xero invoice data under different data scenarios.
*
* @param array $expected
* The expected normalized value.
* @param array $data
* The data to for the typed data object.
* @param string $message
* An optional message to display for the test.
*
* @dataProvider invoiceProvider
*/
public function testNormalizeInvoice(array $expected, array $data, $message) {
$this->assertNormalizationSuccessful(
$expected, $message, 'xero_invoice', 'Invoice', $data, []
);
$this->assertNormalizationSuccessful(
$expected, $message, 'xero_invoice', 'Invoice', [], $data
);
}
/**
* Provides test arguments for testNormalizeContact.
*
* @return array<int, mixed>[]
* An indexed array of tests to run with test arguments.
*/
public static function contactProvider() {
return [
[
[],
[],
'empty data is empty',
],
[
['Name' => 'ABC Limited'],
['Name' => 'ABC Limited'],
'single properties property are normalized with value.',
],
[
[
'Name' => 'ABC Limited',
'FirstName' => 'Ben',
'LastName' => 'Bowden',
],
[
'Name' => 'ABC Limited',
'FirstName' => 'Ben',
'LastName' => 'Bowden',
],
'multiple and optional properties are normalized with values.',
],
[
[
'FirstName' => 'Ben',
'LastName' => 'Bowden',
],
[
'FirstName' => 'Ben',
'LastName' => 'Bowden',
],
'required properties are excluded if unspecified.',
],
[
[
'Name' => 'ABC Limited',
'FirstName' => NULL,
'LastName' => '',
],
[
'Name' => 'ABC Limited',
'FirstName' => NULL,
'LastName' => '',
],
'empty properties are normalized as empty values.',
],
[
[
'Name' => 'ABC Limited',
'Phones' => [
[
'PhoneAreaCode' => '083',
'PhoneNumber' => '111',
],
],
],
[
'Name' => 'ABC Limited',
'Phones' => [
[
'PhoneNumber' => '111',
'PhoneAreaCode' => '083',
],
],
],
'a specified nested xero property is normalized with nested values',
],
[
[
'Name' => 'ABC Limited',
'Phones' => [],
],
[
'Name' => 'ABC Limited',
'Phones' => [],
],
'explicitly empty nested properties are normalized as empty arrays',
],
[
[
'Name' => 'ABC Limited',
'Phones' => [],
],
[
'Name' => 'ABC Limited',
'Phones' => NULL,
],
'explicitly nulled nested properties are normalized as empty arrays',
],
];
}
/**
* Provides test arguments for testNormalizeInvoice.
*
* @return array<int, mixed>[]
* An indexed array of tests to run with test arguments.
*/
public static function invoiceProvider() {
return [
[
[],
[],
'empty data is empty',
],
[
['Type' => 'ACCREC'],
['Type' => 'ACCREC'],
'single properties property are normalized with value.',
],
[
[
'Type' => 'ACCPAY',
'Date' => '2011-10-12',
'Reference' => 'Inv23123',
],
[
'Type' => 'ACCPAY',
'Date' => '2011-10-12',
'Reference' => 'Inv23123',
],
'multiple and optional properties are normalized with values.',
],
[
[
'Type' => 'ACCPAY',
'InvoiceNumber' => NULL,
'Reference' => '',
],
[
'Type' => 'ACCPAY',
'InvoiceNumber' => NULL,
'Reference' => '',
],
'empty properties are normalized as empty values.',
],
[
[
'Type' => 'ACCPAY',
'Contact' => [
'Name' => 'ABC Limited',
'FirstName' => 'Ben',
],
],
[
'Type' => 'ACCPAY',
'Contact' => [
'Name' => 'ABC Limited',
'FirstName' => 'Ben',
],
],
'a specified nested xero property is normalized with nested values',
],
[
[
'Type' => 'ACCPAY',
'LineItems' => [],
],
[
'Type' => 'ACCPAY',
'LineItems' => [],
],
'explicitly empty nested list properties are normalized as empty arrays',
],
[
[
'Type' => 'ACCPAY',
'LineItems' => [],
],
[
'Type' => 'ACCPAY',
'LineItems' => NULL,
],
'explicitly nulled nested list properties are normalized as empty arrays',
],
[
[
'Type' => 'ACCPAY',
],
[
'Type' => 'ACCPAY',
'HasAttachments' => TRUE,
],
'explicitly set properties are ignored if read-only',
],
[
[
'Type' => 'ACCPAY',
],
[
'Type' => 'ACCPAY',
'HasAttachments' => NULL,
],
'explicitly empty properties are ignored if read-only',
],
[
[
'Type' => 'ACCPAY',
],
[
'Type' => 'ACCPAY',
'Payments' => NULL,
],
'explicitly empty nested list properties are ignored if read-only',
],
];
}
/**
* Test setting to null the contact on the invoice.
*/
public function testInvoiceContact() {
$data = [
'Type' => 'ACCPAY',
'Contact' => NULL,
];
$message = 'nested property explicitly set to null is normalized to empty array';
$expected = [
'Type' => 'ACCPAY',
'Contact' => [],
];
$this->assertNormalizationSuccessful(
$expected, $message, 'xero_invoice', 'Invoice', [], $data
);
$message = 'nested property explicitly created as null is normalized to empty array';
$expected = ['Type' => 'ACCPAY', 'Contact' => []];
$this->assertNormalizationSuccessful(
$expected, $message, 'xero_invoice', 'Invoice', $data, []
);
}
/**
* Tests the normalization of a Xero type under different data scenarios.
*
* @param array $expected
* The expected normalized value.
* @param string $message
* An message to display for the test.
* @param string $xeroType
* The Xero type to test.
* @param string $xeroTypeLabel
* The label of the Xero type to test.
* @param array $initialData
* (optional) The data to create the typed data object with.
* @param array $subsequentData
* (optional) The data to set subsequently on the typed data object.
*/
public function assertNormalizationSuccessful(array $expected, $message, $xeroType, $xeroTypeLabel, array $initialData = [], array $subsequentData = []) {
$definition = $this->getTypedDataManager()
->createDataDefinition($xeroType);
$item = $this->getTypedDataManager()->create($definition, $initialData, $xeroTypeLabel);
foreach ($subsequentData as $name => $value) {
$item->get($name)->setValue($value);
}
$normalized = \Drupal::service('serializer')->normalize($item, 'json', [
'plugin_id' => $xeroType,
]);
$this->assertNormalizedSame($expected, $normalized, "$message");
// Test the normalization as part of a list.
$list_definition = $this->typedDataManager->createListDataDefinition($xeroType);
$list = $this->typedDataManager->create($list_definition, []);
$list->offsetSet(0, $item);
$list_normalized = \Drupal::service('serializer')->normalize($list, 'json', [
'plugin_id' => $xeroType,
]);
$this->assertNormalizedSame($expected, $list_normalized, "$message (on list).");
}
/**
* Asserts that the normalized values are the same.
*
* @param mixed $expected
* The expected array or string.
* @param mixed $actual
* The actual array or string.
* @param string $message
* A message to print out.
*/
protected function assertNormalizedSame($expected, $actual, $message): void {
// Ignore order of properties.
$expected = is_array($expected) ? $this->recursiveKSort($expected) : $expected;
$actual = is_array($actual) ? $this->recursiveKSort($actual) : $actual;
// Use assertSame as data type may matter.
$this->assertSame($expected, $actual, $message);
}
/**
* Recursively sorts an associative array.
*
* @param array $array
* The array to sort.
*
* @return array
* The sorted array.
*/
protected function recursiveKSort(array $array): array {
// First, sort the main array.
ksort($array);
// Then check for child arrays.
foreach ($array as $key => $value) {
if (is_array($value)) {
$value = $this->recursiveKSort($value);
}
$array[$key] = $value;
}
return $array;
}
/**
* Gets the normalizer.
*
* @return \Drupal\xero\Normalizer\XeroNormalizer
* The normalizer instance.
*/
protected function getNormalizer() {
$normalizer = new XeroNormalizer($this->typedDataManager);
$listNormalizer = new XeroListNormalizer();
$typeddata_normalizer = new TypedDataNormalizer();
$complex_normalizer = new ComplexDataNormalizer();
$serializer = new Serializer([
$listNormalizer,
$normalizer,
$typeddata_normalizer,
$complex_normalizer,
]);
$normalizer->setSerializer($serializer);
return $normalizer;
}
/**
* Tests denormalization of Xero bank transaction with the Contact property.
*/
public function testDenormalizeBankTransaction() {
$expected = [
'ContactID' => '6d42f03b-181f-43e3-93fb-2025c012de92',
'Name' => 'Wilson Periodicals',
];
$data = [
'BankTransactions' => [
[
'BankTransactionID' => 'd20b6c54-7f5d-4ce6-ab83-55f609719126',
'Contact' => [
'ContactID' => '6d42f03b-181f-43e3-93fb-2025c012de92',
'Name' => 'Wilson Periodicals',
],
'Date' => '2021-09-01T00:00:00',
'Status' => 'AUTHORISED',
'LineAmountType' => 'Inclusive',
'Total' => '49.90',
'TotalTax' => '0.00',
'SubTotal' => '49.90',
'BankAccount' => [
'AccountID' => 'ac993f75-035b-433c-82e0-7b7a2d40802c',
'Code' => '090',
'Name' => 'Business Bank Account',
],
'CurrencyCode' => 'NZD',
'Type' => 'RECEIVE',
'IsReconciled' => TRUE,
],
],
];
$normalizer = new XeroNormalizer($this->getTypedDataManager());
$denormalizedData = $normalizer->denormalize(
$data,
'\Drupal\xero\Plugin\DataType\BankTransaction',
'xml',
[
'plugin_id' => 'xero_bank_transaction',
],
);
$values = $denormalizedData->getValue();
$this->assertArrayHasKey('Contact', $values[0]);
$this->assertArrayHasKey('BankAccount', $values[0]);
$this->assertEquals($expected, $values[0]['Contact']);
}
}
