Skip to content

Commit

Permalink
[BUGFIX] Add <head> element despite <header> (#853)
Browse files Browse the repository at this point in the history
Also test around the issue.

Resolves #844.
  • Loading branch information
JakeQZ authored Mar 30, 2020
1 parent 88bc3e0 commit ddf0174
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
([#773](https://github.com/MyIntervals/emogrifier/pull/773))

### Fixed
- Add missing `<head>` element when there's a `<header>` element
([#844](https://github.com/MyIntervals/emogrifier/pull/844),
[#853](https://github.com/MyIntervals/emogrifier/pull/853))
- Fix mapping width/height when decimal is used
([#845](https://github.com/MyIntervals/emogrifier/pull/845))
- Actually use the specified PHP version on GitHub actions
Expand Down
8 changes: 6 additions & 2 deletions src/HtmlProcessor/AbstractHtmlProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,15 @@ private function addContentTypeMetaTag(string $html): string

// We are trying to insert the meta tag to the right spot in the DOM.
// If we just prepended it to the HTML, we would lose attributes set to the HTML tag.
$hasHeadTag = \stripos($html, '<head') !== false;
$hasHeadTag = \preg_match('/<head[\\s>]/i', $html);
$hasHtmlTag = \stripos($html, '<html') !== false;

if ($hasHeadTag) {
$reworkedHtml = \preg_replace('/<head(.*?)>/i', '<head$1>' . static::CONTENT_TYPE_META_TAG, $html);
$reworkedHtml = \preg_replace(
'/<head(?=[\\s>])([^>]*+)>/i',
'<head$1>' . static::CONTENT_TYPE_META_TAG,
$html
);
} elseif ($hasHtmlTag) {
$reworkedHtml = \preg_replace(
'/<html(.*?)>/i',
Expand Down
12 changes: 12 additions & 0 deletions tests/Unit/CssInlinerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2252,6 +2252,18 @@ public function inlineCssAppliesCssToMatchingElementsAndKeepsRuleWithPseudoCompo
self::assertContainsCss('<style type="text/css">a:hover { color: green; }</style>', $result);
}

/**
* @test
*/
public function inlineCssKeepsRuleWithPseudoComponentInMatchingSelectorForHtmlWithHeader()
{
$subject = $this->buildDebugSubject('<html><header><a>foo</a></header></html>');

$subject->inlineCss('a:hover { color: green; }');

self::assertContainsCss('<style type="text/css">a:hover { color: green; }</style>', $subject->render());
}

/**
* @return string[][]
*/
Expand Down
89 changes: 82 additions & 7 deletions tests/Unit/HtmlProcessor/AbstractHtmlProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ public function contentWithoutHeadTagDataProvider(): array
'doctype only' => ['<!DOCTYPE html>'],
'body content only' => ['<p>Hello</p>'],
'BODY element' => ['<body></body>'],
'HEADER element' => ['<header></header>'],
'META element (implicit HEAD)' => ['<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'],
];
}

Expand All @@ -185,13 +187,57 @@ public function contentWithoutHeadTagDataProvider(): array
*
* @dataProvider contentWithoutHeadTagDataProvider
*/
public function addsMissingHeadTag(string $html)
public function addsMissingHeadTagOnlyOnce(string $html)
{
$subject = TestingHtmlProcessor::fromHtml($html);

$result = $subject->render();

self::assertContains('<head>', $result);
$headTagCount = \substr_count($result, '<head>');
self::assertSame(1, $headTagCount);
}

/**
* @return string[][]
*/
public function contentWithHeadTagDataProvider(): array
{
return [
'HEAD element' => ['<head></head>'],
'HEAD element, capitalized' => ['<HEAD></HEAD>'],
'(invalid) void HEAD element' => ['<head/>'],
'HEAD element with attribute' => ['<head lang="en"></head>'],
'HEAD element and HEADER element' => ['<head></head><header></header>'],
];
}

/**
* @test
*
* @param string $html
*
* @dataProvider contentWithHeadTagDataProvider
*/
public function notAddsSecondHeadTag(string $html)
{
$subject = TestingHtmlProcessor::fromHtml($html);

$result = $subject->render();

$headTagCount = \preg_match_all('%<head[\\s/>]%', $result);
self::assertSame(1, $headTagCount);
}

/**
* @test
*/
public function preservesHeadAttributes()
{
$subject = TestingHtmlProcessor::fromHtml('<head lang="en"></head>');

$result = $subject->render();

self::assertContains('<head lang="en">', $result);
}

/**
Expand Down Expand Up @@ -319,22 +365,51 @@ public function keepsExistingDocumentType(string $documentType)

/**
* @test
*
* @param string $html
*
* @dataProvider contentWithoutHeadTagDataProvider
* @dataProvider contentWithHeadTagDataProvider
*/
public function addsMissingContentTypeMetaTag()
public function addsMissingContentTypeMetaTagOnlyOnce(string $html)
{
$subject = TestingHtmlProcessor::fromHtml('<p>Hello</p>');
$subject = TestingHtmlProcessor::fromHtml($html);

$result = $subject->render();

self::assertContains('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">', $result);
$numberOfContentTypeMetaTags = \substr_count(
$result,
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
);
self::assertSame(1, $numberOfContentTypeMetaTags);
}

/**
* @return string[][]
*/
public function htmlAroundContentTypeDataProvider(): array
{
return [
'HTML and HEAD element' => ['<html><head>', '</head></html>'],
'HTML and HEAD element, HTML end tag omitted' => ['<html><head>', '</head>'],
'HEAD element only' => ['<head>', '</head>'],
'HEAD element with attribute' => ['<head lang="en">', '</head>'],
'HTML, HEAD, and BODY with HEADER elements'
=> ['<html><head>', '</head><body><header></header></body></html>'],
];
}

/**
* @test
*
* @param string $htmlBefore
* @param string $htmlAfter
*
* @dataProvider htmlAroundContentTypeDataProvider
*/
public function notAddsSecondContentTypeMetaTag()
public function notAddsSecondContentTypeMetaTag(string $htmlBefore, string $htmlAfter)
{
$html = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>';
$html = $htmlBefore . '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $htmlAfter;
$subject = TestingHtmlProcessor::fromHtml($html);

$result = $subject->render();
Expand Down

0 comments on commit ddf0174

Please sign in to comment.