migrate_plus-8.x-5.x-dev/tests/src/Kernel/Plugin/migrate_plus/data_parser/BaseXml.php
tests/src/Kernel/Plugin/migrate_plus/data_parser/BaseXml.php
<?php
declare(strict_types=1);
namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate_plus\data_parser;
use Drupal\KernelTests\KernelTestBase;
use Drupal\migrate_plus\DataParserPluginInterface;
use Drupal\migrate_plus\DataParserPluginManager;
/**
* Test of the data_parser SimpleXml migrate_plus plugin.
*
* @group migrate_plus
*/
abstract class BaseXml extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['migrate', 'migrate_plus'];
/**
* Path for the xml file.
*/
protected ?string $path;
/**
* The plugin manager.
*/
protected ?DataParserPluginManager $pluginManager = NULL;
/**
* The plugin configuration.
*/
protected ?array $configuration;
/**
* The expected result.
*/
protected ?array $expected;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->path = $this->container->get('module_handler')
->getModule('migrate_plus')->getPath();
$this->pluginManager = $this->container
->get('plugin.manager.migrate_plus.data_parser');
$this->configuration = [
'plugin' => 'url',
'data_fetcher_plugin' => 'file',
'data_parser_plugin' => $this->getDataParserPluginId(),
'destination' => 'node',
'urls' => [],
'ids' => ['id' => ['type' => 'integer']],
'fields' => [
[
'name' => 'id',
'label' => 'Id',
'selector' => '@id',
],
[
'name' => 'values',
'label' => 'Values',
'selector' => 'values',
],
],
'item_selector' => '/items/item',
];
$this->expected = [
[
'Value 1',
'Value 2',
],
[
'Value 1 (single)',
],
];
}
/**
* Tests current URL of parsed XML item.
*/
public function testCurrentUrl(): void {
$urls = [
$this->path . '/tests/data/xml_current_url1.xml',
$this->path . '/tests/data/xml_current_url2.xml',
];
$this->configuration['urls'] = $urls;
$parser = $this->getParser();
// First 2 items available in the first URL.
$parser->rewind();
$this->assertEquals($urls[0], $parser->currentUrl());
$parser->next();
$this->assertEquals($urls[0], $parser->currentUrl());
// Third item available in the second URL.
$parser->next();
$this->assertEquals($urls[1], $parser->currentUrl());
}
/**
* Tests reducing single values.
*/
public function testReduceSingleValue(): void {
$url = $this->path . '/tests/data/xml_reduce_single_value.xml';
$this->configuration['urls'][0] = $url;
$this->assertEquals($this->expected, $this->parseResults($this->getParser()));
}
/**
* Tests retrieving single value from element with attributes.
*/
public function testSingleValueWithAttributes() {
$urls = [
$this->path . '/tests/data/xml_persons.xml',
];
$this->configuration['urls'] = $urls;
$this->configuration['item_selector'] = '/persons/person';
$this->configuration['fields'] = [
[
'name' => 'id',
'label' => 'Id',
'selector' => 'id',
],
[
'name' => 'child',
'label' => 'child',
'selector' => 'children/child',
],
];
$names = [];
foreach ($this->getParser() as $item) {
$names[] = (string) $item['child']->name;
}
$expected_names = ['Elizabeth Junior', 'George Junior', 'Lucy'];
$this->assertEquals($expected_names, $names);
}
/**
* Tests retrieval a value with multiple items.
*/
public function testMultipleItems(): void {
$this->configuration['urls'] = [
$this->path . '/tests/data/xml_multiple_items.xml',
];
$this->configuration['fields'] = [
[
'name' => 'id',
'label' => 'Id',
'selector' => 'Id',
],
[
'name' => 'sub_items1',
'label' => 'Sub items 1',
'selector' => 'Values1/SubItem',
],
[
'name' => 'sub_items2',
'label' => 'Sub items 2',
'selector' => 'Values2/SubItem',
],
];
$parser = $this->getParser();
$parser->next();
// Transform SimpleXMLElements to arrays.
$item = json_decode(json_encode($parser->current()), TRUE);
$sub_items1 = array_column($item['sub_items1'], 'Id');
$this->assertEquals(['1', '2'], $sub_items1);
$this->assertEquals(['3', '4'], $item['sub_items2']);
}
/**
* Tests that the item selector can traverse back up the node at the end.
*
* Traversals like these are redundant and highly inefficient, and filtering
* should be done before the final predicate is specified.
*/
public function testParentTraversalMatch(): void {
$url = $this->path . '/tests/data/xml_items.xml';
$this->configuration['urls'][0] = $url;
$this->configuration['item_selector'] = '/items/item/values[value="Value 3"]/..';
$this->expected = [
['Value 3'],
];
$this->assertEquals($this->expected, $this->parseResults($this->getParser()));
}
/**
* Tests predicate matching.
*
* @dataProvider predicateMatchProvider
*/
public function testPredicateMatch(string $item_selector, array $expected_items): void {
$url = $this->path . '/tests/data/xml_items.xml';
$this->configuration['urls'][0] = $url;
$this->configuration['item_selector'] = $item_selector;
$this->assertEquals($expected_items, $this->parseResults($this->getParser()));
}
/**
* Data provider for testPredicateMatch().
*
* @return array
* An array containing input values and expected output values.
*/
public static function predicateMatchProvider(): array {
return [
// Pick up items with the "odd" parity attribute.
'odd parity' => [
'item_selector' => '/items/item[@parity="odd"]',
'expected_items' => [
['Value 1'],
['Value 3'],
],
],
// Pick up items with the "even" parity attribute.
'even parity' => [
'item_selector' => '/items/item[@parity="even"]',
'expected_items' => [
['Value 2'],
],
],
// Pick up items with the special attribute on the child.
'special attribute' => [
'item_selector' => '/items/item[condition[@special="true"]]',
'expected_items' => [
['Special value'],
],
],
];
}
/**
* Converts the results into a standardized data format to compare.
*
* @param \Traversable $results
* An iterable data result to parse.
* @param string $property
* The property of the result set.
*
* @return array
* The results.
*/
protected function parseResults(\Traversable $results, string $property = 'values'): array {
$data = [];
foreach ($results as $item) {
$values = [];
foreach ($item[$property] as $value) {
$values[] = (string) $value;
}
$data[] = $values;
}
return $data;
}
/**
* Returns a parse object with active configuration.
*
* @return \Drupal\migrate_plus\DataParserPluginInterface
* Data parser object.
*/
protected function getParser(): DataParserPluginInterface {
return $this->pluginManager->createInstance($this->getDataParserPluginId(), $this->configuration);
}
/**
* Returns the data parser plugin ID.
*
* @return string
* The data parser plugin ID.
*/
abstract protected function getDataParserPluginId(): string;
}
