Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 80 additions & 51 deletions tests/Unit/Html/SubsetHtmlParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,78 @@
use DOMDocument;
use LibreSign\XObjectTemplate\Exception\UnsupportedSubsetException;
use LibreSign\XObjectTemplate\Html\SubsetHtmlParser;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

final class SubsetHtmlParserTest extends TestCase
{
public function testUnsupportedTagThrowsException(): void
/**
* @return iterable<string, array{0: string, 1: string}>
*/
public static function unsupportedTagProvider(): iterable
{
yield 'table is outside the supported subset' => [
'<table><tr><td>x</td></tr></table>',
'Tag <table> is not supported.',
];

yield 'unordered lists are outside the supported subset' => [
'<ul><li>x</li></ul>',
'Tag <ul> is not supported.',
];

yield 'semantic strong tags are outside the supported subset' => [
'<strong>x</strong>',
'Tag <strong> is not supported.',
];
}

/**
* @return iterable<string, array{0: string, 1: string, 2: string, 3: string, 4: list<string>}>
*/
public static function inheritableStyleProvider(): iterable
{
yield 'layout-only styles stay on parent while text styles inherit' => [
'<div style="width:58%;height:100%;padding:18 24;font-size:20;color:#123456">'
. '<div style="font-weight:700">Title</div>'
. '</div>',
'width:58%;height:100%;padding:18 24;font-size:20;color:#123456',
'font-size:20;color:#123456;font-weight:700',
'font-size:20;color:#123456;font-weight:700',
['width:58%', 'height:100%', 'padding:18 24'],
];

yield 'text alignment white-space and color inherit together' => [
'<div style="text-align:right;white-space:nowrap;color:#222222;font-size:11">'
. '<span>Aligned</span>'
. '</div>',
'text-align:right;white-space:nowrap;color:#222222;font-size:11',
'text-align:right;white-space:nowrap;color:#222222;font-size:11',
'text-align:right;white-space:nowrap;color:#222222;font-size:11',
[],
];

yield 'malformed declarations preserve last inheritable values and colon values' => [
'<div style=" ; COLOR : #fff ; broken ; font-family : Times:Bold ; invalid: ; '
. 'white-space : nowrap ; hyphens : auto ; color : #abc ; line-height : 12 ; ">'
. '<span style="font-weight:bold">Hello</span>'
. '</div>',
'; COLOR : #fff ; broken ; font-family : Times:Bold ; invalid: ; white-space : nowrap ; '
. 'hyphens : auto ; color : #abc ; line-height : 12 ;',
'color:#abc;font-family:Times:Bold;white-space:nowrap;hyphens:auto;line-height:12;font-weight:bold',
'color:#abc;font-family:Times:Bold;white-space:nowrap;hyphens:auto;line-height:12;font-weight:bold',
[],
];
}

#[DataProvider('unsupportedTagProvider')]
public function testUnsupportedTagThrowsException(string $html, string $expectedMessage): void
{
$parser = new SubsetHtmlParser();

$this->expectException(UnsupportedSubsetException::class);
$this->expectExceptionMessage('Tag <table> is not supported.');
$parser->parse('<table><tr><td>x</td></tr></table>');
$this->expectExceptionMessage($expectedMessage);
$parser->parse($html);
}

public function testParseNormalizesAttributesAndTrimsTextNodes(): void
Expand Down Expand Up @@ -62,31 +123,25 @@ public function testParseMergesInheritedStylesAndKeepsAllowedTags(): void
self::assertSame('font-size:10; margin:2', $nodes[0]->children[2]->attributes['style']);
}

public function testParseOnlyInheritsTextualStylesToDescendants(): void
{
#[DataProvider('inheritableStyleProvider')]
public function testParseOnlyInheritsTextualStylesToDescendants(
string $html,
string $expectedRootStyle,
string $expectedChildStyle,
string $expectedTextStyle,
array $excludedFragments,
): void {
$parser = new SubsetHtmlParser();

$nodes = $parser->parse(
'<div style="width:58%;height:100%;padding:18 24;font-size:20;color:#123456">'
. '<div style="font-weight:700">Title</div>'
. '</div>',
);
$nodes = $parser->parse($html);

self::assertSame(
'width:58%;height:100%;padding:18 24;font-size:20;color:#123456',
$nodes[0]->attributes['style'],
);
self::assertSame(
'font-size:20;color:#123456;font-weight:700',
$nodes[0]->children[0]->attributes['style'],
);
self::assertSame(
'font-size:20;color:#123456;font-weight:700',
$nodes[0]->children[0]->children[0]->attributes['style'],
);
self::assertStringNotContainsString('width:58%', $nodes[0]->children[0]->attributes['style']);
self::assertStringNotContainsString('height:100%', $nodes[0]->children[0]->attributes['style']);
self::assertStringNotContainsString('padding:18 24', $nodes[0]->children[0]->attributes['style']);
self::assertSame($expectedRootStyle, $nodes[0]->attributes['style']);
self::assertSame($expectedChildStyle, $nodes[0]->children[0]->attributes['style']);
self::assertSame($expectedTextStyle, $nodes[0]->children[0]->children[0]->attributes['style']);

foreach ($excludedFragments as $excludedFragment) {
self::assertStringNotContainsString($excludedFragment, $nodes[0]->children[0]->attributes['style']);
}
}

public function testParseNormalizesTagAndAttributeNamesAndKeepsAllAttributes(): void
Expand Down Expand Up @@ -178,32 +233,6 @@ public function testParseKeepsAllTopLevelNodesInOrder(): void
$this->assertSame('Second', $nodes[1]->children[0]->text);
}

public function testParseFiltersInheritedStylesAfterMalformedDeclarationsAndPreservesColonValues(): void
{
$parser = new SubsetHtmlParser();

$nodes = $parser->parse(
'<div style=" ; COLOR : #fff ; broken ; font-family : Times:Bold ; invalid: ; '
. 'white-space : nowrap ; hyphens : auto ; color : #abc ; line-height : 12 ; ">'
. '<span style="font-weight:bold">Hello</span>'
. '</div>',
);

$this->assertSame(
'; COLOR : #fff ; broken ; font-family : Times:Bold ; invalid: ; white-space : nowrap ; '
. 'hyphens : auto ; color : #abc ; line-height : 12 ;',
$nodes[0]->attributes['style'],
);
$this->assertSame(
'color:#abc;font-family:Times:Bold;white-space:nowrap;hyphens:auto;line-height:12;font-weight:bold',
$nodes[0]->children[0]->attributes['style'],
);
$this->assertSame(
'color:#abc;font-family:Times:Bold;white-space:nowrap;hyphens:auto;line-height:12;font-weight:bold',
$nodes[0]->children[0]->children[0]->attributes['style'],
);
}

public function testParseClearsPreExistingLibxmlErrorBuffer(): void
{
$parser = new SubsetHtmlParser();
Expand Down
Loading
Loading