Merge branch 'master' into otherwise

This commit is contained in:
Oliver Davies 2019-04-19 08:08:58 +01:00
commit 54d8c2c178
19 changed files with 249 additions and 120 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
composer.lock
filters.php
*.xml
!/tests/fixtures/*/*.xml

8
CHANGELOG.md Normal file
View file

@ -0,0 +1,8 @@
# CHANGELOG
## 2.2.0
Released 2019-04-19
* [#19](https://github.com/opdavies/gmail-filter-builder/issues/19): Update test method names
* [#21](https://github.com/opdavies/gmail-filter-builder/issues/21): Minify the generated XML by default

2
bin/generate-filters.php Normal file → Executable file
View file

@ -13,6 +13,6 @@ if (file_exists(__DIR__.'/../vendor/autoload.php')) {
$container = new Container();
/** @var Application $application */
$application = $container->get('app.cli');
$application = $container->get(Application::class);
$application->setDefaultCommand(GenerateCommand::NAME);
$application->run();

View file

@ -6,7 +6,7 @@
"authors": [
{
"name": "Oliver Davies",
"email": "opdavies@gmail.com"
"email": "oliver@oliverdavies.uk"
}
],
"require": {
@ -44,7 +44,7 @@
"sort-packages": true
},
"scripts": {
"style": "php-cs-fixer fix",
"lint": "php-cs-fixer fix",
"test": "phpunit"
}
}

View file

@ -1,13 +0,0 @@
services:
app.cli:
class: Symfony\Component\Console\Application
autowire: true
app.builder:
class: Opdavies\GmailFilterBuilder\Service\Builder
app.generate.command:
class: Opdavies\GmailFilterBuilder\Console\Command\GenerateCommand
autowire: true
tags:
- { name: ConsoleCommand }

8
services.yml Normal file
View file

@ -0,0 +1,8 @@
services:
Symfony\Component\Console\Application:
autowire: true
Opdavies\GmailFilterBuilder\Console\Command\GenerateCommand:
autowire: true
tags:
- { name: ConsoleCommand }

View file

@ -2,13 +2,14 @@
namespace Opdavies\GmailFilterBuilder\Console\Command;
use Opdavies\GmailFilterBuilder\Builder;
use Opdavies\GmailFilterBuilder\Service\Builder;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
class GenerateCommand extends Command
{
@ -22,8 +23,9 @@ class GenerateCommand extends Command
$this
->setName(self::NAME)
->setDefinition([
new InputOption('input-file', null, InputOption::VALUE_OPTIONAL, 'The name of the PHP file containing your filters.', 'filters.php'),
new InputOption('output-file', null, InputOption::VALUE_OPTIONAL, 'The name of the XML file to generate.', 'filters.xml')
new InputOption('input-file', 'i', InputOption::VALUE_OPTIONAL, 'The name of the PHP file containing your filters.', 'filters.php'),
new InputOption('output-file', 'o', InputOption::VALUE_OPTIONAL, 'The name of the XML file to generate.', 'filters.xml'),
new InputOption('expanded', 'e', InputOption::VALUE_NONE, 'Whether to generate expanded XML.')
])
->setDescription('Generates XML for Gmail filters.')
;
@ -34,26 +36,31 @@ class GenerateCommand extends Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$inputFile = $input->getOption('input-file');
$outputFile = $input->getOption('output-file');
if (file_exists(__DIR__.'/'.$inputFile)) {
$filters = require_once __DIR__.'/'.$inputFile;
} elseif (file_exists(__DIR__.'/../../../../../../'.$inputFile)) {
# Installed as a dependency within "vendor".
$filters = require_once __DIR__.'/../../../../../../'.$inputFile;
} else {
throw new \Exception('No filters.php file found.');
}
$io = new SymfonyStyle($input, $output);
try {
new Builder($filters, $outputFile);
// TODO: Inject this.
new Builder($this->filters($input), $outputFile = $this->outputFile($input), true, $input->getOption('expanded'));
$io->success(sprintf('%s file generated.', $outputFile));
} catch (IOException $e) {
$io->error($e->getMessage());
}
}
private function outputFile(InputInterface $input): string
{
return $input->getOption('output-file') ?? getcwd() . '/filters.xml';
}
private function filters(InputInterface $input): array
{
$fs = new Filesystem();
if (!$fs->exists($inputFile = $input->getOption('input-file') ?? getcwd() . '/filters.php')) {
throw new \RuntimeException('No input file found.');
}
return require $inputFile;
}
}

View file

@ -2,6 +2,7 @@
namespace Opdavies\GmailFilterBuilder\Container;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
@ -13,7 +14,7 @@ class CommandCompilerClass implements CompilerPassInterface
*/
public function process(ContainerBuilder $container)
{
$definition = $container->findDefinition('app.cli');
$definition = $container->findDefinition(Application::class);
$taggedServices = $container->findTaggedServiceIds('ConsoleCommand');
foreach ($taggedServices as $id => $tags) {

View file

@ -11,7 +11,7 @@ class Container
public function __construct()
{
$this->containerBuilder = new ContainerBuilder();
$loader = new YamlFileLoader($this->containerBuilder, new FileLocator(__DIR__.'/../../config'));
$loader = new YamlFileLoader($this->containerBuilder, new FileLocator(__DIR__.'/../../'));
$loader->load('services.yml');
$this->containerBuilder->addCompilerPass(new CommandCompilerClass());
$this->containerBuilder->compile();

View file

@ -6,6 +6,9 @@ use Tightenco\Collect\Support\Collection;
class Filter
{
/** @var string */
const SEPARATOR = '|';
/**
* @var array
*/
@ -22,7 +25,7 @@ class Filter
/**
* @return static
*/
public static function create()
public static function create(): self
{
return new static();
}
@ -32,7 +35,7 @@ class Filter
*
* @return $this
*/
public function has($value)
public function has(string $value): self
{
$this->properties['hasTheWord'] = $value;
@ -48,7 +51,7 @@ class Filter
*
* @return $this
*/
public function hasNot($value)
public function hasNot(string $value): self
{
$this->properties['doesNotHaveTheWord'] = $value;
@ -60,7 +63,7 @@ class Filter
*
* @return $this
*/
public function from($values)
public function from($values): self
{
if (!empty($values)) {
$this->properties['from'] = collect($values)->map(function ($value) {
@ -76,7 +79,7 @@ class Filter
*
* @return $this
*/
public function to($values)
public function to($values): self
{
if (!empty($values)) {
$this->properties['to'] = collect($values)->map(function ($value) {
@ -92,7 +95,7 @@ class Filter
*
* @return $this
*/
public function subject($values)
public function subject($values): self
{
$this->properties['subject'] = collect($values)->map(function ($value) {
return json_encode($value);
@ -104,7 +107,7 @@ class Filter
/**
* @return $this
*/
public function hasAttachment()
public function hasAttachment(): self
{
$this->properties['hasAttachment'] = 'true';
@ -114,12 +117,13 @@ class Filter
/**
* Filter a message if it was sent from a mailing list.
*
* @param $value The mailing list address
* @param string|array $value The mailing list address
*
* @return $this
*/
public function fromList($value)
public function fromList($value): self
{
$value = collect($value)->implode(self::SEPARATOR);
$this->has("list:{$value}");
return $this;
@ -128,7 +132,7 @@ class Filter
/**
* @return $this
*/
public function excludeChats()
public function excludeChats(): self
{
$this->properties['excludeChats'] = 'true';
@ -140,7 +144,7 @@ class Filter
*
* @return $this
*/
public function label($label)
public function label(string $label): self
{
$this->properties['label'] = $label;
@ -150,7 +154,7 @@ class Filter
/**
* @return $this
*/
public function archive()
public function archive(): self
{
$this->properties['shouldArchive'] = 'true';
@ -162,7 +166,7 @@ class Filter
*
* @return $this
*/
public function labelAndArchive($label)
public function labelAndArchive(string $label): self
{
$this->label($label)->archive();
@ -172,7 +176,7 @@ class Filter
/**
* @return $this
*/
public function spam()
public function spam(): self
{
$this->properties['shouldSpam'] = 'true';
$this->properties['shouldNeverSpam'] = 'false';
@ -183,7 +187,7 @@ class Filter
/**
* @return $this
*/
public function neverSpam()
public function neverSpam(): self
{
$this->properties['shouldSpam'] = 'false';
$this->properties['shouldNeverSpam'] = 'true';
@ -194,7 +198,7 @@ class Filter
/**
* @return $this
*/
public function trash()
public function trash(): self
{
$this->properties['shouldTrash'] = 'true';
@ -204,7 +208,7 @@ class Filter
/**
* @return $this
*/
public function read()
public function read(): self
{
$this->properties['markAsRead'] = 'true';
@ -214,7 +218,7 @@ class Filter
/**
* @return $this
*/
public function star()
public function star(): self
{
$this->properties['shouldStar'] = 'true';
@ -226,7 +230,7 @@ class Filter
*
* @return $this
*/
public function forward($to)
public function forward(string $to): self
{
$this->properties['forwardTo'] = $to;
@ -236,7 +240,7 @@ class Filter
/**
* @return $this
*/
public function important()
public function important(): self
{
$this->properties['shouldAlwaysMarkAsImportant'] = 'true';
@ -246,7 +250,7 @@ class Filter
/**
* @return $this
*/
public function notImportant()
public function notImportant(): self
{
$this->properties['shouldNeverMarkAsImportant'] = 'true';
@ -258,7 +262,7 @@ class Filter
*
* @return $this
*/
public function categorise($category)
public function categorise(string $category): self
{
$this->properties['smartLabelToApply'] = $category;
@ -271,7 +275,7 @@ class Filter
* @return array
* @deprecated toArray()
*/
public function getProperties()
public function getProperties(): array
{
return $this->properties;
}
@ -281,7 +285,7 @@ class Filter
*
* @return array
*/
public function toArray()
public function toArray(): array
{
return $this->properties;
}

View file

@ -29,17 +29,21 @@ class Builder
*/
private $xml;
public function __construct(array $filters, $outputFile = 'filters.xml', $writeFile = true)
/** @var bool */
private $expanded;
public function __construct(array $filters, $outputFile = 'filters.xml', $writeFile = true, $expanded = false)
{
$this->filesystem = new Filesystem();
$this->filters = $filters;
$this->outputFile = $outputFile;
$this->writeFile = $writeFile;
$this->expanded = $expanded;
$this->build();
}
public function __toString()
public function __toString(): string
{
return $this->build();
}
@ -49,7 +53,7 @@ class Builder
*
* @return string
*/
public function getXml()
public function getXml(): string
{
return $this->xml;
}
@ -59,16 +63,16 @@ class Builder
*
* @return string
*/
private function build()
private function build(): void
{
$prefix = "<?xml version='1.0' encoding='UTF-8'?>" . PHP_EOL . "<feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>";
$prefix = "<?xml version='1.0' encoding='UTF-8'?>" . $this->glue() . "<feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>";
$suffix = '</feed>';
$xml = collect($this->filters)->map(function ($items) {
return $this->buildEntry($items);
})->implode(PHP_EOL);
})->implode($this->glue());
$this->xml = collect([$prefix, $xml, $suffix])->implode(PHP_EOL);
$this->xml = collect([$prefix, $xml, $suffix])->implode($this->glue());
if ($this->writeFile) {
$this->filesystem->dumpFile($this->outputFile, $this->xml);
@ -82,13 +86,15 @@ class Builder
*
* @return string
*/
private function buildEntry(Filter $filter)
private function buildEntry(Filter $filter): string
{
$entry = collect($filter->getProperties())->map(function ($value, $key) {
return $this->buildProperty($value, $key);
})->implode(PHP_EOL);
$entry = collect($filter->toArray())
->map(function ($value, $key): string {
return $this->buildProperty($value, $key);
})
->implode($this->glue());
return collect(['<entry>', $entry, '</entry>'])->implode(PHP_EOL);
return collect(['<entry>', $entry, '</entry>'])->implode($this->glue());
}
/**
@ -99,7 +105,7 @@ class Builder
*
* @return string
*/
private function buildProperty($value, $key)
private function buildProperty($value, $key): string
{
if (collect(['from', 'to'])->contains($key)) {
$value = $this->implode($value);
@ -114,7 +120,7 @@ class Builder
/**
* Implode values with the appropriate prefix, suffix and separator.
*/
private function implode($value, $separator = '|')
private function implode($value, $separator = '|'): string
{
if (is_string($value)) {
return $value;
@ -126,4 +132,9 @@ class Builder
return sprintf('(%s)', collect($value)->implode($separator));
}
private function glue(): ?string
{
return $this->expanded ? PHP_EOL : null;
}
}

View file

@ -14,7 +14,7 @@ class Partials
*
* @return array The loaded filters.
*/
public static function load($directoryName = 'filters')
public static function load($directoryName = 'filters'): array
{
$files = static::getFilePattern($directoryName);
@ -23,7 +23,7 @@ class Partials
return include $filename;
})
->flatten(1)
->all();
->toArray();
}
/**
@ -36,7 +36,7 @@ class Partials
*
* @return string The full path.
*/
protected static function getFilePattern($directoryName)
protected static function getFilePattern($directoryName): string
{
return getcwd() . DIRECTORY_SEPARATOR . $directoryName . DIRECTORY_SEPARATOR . '*.php';
}

View file

@ -0,0 +1,68 @@
<?php
namespace Opdavies\Tests\GmailFilterBuilder\Console\Command;
use Opdavies\GmailFilterBuilder\Console\Command\GenerateCommand;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Filesystem\Filesystem;
class GenerateFiltersTest extends TestCase
{
const INPUT_FILENAME = __DIR__ . '/../../../fixtures/simple/input.php';
const OUTPUT_FILENAME = 'test-output.xml';
/** @var CommandTester */
private $commandTester;
/** @var Filesystem */
private $fs;
protected function setUp()
{
parent::setUp();
$this->commandTester = new CommandTester(new GenerateCommand());
$this->fs = new Filesystem();
}
protected function tearDown()
{
// Ensure that files generated during tests are removed to prevent
// failures on future runs.
$this->fs->remove([self::OUTPUT_FILENAME]);
}
/** @test */
public function it_converts_filters_from_php_to_minified_xml()
{
$this->commandTester->execute([
'--input-file' => self::INPUT_FILENAME,
'--output-file' => self::OUTPUT_FILENAME,
]);
$this->assertTrue($this->fs->exists(self::OUTPUT_FILENAME));
$expected = file_get_contents(__DIR__ . '/../../../fixtures/simple/output.xml');
$result = file_get_contents(self::OUTPUT_FILENAME);
$this->assertEquals(trim($expected), $result);
}
/** @test */
public function it_converts_filters_from_php_to_expanded_xml()
{
$this->commandTester->execute([
'--input-file' => self::INPUT_FILENAME,
'--output-file' => self::OUTPUT_FILENAME,
'--expanded' => true,
]);
$this->assertTrue($this->fs->exists(self::OUTPUT_FILENAME));
$expected = file_get_contents(__DIR__ . '/../../../fixtures/simple/output-expanded.xml');
$result = file_get_contents(self::OUTPUT_FILENAME);
$this->assertEquals(trim($expected), $result);
}
}

View file

@ -26,9 +26,10 @@ class FilterTest extends TestCase
}
/**
* @covers ::has
* @test
* @covers::has
*/
public function testHas()
public function can_filter_on_a_has_value()
{
$this->assertEquals(
['hasTheWord' => 'something'],
@ -37,9 +38,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::hasNot
*/
public function testHasNot()
public function can_filter_on_a_has_not_value()
{
$this->assertEquals(
['doesNotHaveTheWord' => 'something'],
@ -48,9 +50,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::from
*/
public function testFrom()
public function can_filter_based_on_the_sender()
{
// Ensure that we can set one from address.
$this->assertEquals(
@ -66,20 +69,20 @@ class FilterTest extends TestCase
}
/**
* Test that no 'from' key exists if no values were entered.
*
* @test
* @covers ::from
*/
public function testNoFromPropertyExistsIfTheValueIsEmpty()
public function no_from_property_exists_if_the_value_is_empty()
{
$this->assertArrayNotHasKey('from', $this->filter->from('')->toArray());
$this->assertArrayNotHasKey('from', $this->filter->from([])->toArray());
}
/**
* @test
* @covers ::to
*/
public function testTo()
public function can_filter_based_on_the_recipient()
{
$this->assertEquals(
['to' => ['foo@example.com']],
@ -92,31 +95,24 @@ class FilterTest extends TestCase
);
}
/**
* Test that no 'to' key exists if values were entered.
*/
public function testNoToPropertyExistsIfTheValueIsEmpty()
/** @test */
public function no_to_property_exists_if_the_value_is_empty()
{
$this->assertArrayNotHasKey('to', $this->filter->to('')->toArray());
$this->assertArrayNotHasKey('to', $this->filter->to([])->toArray());
}
/**
* @test
* @covers ::subject
*/
public function testSubject()
public function can_filter_based_on_the_subject()
{
$this->assertEquals(
['subject' => '"Something"'],
$this->filter->subject('Something')->toArray()
);
}
/**
* Test that multiple subject conditions can be added.
*/
public function testMultipleSubjectsCanBeAdded()
{
$this->assertEquals(
['subject' => '"Test"|"Foo bar"'],
$this->filter->subject(['Test', 'Foo bar'])->toArray()
@ -124,9 +120,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::hasAttachment
*/
public function testHasAttachment()
public function can_filter_based_on_whether_there_is_an_attachment()
{
$this->assertEquals(
['hasAttachment' => 'true'],
@ -135,20 +132,27 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::fromList
*/
public function testFromList()
public function can_filter_based_on_wether_it_was_sent_to_a_list()
{
$this->assertEquals(
['hasTheWord' => 'list:foobar'],
$this->filter->fromList('foobar')->toArray()
);
$this->assertEquals(
['hasTheWord' => 'list:list-one.com|list-two.com'],
$this->filter->fromList(['list-one.com', 'list-two.com'])->toArray()
);
}
/**
* @test
* @covers ::excludeChats
*/
public function testExcludeChats()
public function chats_can_be_excluded()
{
$this->assertEquals(
['excludeChats' => 'true'],
@ -157,9 +161,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::label
*/
public function testLabel()
public function labels_can_be_added()
{
$this->assertEquals(
['label' => 'Foo'],
@ -168,9 +173,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::archive
*/
public function testArchive()
public function messages_can_be_archived()
{
$this->assertEquals(
['shouldArchive' => 'true'],
@ -179,9 +185,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::labelAndArchive
*/
public function testLabelAndArchive()
public function messages_can_be_labelled_and_archived()
{
$this->assertEquals(
['shouldArchive' => 'true', 'label' => 'Foo'],
@ -190,9 +197,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::spam
*/
public function testSpam()
public function messages_can_be_marked_as_spam()
{
$this->assertEquals(
[
@ -204,9 +212,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::neverSpam
*/
public function testNeverSpam()
public function messages_can_be_marked_as_not_spam()
{
$this->assertEquals(
[
@ -218,9 +227,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::trash
*/
public function testTrash()
public function messages_can_be_deleted()
{
$this->assertEquals(
['shouldTrash' => 'true'],
@ -229,9 +239,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::read
*/
public function testMarkAsRead()
public function messages_can_be_marked_as_read()
{
$this->assertEquals(
['markAsRead' => 'true'],
@ -240,9 +251,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::star
*/
public function testStar()
public function messages_can_be_starred()
{
$this->assertEquals(
['shouldStar' => 'true'],
@ -251,9 +263,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::forward
*/
public function testForwardTo()
public function messages_can_be_forwarded()
{
$this->assertEquals(
['forwardTo' => 'foo@example.com'],
@ -262,9 +275,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::important
*/
public function testMarkImportant()
public function messages_can_be_marked_as_important()
{
$this->assertEquals(
['shouldAlwaysMarkAsImportant' => 'true'],
@ -273,9 +287,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::notImportant
*/
public function testMarkNotImportant()
public function messages_can_be_marked_as_not_important()
{
$this->assertEquals(
['shouldNeverMarkAsImportant' => 'true'],
@ -284,9 +299,10 @@ class FilterTest extends TestCase
}
/**
* @test
* @covers ::categorise
*/
public function testCategorise()
public function messages_can_be_categorised()
{
$this->assertEquals(
['smartLabelToApply' => 'Foo'],
@ -294,7 +310,8 @@ class FilterTest extends TestCase
);
}
public function testMethodsCanBeChained()
/** @test */
public function methods_can_be_chained()
{
$this->assertEquals(
[

View file

@ -8,7 +8,8 @@ use PHPUnit\Framework\TestCase;
class BuilderTest extends TestCase
{
public function testBuild()
/** @test */
public function it_can_build_filters()
{
$filterA = (new Filter())
->from(['foo@example.com', 'test@example.com'])
@ -20,7 +21,7 @@ class BuilderTest extends TestCase
->star()
->important();
$result = new Builder([$filterA, $filterB], '', false);
$result = new Builder([$filterA, $filterB], '', false, true);
$expected = <<<EOF
<?xml version='1.0' encoding='UTF-8'?>

View file

@ -8,19 +8,17 @@ use PHPUnit\Framework\TestCase;
class PartialsTest extends TestCase
{
/**
* Test loading partials from multiple partial files.
*/
public function testLoadingFiltersFromPartials()
/** @test */
public function filters_can_be_loaded_from_partials()
{
/** @var Filter[] $filters */
$filters = FakePartials::load('filters');
$this->assertCount(3, $filters);
$this->assertSame('foo@example.com', $filters[0]->getProperties()['from'][0]);
$this->assertSame('bar@example.com', $filters[1]->getProperties()['from'][0]);
$this->assertSame('baz@example.com', $filters[2]->getProperties()['from'][0]);
$this->assertSame('foo@example.com', $filters[0]->toArray()['from'][0]);
$this->assertSame('bar@example.com', $filters[1]->toArray()['from'][0]);
$this->assertSame('baz@example.com', $filters[2]->toArray()['from'][0]);
}
}
@ -29,7 +27,7 @@ class FakePartials extends Partials
/**
* {@inheritdoc}
*/
protected static function getFilePattern($directoryName)
protected static function getFilePattern($directoryName): string
{
return __DIR__ . '/../../stubs/filters/*.php';
}

9
tests/fixtures/simple/input.php vendored Normal file
View file

@ -0,0 +1,9 @@
<?php
use Opdavies\GmailFilterBuilder\Model\Filter;
return [
Filter::create()
->from('example.com')
->labelAndArchive('Test'),
];

View file

@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
<entry>
<apps:property name='from' value='example.com'/>
<apps:property name='label' value='Test'/>
<apps:property name='shouldArchive' value='true'/>
</entry>
</feed>

1
tests/fixtures/simple/output.xml vendored Normal file
View file

@ -0,0 +1 @@
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'><entry><apps:property name='from' value='example.com'/><apps:property name='label' value='Test'/><apps:property name='shouldArchive' value='true'/></entry></feed>