1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-02 12:21:09 +02:00

[1.7.0] Refactor HTMLModule unit tests

- AttrCollections does not barf when an inclusion is not present
- HTMLDefinition configuration directives now use new syntax
- Added %HTML.AllowedModules and %HTML.CoreModules for testing
- Extend Harness so that it can accept a default configuration object member variable
- Refactor modules to use Scaffolding, which defines some custom attributes that allows for the easy testing of attribute collections

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1082 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2007-05-20 22:29:31 +00:00
parent 2945f6a930
commit a470fc5621
12 changed files with 190 additions and 67 deletions

View File

@@ -63,6 +63,7 @@ class HTMLPurifier_AttrCollections
if (isset($seen[$merge[$i]])) continue; if (isset($seen[$merge[$i]])) continue;
$seen[$merge[$i]] = true; $seen[$merge[$i]] = true;
// foreach attribute of the inclusion, copy it over // foreach attribute of the inclusion, copy it over
if (!isset($this->info[$merge[$i]])) continue;
foreach ($this->info[$merge[$i]] as $key => $value) { foreach ($this->info[$merge[$i]] as $key => $value) {
if (isset($attr[$key])) continue; // also catches more inclusions if (isset($attr[$key])) continue; // also catches more inclusions
$attr[$key] = $value; $attr[$key] = $value;

View File

@@ -7,48 +7,63 @@ require_once 'HTMLPurifier/HTMLModuleManager.php';
// outside of the HTML or Attr namespaces // outside of the HTML or Attr namespaces
HTMLPurifier_ConfigSchema::define( HTMLPurifier_ConfigSchema::define(
'HTML', 'BlockWrapper', 'p', 'string', 'HTML', 'BlockWrapper', 'p', 'string', '
'String name of element to wrap inline elements that are inside a block '. <p>
'context. This only occurs in the children of blockquote in strict mode. '. String name of element to wrap inline elements that are inside a block
'Example: by default value, <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> '. context. This only occurs in the children of blockquote in strict mode.
'would become <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>. The '. </p>
'<code>&lt;p&gt;</code> tags can be replaced '. <p>
'with whatever you desire, as long as it is a block level element. '. Example: by default value,
'This directive has been available since 1.3.0.' <code>&lt;blockquote&gt;Foo&lt;/blockquote&gt;</code> would become
); <code>&lt;blockquote&gt;&lt;p&gt;Foo&lt;/p&gt;&lt;/blockquote&gt;</code>.
The <code>&lt;p&gt;</code> tags can be replaced with whatever you desire,
as long as it is a block level element. This directive has been available
since 1.3.0.
</p>
');
HTMLPurifier_ConfigSchema::define( HTMLPurifier_ConfigSchema::define(
'HTML', 'Parent', 'div', 'string', 'HTML', 'Parent', 'div', 'string', '
'String name of element that HTML fragment passed to library will be '. <p>
'inserted in. An interesting variation would be using span as the '. String name of element that HTML fragment passed to library will be
'parent element, meaning that only inline tags would be allowed. '. inserted in. An interesting variation would be using span as the
'This directive has been available since 1.3.0.' parent element, meaning that only inline tags would be allowed.
); This directive has been available since 1.3.0.
</p>
');
HTMLPurifier_ConfigSchema::define( HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedElements', null, 'lookup/null', 'HTML', 'AllowedElements', null, 'lookup/null', '
'If HTML Purifier\'s tag set is unsatisfactory for your needs, you '. <p>
'can overload it with your own list of tags to allow. Note that this '. If HTML Purifier\'s tag set is unsatisfactory for your needs, you
'method is subtractive: it does its job by taking away from HTML Purifier '. can overload it with your own list of tags to allow. Note that this
'usual feature set, so you cannot add a tag that HTML Purifier never '. method is subtractive: it does its job by taking away from HTML Purifier
'supported in the first place (like embed, form or head). If you change this, you '. usual feature set, so you cannot add a tag that HTML Purifier never
'probably also want to change %HTML.AllowedAttributes. '. supported in the first place (like embed, form or head). If you
'<strong>Warning:</strong> If another directive conflicts with the '. change this, you probably also want to change %HTML.AllowedAttributes.
'elements here, <em>that</em> directive will win and override. '. </p>
'This directive has been available since 1.3.0.' <p>
); <strong>Warning:</strong> If another directive conflicts with the
elements here, <em>that</em> directive will win and override.
This directive has been available since 1.3.0.
</p>
');
HTMLPurifier_ConfigSchema::define( HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedAttributes', null, 'lookup/null', 'HTML', 'AllowedAttributes', null, 'lookup/null', '
'IF HTML Purifier\'s attribute set is unsatisfactory, overload it! '. <p>
'The syntax is \'tag.attr\' or \'*.attr\' for the global attributes '. If HTML Purifier\'s attribute set is unsatisfactory, overload it!
'(style, id, class, dir, lang, xml:lang).'. The syntax is "tag.attr" or "*.attr" for the global attributes
'<strong>Warning:</strong> If another directive conflicts with the '. (style, id, class, dir, lang, xml:lang).
'elements here, <em>that</em> directive will win and override. For '. </p>
'example, %HTML.EnableAttrID will take precedence over *.id in this '. <p>
'directive. You must set that directive to true before you can use '. <strong>Warning:</strong> If another directive conflicts with the
'IDs at all. This directive has been available since 1.3.0.' elements here, <em>that</em> directive will win and override. For
); example, %HTML.EnableAttrID will take precedence over *.id in this
directive. You must set that directive to true before you can use
IDs at all. This directive has been available since 1.3.0.
</p>
');
/** /**
* Definition of the purified HTML that describes allowed children, * Definition of the purified HTML that describes allowed children,

View File

@@ -53,6 +53,43 @@ HTMLPurifier_ConfigSchema::define(
'will be used. This directive has been available since 1.7.0.' 'will be used. This directive has been available since 1.7.0.'
); );
HTMLPurifier_ConfigSchema::define(
'HTML', 'AllowedModules', null, 'lookup/null', '
<p>
A doctype comes with a set of usual modules to use. Without having
to mucking about with the doctypes, you can quickly activate or
disable these modules by specifying which modules you wish to allow
with this directive. This is most useful for unit testing specific
modules, although end users may find it useful for their own ends.
</p>
<p>
If you specify a module that does not exist, the manager will silently
fail to use it, so be careful! User-defined modules are not affected
by this directive. Modules defined in %HTML.CoreModules are not
affected by this directive. This directive has been available since 1.7.0.
</p>
');
HTMLPurifier_ConfigSchema::define(
'HTML', 'CoreModules', array(
'Structure' => true,
'Text' => true,
'Hypertext' => true,
'List' => true,
'NonXMLCommonAttributes' => true,
'XMLCommonAttributes' => true,
'CommonAttributes' => true
), 'lookup', '
<p>
Certain modularized doctypes (XHTML, namely), have certain modules
that must be included for the doctype to be an conforming document
type: put those modules here. By default, XHTML\'s core modules
are used. You can set this to a blank array to disable core module
protection, but this is not recommended. This directive has been
available since 1.7.0.
</p>
');
class HTMLPurifier_HTMLModuleManager class HTMLPurifier_HTMLModuleManager
{ {
@@ -254,6 +291,17 @@ class HTMLPurifier_HTMLModuleManager
$doctype = $this->doctypes->make($config); $doctype = $this->doctypes->make($config);
$modules = $doctype->modules; $modules = $doctype->modules;
// take out the default modules that aren't allowed
$lookup = $config->get('HTML', 'AllowedModules');
$special_cases = $config->get('HTML', 'CoreModules');
if (is_array($lookup)) {
foreach ($modules as $k => $m) {
if (isset($special_cases[$m])) continue;
if (!isset($lookup[$m])) unset($modules[$k]);
}
}
// merge in custom modules // merge in custom modules
$modules = array_merge($modules, $this->userModules); $modules = array_merge($modules, $this->userModules);
@@ -291,7 +339,6 @@ class HTMLPurifier_HTMLModuleManager
// the module in your custom doctype should be sufficient // the module in your custom doctype should be sufficient
$this->modules $this->modules
); );
} }
/** /**

View File

@@ -70,7 +70,7 @@ class HTMLPurifier_AttrCollectionsTest extends UnitTestCase
$types = new HTMLPurifier_AttrTypesMock($this); $types = new HTMLPurifier_AttrTypesMock($this);
$collections = new HTMLPurifier_AttrCollections($types, array()); $collections = new HTMLPurifier_AttrCollections($types, array());
$collections->info = array( $collections->info = array(
'Core' => array(0 => array('Inclusion'), 'attr-original' => 'Type'), 'Core' => array(0 => array('Inclusion', 'Undefined'), 'attr-original' => 'Type'),
'Inclusion' => array(0 => array('SubInclusion'), 'attr' => 'Type'), 'Inclusion' => array(0 => array('SubInclusion'), 'attr' => 'Type'),
'SubInclusion' => array('attr2' => 'Type') 'SubInclusion' => array('attr2' => 'Type')
); );

View File

@@ -7,21 +7,15 @@ class HTMLPurifier_HTMLModule_BdoTest extends HTMLPurifier_HTMLModuleHarness
function test() { function test() {
$this->setupScaffold('Bdo');
// max // max
$this->assertResult( $this->assertResult(
'<span> '<span>
<bdo <bdo ac:core="yes" dir="rtl">
id="test-id"
class="class-name"
style="font-weight:bold;"
title="Title of tag"
lang="en"
xml:lang="en"
dir="rtl"
>
#PCDATA <span>Inline</span> #PCDATA <span>Inline</span>
</bdo> </bdo>
</span>', true, array('Attr.EnableID' => true) </span>'
); );
// min // min

View File

@@ -7,21 +7,23 @@ class HTMLPurifier_HTMLModule_EditTest extends HTMLPurifier_HTMLModuleHarness
function test() { function test() {
$this->setupScaffold('Edit');
// max // max
$this->assertResult( $this->assertResult(
'<span> '<span>
<ins cite="http://www.example.com/"> <ins cite="http://www.example.com/" ac:common="yes">
#PCDATA <span></span> #PCDATA <span></span>
</ins> </ins>
<del cite="http://www.example.com/"> <del cite="http://www.example.com/" ac:common="yes">
#PCDATA <span></span> #PCDATA <span></span>
</del> </del>
</span> </span>
<div> <div>
<ins cite="http://www.example.com/"> <ins cite="http://www.example.com/" ac:common="yes">
#PCDATA <div></div> <span></span> #PCDATA <div></div> <span></span>
</ins> </ins>
<del cite="http://www.example.com/"> <del cite="http://www.example.com/" ac:common="yes">
#PCDATA <div></div> <span></span> #PCDATA <div></div> <span></span>
</del> </del>
</div>' </div>'

View File

@@ -7,6 +7,11 @@ class HTMLPurifier_HTMLModule_HypertextTest extends HTMLPurifier_HTMLModuleHarne
function test() { function test() {
$this->setupScaffold('Hypertext', array(
'Attr.AllowedRel' => 'nofollow',
'Attr.AllowedRev' => 'index'
));
// max // max
$this->assertResult( $this->assertResult(
'<span> '<span>
@@ -14,13 +19,11 @@ class HTMLPurifier_HTMLModule_HypertextTest extends HTMLPurifier_HTMLModuleHarne
href="http://www.example.com/" href="http://www.example.com/"
rel="nofollow" rel="nofollow"
rev="index" rev="index"
ac:common="true"
> >
#PCDATA <span>Inline</span> #PCDATA <span>Inline</span>
</a> </a>
</span>', true, array( </span>', true
'Attr.AllowedRel' => 'nofollow',
'Attr.AllowedRev' => 'index'
)
); );
// invalid children // invalid children

View File

@@ -7,6 +7,8 @@ class HTMLPurifier_HTMLModule_ImageTest extends HTMLPurifier_HTMLModuleHarness
function test() { function test() {
$this->setupScaffold('Image');
// max // max
$this->assertResult( $this->assertResult(
'<span> '<span>
@@ -16,6 +18,7 @@ class HTMLPurifier_HTMLModule_ImageTest extends HTMLPurifier_HTMLModuleHarness
longdesc="example.description.txt" longdesc="example.description.txt"
height="42" height="42"
width="42" width="42"
ac:common="yes"
/> />
</span>' </span>'
); );

View File

@@ -7,12 +7,14 @@ class HTMLPurifier_HTMLModule_LegacyTest extends HTMLPurifier_HTMLModuleHarness
function test() { function test() {
$this->setupScaffold('Legacy');
// max // max
$this->assertResult( $this->assertResult(
'<span> '<span>
<u>Text<span></span></u> <u ac:common="yes">Text<span></span></u>
<s>Text<span></span></s> <s ac:common="yes">Text<span></span></s>
<strike>Text<span></span></strike> <strike ac:common="yes">Text<span></span></strike>
</span>' </span>'
); );

View File

@@ -9,6 +9,27 @@ class HTMLPurifier_HTMLModuleHarness extends HTMLPurifier_StrategyHarness
parent::setup(); parent::setup();
$this->obj = new HTMLPurifier_Strategy_Core(); $this->obj = new HTMLPurifier_Strategy_Core();
} }
function setupScaffold($module, $config = array()) {
$this->config = HTMLPurifier_Config::create($config);
$this->config->set('HTML', 'AllowedModules', $module);
$def =& $this->config->getHTMLDefinition(true);
$def->manager->addModule(new HTMLPurifier_HTMLModuleHarness_Scaffold());
}
}
/**
* Special module that defines scaffolding for easy unit testing
*/
class HTMLPurifier_HTMLModuleHarness_Scaffold extends HTMLPurifier_HTMLModule
{
var $name = 'Scaffold';
var $attr_collections = array(
'Common' => array('ac:common' => 'Text'),
'Core' => array('ac:core' => 'Text')
);
} }
?> ?>

View File

@@ -72,6 +72,34 @@ class HTMLPurifier_HTMLModuleManagerTest extends UnitTestCase
} }
function testAllowedModules() {
$manager = new HTMLPurifier_HTMLModuleManager();
$manager->doctypes->register(
'Fantasy Inventory 1.0', true,
array('Weapons', 'Magic')
);
// register these modules so it doesn't blow up
$weapons_module = new HTMLPurifier_HTMLModule();
$weapons_module->name = 'Weapons';
$manager->registerModule($weapons_module);
$magic_module = new HTMLPurifier_HTMLModule();
$magic_module->name = 'Magic';
$manager->registerModule($magic_module);
$config = HTMLPurifier_Config::create(array(
'HTML.Doctype' => 'Fantasy Inventory 1.0',
'HTML.AllowedModules' => 'Weapons'
));
$manager->setup($config);
$this->assertTrue( isset($manager->modules['Weapons']));
$this->assertFalse(isset($manager->modules['Magic']));
}
} }
?> ?>

View File

@@ -42,6 +42,11 @@ class HTMLPurifier_Harness extends UnitTestCase
*/ */
var $generator; var $generator;
/**
* Default config to fall back on if no config is available
*/
var $config;
function HTMLPurifier_Harness() { function HTMLPurifier_Harness() {
$this->lexer = new HTMLPurifier_Lexer_DirectLex(); $this->lexer = new HTMLPurifier_Lexer_DirectLex();
$this->generator = new HTMLPurifier_Generator(); $this->generator = new HTMLPurifier_Generator();
@@ -52,22 +57,24 @@ class HTMLPurifier_Harness extends UnitTestCase
* Asserts a specific result from a one parameter + config/context function * Asserts a specific result from a one parameter + config/context function
* @param $input Input parameter * @param $input Input parameter
* @param $expect Expectation * @param $expect Expectation
* @param $config_array Configuration array in form of * @param $config Configuration array in form of Ns.Directive => Value.
* Namespace.Directive => Value or an actual config * Has no effect if $this->config is set.
* object.
* @param $context_array Context array in form of Key => Value or an actual * @param $context_array Context array in form of Key => Value or an actual
* context object. * context object.
*/ */
function assertResult($input, $expect = true, function assertResult($input, $expect = true,
$config_array = array(), $context_array = array() $config_array = false, $context_array = array()
) { ) {
// setup config object // setup config
$config = HTMLPurifier_Config::createDefault(); if ($this->config) {
$config->loadArray($config_array); $config = HTMLPurifier_Config::create($this->config);
} else {
$config = HTMLPurifier_Config::create($config_array);
}
// setup context object. Note that we are operating on a copy of it! // setup context object. Note that we are operating on a copy of it!
// We will extend the test harness to allow you to do post-tests // When necessary, extend the test harness to allow post-tests
// on the context object // on the context object
$context = new HTMLPurifier_Context(); $context = new HTMLPurifier_Context();
$context->loadArray($context_array); $context->loadArray($context_array);