diff --git a/README.md b/README.md index 38e70c4..7bde4fc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [PHPUnit](https://phpunit.de/) Testing extensions for HMTL and CSS. **PHPFUI\HTMLUnitTester** allows you to unit test HTML and CSS for errors and warnings. Often simple errors in HTML or CSS create hard to debug issues where a simple check will reveal bad code. -This package will check detect errors and warnings in HTML and CSS in stand alone strings, files or urls. +This package will check detect errors and warnings in HTML and CSS in stand alone strings, files, entire directories or urls. # Requirements - PHP 7.1 or higher - PHPUnit 7 or higher @@ -51,6 +51,14 @@ You can use any of the following asserts: - assertValidHtml - assertValidUrl +## Directory Testing +Instead of file by file testing, use **assertDirectory** to test an entire directory. Any files added to the directory will be automatically tested. +```php + $this->assertDirectory('ValidCSS', 'cssDirectory', 'Invalid CSS'); + $this->assertDirectory('NotWarningCSS', 'cssDirectory', 'CSS has warnings'); +``` +The error message will include the offending file name. + ## Examples See [examples](https://github.com/phpfui/HTMLUnitTester/blob/master/tests/UnitTest.php) diff --git a/composer.json b/composer.json index d478ac3..c4d0d83 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "rexxars/html-validator": "^2.2" }, "require-dev": { - "phpunit/phpunit": "~7.0" + "phpunit/phpunit": "^7.0|>=8.0" }, "autoload": { "psr-0": {"PHPFUI": "src/"} diff --git a/examples/subdirectory/valid.css b/examples/subdirectory/valid.css new file mode 100644 index 0000000..efcdcda --- /dev/null +++ b/examples/subdirectory/valid.css @@ -0,0 +1,1045 @@ +html, +body, +h1, +#banner, +#banner a { + margin: 0; + padding: 0; + border: 0; + font-weight: 400; + font-style: normal; + font-size: 100%; + font-family: sans-serif; + vertical-align: baseline; + background-color: #fff; + color: #1f2126; + line-height: 1; + text-align: left; +} + +a img { + border: 0; +} + +abbr[title], +acronym[title], +span[title], +strong[title] { + border-bottom: thin dotted; + cursor: help; +} + +acronym:hover, +abbr:hover { + cursor: help; +} + +body { + padding-bottom: 6em; +} + +h3 { + font-family: sans-serif; + color: #690; + background-color: #fff; + font-size: 1.1em; + border-bottom: 1px dotted #aa7; + text-decoration: none; + margin-top: 1em; + margin-bottom: 1em; +} + +textarea { + width: 95.5%; +} + +label { + font-size: 0.9em; + padding-left: 0.2em; + padding-right: 0.2em; +} + +#csslabel { + font-size: 11px; + margin-left: 4px; +} + +#title { + font-family: "Myriad Web", "Myriad Pro", "Gill Sans", Helvetica, Arial, sans-serif; + background-color: #365d95; + color: #fdfdfd; + font-size: 1.6em; + padding: 0.43em; + border-radius: 6px; +} +#title a, +#title a img { + background-color: #365d95; +} + +#title a:link, +#title a:hover, +#title a:visited, +#title a:active { + color: #fdfdfd !important; + text-decoration: none; +} + +#title img { + vertical-align: middle; + margin-right: 0.7em; +} + +#banner { + margin: 1.5em 2em; +} + +fieldset { + background-color: #eaebee; +} + +legend { + font-size: 1.1em; + padding: 1em 0 0.23em; +} + +legend a:link, +legend a:visited, +legend a:hover, +legend a:active { + text-decoration: none; +} + +div, +p, +li, +h2 { + font-family: sans-serif; +} + +h2 { + font-size: 16px; +} + +#top { + margin-left: 3%; +} + +th, +td { + padding-top: 1px; + padding-bottom: 1px; + margin: 0; +} + +hr { + margin-left: 3%; + margin-right: 3%; +} + +#banner { + margin-left: 3%; + margin-right: 3%; + margin-bottom: 18px; +} + +#results, +#about { + font-size: 14px; + margin-top: 50px; + padding-left: 3%; + margin-right: 3%; +} + +#about { + margin-bottom: 12px; +} + +p.disclaimer { + font: caption; + margin-left: 3%; + margin-right: 3%; + color: #747474; +} + +.alert { + color: #000; + background-color: #ff0; +} + +body { + font-family: sans-serif; + font-size: inherit; + margin: 0; +} + +form { + font: caption; + background-color: #fff; + margin-bottom: 0.5em; + padding: 3%; + padding-top: 0; + padding-bottom: 0; + vertical-align: inherit; +} + +legend { + font: inherit; + vertical-align: inherit; + padding: 0; +} + +fieldset { + font: caption; + padding: 0.5% 1.5% 0.5% 1.5%; + border: 1px solid #ccc; + border-radius: 4px; + margin: 0; + background-color: #fff; + vertical-align: inherit; +} + +label { + font: caption; + vertical-align: baseline; + padding: 0; +} + +select { + vertical-align: baseline; +} + +textarea { + font: caption; + font-size: 0.85em; + border: 1px solid; + margin: 2px; + padding: 2px; +} + +#inputregion { + margin-top: 12px; +} + +#inputlabel { + margin-left: 2px; +} + +#doc { + width: 95.5%; + margin-left: 2px; + margin-top: 2px; + padding: 3px 5px 2px 5px; + border-radius: 6px; + border: solid #ccc 1px; +} + +#doc[type="file"] { + margin-top: -3px; + width: initial; + border: 0; +} + +#docselect { + margin-left: 3px; +} + +#submit { + margin-left: 2px; +} + +table { + width: 100%; + table-layout: fixed; +} + +fieldset td, +fieldset th { + padding-top: 0.4em; + padding-bottom: 0.4em; +} + +tr:first-child td, +tr:first-child th { + vertical-align: top; +} + +fieldset th { + padding-left: 0; + padding-right: 0.4em; + text-align: right; + width: 8em; + font-weight: inherit; +} + +fieldset td { + padding-left: 0; + padding-right: 0; +} + +textarea, +#doc[type="url"] { + font-family: Monaco, Consolas, "Andale Mono", monospace; +} + +.stats, +.details { + font-size: 0.85em; +} + +.details .msgschema { + display: none; +} + +.details p { + margin: 0; +} + +.success, +.failure, +.fatalfailure { + border-radius: 4px; + padding: 0.5em; + font-weight: 700; + font-family: Arial, sans-serif; +} + +.success { + color: #000; + background-color: #cfc; + border: 1px solid #ccc; +} + +.failure { + color: #fff; + background-color: #365d95; +} + +.fatalfailure { + color: #fff; + background-color: #f00; +} + +ol { + margin: 1.5em 0; + padding: 0 2.5em; +} + +li { + margin: 0; + padding: 0.5em; +} + +li ol { + padding-right: 0; + margin-top: 0.5em; + margin-bottom: 0; +} + +li li { + padding-right: 0; + padding-bottom: 0.2em; + padding-top: 0.2em; +} + +#results > ol:first-child { + padding-top: 35px; + padding-bottom: 25px; + background-color: #efefef; + border-radius: 4px; +} + +#results > ol:first-child > li { + border: 1px solid #ccc; + margin-bottom: 8px; + padding-left: 12px; + border-radius: 4px; + background-color: #fff; +} + +#results > ol:first-child > li > p:first-child, +#results > ol:first-child > li > p:first-child code { + font-size: 16px; + font-weight: 700; +} + +#results > ol:first-child > li > p:first-child code { + font-weight: 400; +} + +#results > ol:first-child > li > p:first-child { + color: transparent; +} + +#results > ol:first-child > li > p:first-child > strong, +#results > ol:first-child > li > p:first-child > span { + color: #000; +} + +#results > ol:first-child > li > p:first-child > strong:first-child { + padding: 1px 6px; + border-radius: 6px; + border: 1px solid #ccc; + font: caption; + font-weight: 700; +} + +.info, +.warning, +.error, +.io, +.fatal, +.schema, +.internal { + color: #000; +} + +.info > p:first-child > strong:first-child { + background-color: #cfc; +} + +.warning > p:first-child > strong:first-child { + background-color: #ffc; +} + +.error > p:first-child > strong:first-child, +.io > p:first-child > strong:first-child, +.fatal > p:first-child > strong:first-child, +.schema > p:first-child > strong:first-child, +.internal > p:first-child > strong:first-child { + background-color: #fcc; +} + +hr { + border-top: 1px dotted #666; + border-bottom: none; + border-left: none; + border-right: none; + height: 0; +} + +p { + margin: 0.5em 0 0.5em 0; +} + +li p { + margin: 0; +} + +.stats, +.details { + margin-top: 0.75em; +} + +.lf { + color: #222; +} + +.extract { + overflow: hidden; + max-height: 5.5em; +} + +.extract b, +.source b { + color: #000; + background-color: #ffff80; +} + +.extract b { + font-weight: 400; +} + +ol.source li { + padding-top: 0; + padding-bottom: 0; +} + +ol.source b, +ol.source .b { + color: #000; + background-color: #ffff80; + font-weight: 700; +} + +code { + white-space: pre; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + white-space: -moz-pre-wrap; + white-space: -hp-pre-wrap; + white-space: pre-wrap; + word-wrap: break-word; +} + +.error p, +.info p, +.warning p, +.error dd, +.info dd, +.warning dd { + line-height: 1.8; +} + +.error p code { + border: 1px dashed #999; + padding: 2px; + padding-left: 4px; + padding-right: 4px; +} + +.warning code { + border: 1px dashed #999; + padding: 2px; + padding-left: 4px; + padding-right: 4px; +} + +.extract code { + background-color: inherit; + border: none; + padding: 0; +} + +dl { + margin-top: 0.5em; + margin-bottom: 0; +} + +dd { + margin-left: 1.5em; + padding-left: 0; + margin-top: 2px; +} + +table.imagereview { + width: 100%; + table-layout: auto; + border-collapse: collapse; + border-spacing: 0; +} + +col.img { + width: 180px; +} + +col.alt { + color: #000; + background-color: #ffc; +} + +td.alt span { + color: #000; + background-color: #ffa; +} + +.imagereview th { + font-weight: 700; + text-align: left; + vertical-align: bottom; +} + +.imagereview td { + vertical-align: middle; +} + +td.img { + padding: 0 0.5em 0.5em 0; + text-align: right; +} + +img { + max-height: 180px; + max-width: 180px; + -ms-interpolation-mode: bicubic; +} + +th.img { + padding: 0 0.5em 0.5em 0; + vertical-align: bottom; + text-align: right; +} + +td.alt, +td.location { + text-align: left; + padding: 0 0.5em 0.5em 0.5em; +} + +th.alt, +th.location { + padding: 0 0.5em 0.5em 0.5em; + vertical-align: bottom; +} + + +*[irrelevent], +.irrelevant { + display: none; +} + +/* "(required)" text in spec advice */ +dd code ~ span { + color: #666; +} + +dl.inputattrs { + display: table; +} + +dl.inputattrs dt { + display: table-caption; +} + +dl.inputattrs dd { + display: table-row; +} + +dl.inputattrs .inputattrname, +dl.inputattrs .inputattrtypes, +dl.inputattrs > dd > a { + display: table-cell; + padding-top: 2px; + padding-left: 1.5em; + padding-right: 1.5em; +} + +dl.inputattrs .inputattrtypes { + padding-left: 4px; + padding-right: 4px; +} + +.inputattrtypes > a { + color: #666; +} + +dl.inputattrs .highlight { + background-color: #ffc; + padding-bottom: 2px; + font-weight: 400; + color: #666; +} + +@media all and (max-width: 24em) { + body { + padding: 3px; + } + + table, + thead, + tfoot, + tbody, + tr, + th, + td { + display: block; + width: 100%; + } + + th { + text-align: left; + padding-bottom: 0; + } +} + +.checkboxes label { + margin-right: 20px; +} + +#xnote { + color: #747474; + margin-left: 2px; + margin-top: 6px; +} + +#outline h2 { + margin-top: 24px; + margin-bottom: 0; +} + +#outline .heading { + color: #bf4f00; + font-weight: 700; +} + +#outline ol { + margin-top: 0; + padding-top: 1px; +} + +#outline > h2 + ol { + margin-top: 0; +} + +#outline li { + padding: 0 0 9px 0; + margin: 0; + list-style-type: none; + position: relative; +} + +#outline li:first-child { + padding-top: 7px; +} + +#outline li li { + list-style-type: none; +} + +#outline li:first-child::before { + position: absolute; + top: 1px; + height: 13px; + left: -0.75em; + width: 0.5em; + border-color: #bbb; + border-style: none none solid solid; + content: ""; + border-width: 0.1em; +} + +#outline li:not(:last-child)::after { + position: absolute; + top: 1px; + bottom: -0.6em; + left: -0.75em; + width: 0.5em; + border-color: #bbb; + border-style: none none solid solid; + content: ""; + border-width: 0.1em; +} + +#results .hidden { + display: none; +} + +#filters { + margin-left: 3%; + margin-right: 3%; + margin-bottom: 1em; + padding: 4px; + padding-bottom: 0.5em; +} + +#filters > div { + font: caption; + color: #747474; + background-color: #ffc; + padding: 4px; +} + +#filters button { + min-height: 18px; + border-radius: 6px; + border: solid #ccc 1px; + background-color: #ffc; +} + +.message_filtering { + background-color: #fff !important; + -o-transition: all 1.5s ease-out; + -webkit-transition: all 1.5s ease-out; + transition: all 1.5s ease-out; +} + +#filters .filtercount { + font: caption; + font-style: italic; + padding-left: 4px; + padding-top: 6px; +} + +#filters.expanded { + background-color: #ffc; + -o-transition: all 0.8s ease-out; + -webkit-transition: all 0.8s ease-out; + transition: all 0.8s ease-out; + opacity: 1; + visibility: visible; +} + +#filters.unexpanded { + -o-transition: all 0.8s ease-out; + -webkit-transition: all 0.8s ease-out; + transition: all 0.8s ease-out; +} + +#filters h2 { + margin-top: 4px; + margin-bottom: 0; +} + +#filters fieldset { + margin: 1em; + font: caption; +} + +#filters fieldset.hidden, +.checkboxes input.hidden, +.checkboxes .extraoptions.hidden { + -o-transition: all 0.8s ease-out; + -webkit-transition: all 0.8s ease-out; + transition: all 0.8s ease-out; + opacity: 0; + visibility: hidden; + height: 0; + margin: -45px; +} + +#filters fieldset.unhidden, +.checkboxes input.unhidden, +.checkboxes .extraoptions.unhidden { + -o-transition: all 0.8s ease-out; + -webkit-transition: all 0.8s ease-out; + transition: all 0.8s ease-out; + opacity: 1; + visibility: visible; +} + +.checkboxes .extraoptions label { + margin-right: 0; +} + +#filters fieldset.unhidden, +.checkboxes input.unhidden { + background-color: #ffc; +} + +#filters fieldset legend { + font-weight: 700; + font-family: sans-serif; +} + +#filters fieldset legend a { + font-weight: 400; +} + +#filters input { + position: absolute; + top: 2px; + left: 0; +} + +#filters ol { + counter-reset: item; + margin: 0; + padding: 0 0 0 40px; +} + +#filters ol ol { + margin-top: 6px; +} + +#filters li { + list-style-type: none; + padding: 0 0 0 25px; + margin: 6px 0 0; + position: relative; + line-height: 1.8; +} + +#filters li li { + margin: 0; +} + +#filters li::before { + content: counters(item, ".") " "; + counter-increment: item; + width: 40px; + position: absolute; + top: 0; + left: -50px; + text-align: right; + color: #777; +} + +#filters fieldset label { + font-family: sans-serif; +} + +#filters code { + white-space: pre; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + white-space: -moz-pre-wrap; + white-space: -hp-pre-wrap; + white-space: pre-wrap; + word-wrap: break-word; + border: 1px dashed #999; + background-color: #ffd; + padding: 2px 4px; +} + +#filters .hide, +#filters .show { + color: #00f; +} + +.checkboxes { + margin-left: 2px; +} + +.checkboxes label { + margin-right: 16px; +} + +.checkboxgroup { + padding: 3px 6px 2px 2px; + border-radius: 6px; + border: solid #ccc 1px; + font-size: 11px; + margin-right: 10px; +} + +.checkboxgroup label { + font-size: 11px; +} + +label[for="showimagereport"] { + margin-right: 0; +} + +input[list="useragents"], +input#acceptlanguage { + padding: 2px 4px 3px 4px; + border-radius: 6px; + border: solid #ccc 1px; +} + +input[list="useragents"] { + width: 30.5%; + margin-right: 10px; +} + +input#acceptlanguage { + width: 55px; +} + +@media (max-width: 1234px) { + #accept-language-label { + display: block; + margin-top: 8px; + } +} + +@media (min-width: 954px) { + .checkboxes .extraoptions { + padding-left: 8px; + } +} + +@media (max-width: 953px) { + .extraoptions { + display: block; + margin-top: 8px; + padding-left: 0; + } + + .checkboxes { + margin-bottom: -43px; + } + + #inputregion { + margin-top: 76px; + } + + .extraoptions.unhidden { + margin-bottom: -68px; + } + + input[list="useragents"] { + width: 350px; + } + + #accept-language-label { + display: inline; + } +} + +@media (max-width: 836px) { + #accept-language-label { + display: block; + margin-top: 8px; + } +} + +@media (max-width: 640px) { + #show_options { + display: block; + margin-top: 8px; + padding-left: 0; + } + + #inputregion { + margin-top: 100px; + } + + .extraoptions.unhidden { + margin-bottom: -90px; + } + + #user-agent-label { + display: block; + margin-top: 8px; + } + + input[list="useragents"] { + width: 96.9%; + } + + #about { + font-size: 11px; + } + + .checkboxes { + font-size: 12px; + } +} + +#headingoutline p { + margin: 10px; + font-weight: 700; + color: #bf4f00; +} + +.h2 { + padding-left: 24px; +} + +.h3 { + padding-left: 48px; +} + +.h4 { + padding-left: 72px; +} + +.h5 { + padding-left: 96px; +} + +.h6 { + padding-left: 120px; +} + +.headinglevel, +.missingheadinglevel { + border-radius: 4px; + padding: 1px 3px 1px 4px; + font-size: 80%; + font-weight: 400; + color: #fff; +} + +.headinglevel { + background-color: #008000; +} + +.missingheadinglevel { + background-color: #800000; +} + +.missingheading { + color: #747474; +} diff --git a/examples/subdirectory/valid.html b/examples/subdirectory/valid.html new file mode 100644 index 0000000..ee6418e --- /dev/null +++ b/examples/subdirectory/valid.html @@ -0,0 +1 @@ +