LibWeb: Add wpt test for createPolicy and createXXX

This gives us at least 72 new tests :)
This commit is contained in:
Tete17 2025-07-31 18:26:58 +02:00 committed by Luke Wilde
commit 7559084b22
Notes: github-actions[bot] 2025-08-11 11:22:42 +00:00
18 changed files with 636 additions and 0 deletions

View file

@ -0,0 +1,40 @@
Harness status: OK
Found 29 tests
29 Pass
Pass calling undefined callbacks throws
Pass Attributes without type constraints will work as before.
Pass trustedTypes.createPolicy(.., null) creates empty policy.
Pass TestPolicyTrustedHTML0 (TrustedHTML: s => s)
Pass TestPolicyTrustedHTML1 (TrustedHTML: s => null)
Pass TestPolicyTrustedHTML2 (TrustedHTML: s => "well, " + s)
Pass TestPolicyTrustedHTML3 (TrustedHTML: s => { throw new Error() })
Pass TestPolicyTrustedHTML4 (TrustedHTML: callback_to_capture_this(s) {
return String(this);
})
Pass TestPolicyTrustedHTML5 (TrustedHTML: s => { aGlobalVarForSideEffectTesting = s; return s })
Pass TestPolicyTrustedHTML6 (TrustedHTML: s => aGlobalVarForSideEffectTesting + s)
Pass TestPolicyTrustedHTML7 (TrustedHTML: function () { [native code] })
Pass TestPolicyTrustedHTML8 (TrustedHTML: s => aGlobalFunction(s))
Pass TestPolicyTrustedScript0 (TrustedScript: s => s)
Pass TestPolicyTrustedScript1 (TrustedScript: s => null)
Pass TestPolicyTrustedScript2 (TrustedScript: s => "well, " + s)
Pass TestPolicyTrustedScript3 (TrustedScript: s => { throw new Error() })
Pass TestPolicyTrustedScript4 (TrustedScript: callback_to_capture_this(s) {
return String(this);
})
Pass TestPolicyTrustedScript5 (TrustedScript: s => { aGlobalVarForSideEffectTesting = s; return s })
Pass TestPolicyTrustedScript6 (TrustedScript: s => aGlobalVarForSideEffectTesting + s)
Pass TestPolicyTrustedScript7 (TrustedScript: function () { [native code] })
Pass TestPolicyTrustedScript8 (TrustedScript: s => aGlobalFunction(s))
Pass TestPolicyTrustedScriptURL0 (TrustedScriptURL: s => s)
Pass TestPolicyTrustedScriptURL1 (TrustedScriptURL: s => null)
Pass TestPolicyTrustedScriptURL2 (TrustedScriptURL: s => s + "#duck")
Pass TestPolicyTrustedScriptURL3 (TrustedScriptURL: s => { throw new Error() })
Pass TestPolicyTrustedScriptURL4 (TrustedScriptURL: callback_to_capture_this(s) {
return String(this);
})
Pass TestPolicyTrustedScriptURL5 (TrustedScriptURL: s => s + "#" + aGlobalVarForSideEffectTesting)
Pass TestPolicyTrustedScriptURL6 (TrustedScriptURL: function () { [native code] })
Pass TestPolicyTrustedScriptURL7 (TrustedScriptURL: s => anotherGlobalFunction(s))

View file

@ -0,0 +1,33 @@
Harness status: OK
Found 28 tests
28 Pass
Pass html = identity function
Pass html = null
Pass html = string + global string
Pass html = identity function, global string changed
Pass html = callback that throws
Pass html = this bound to an object
Pass html = this without bind
Pass html - calling undefined callback throws
Pass createHTML defined - calling undefined callbacks throws
Pass script = identity function
Pass script = null
Pass script = string + global string
Pass script = identity function, global string changed
Pass script = callback that throws
Pass script = this bound to an object
Pass script = this without bind
Pass script - calling undefined callback throws
Pass createScript defined - calling undefined callbacks throws
Pass script_url = identity function
Pass script_url = null
Pass script_url = string + global string
Pass script_url = identity function, global string changed
Pass script_url = callback that throws
Pass script_url = this bound to an object
Pass script_url = this without bind
Pass script_url - calling undefined callback throws
Pass createScriptURL defined - calling undefined callbacks throws
Pass Arbitrary number of arguments

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass No name list given - policy creation throws

View file

@ -0,0 +1,7 @@
Harness status: OK
Found 2 tests
2 Pass
Pass Can create policy with name 'SomeName'
Pass Cannot create policy with name 'default' - policy creation throws

View file

@ -0,0 +1,7 @@
Harness status: OK
Found 2 tests
2 Pass
Pass Cannot create policy with name 'SomeName' - policy creation throws
Pass Cannot create policy with name 'default' - policy creation throws

View file

@ -0,0 +1,8 @@
Harness status: OK
Found 3 tests
3 Pass
Pass Can create policy with name 'SomeName'
Pass Can create a second policy with name 'SomeName'
Pass Can create policy with name 'default'

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass Wildcard given - policy creation works

View file

@ -0,0 +1,10 @@
Harness status: OK
Found 5 tests
5 Pass
Pass Creating an empty policy name works.
Pass Creating policy names made of a single ASCII character works.
Pass Creating policy names made of multiple ASCII characters works.
Pass Creating policy names containing a non-ASCII BMP character works.
Pass Creating policy names containing a non-BMP character works.

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass Duplicate policy names should be tolerated (unless in enforcing mode)

View file

@ -0,0 +1,118 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<body>
<script>
test(t => {
const p1 = trustedTypes.createPolicy("policyHTMLAndScript", {
createHTML: s => s,
createScript: s => s
});
assert_throws_js(TypeError, _ => { p1.createScriptURL("foo"); });
const p2 = trustedTypes.createPolicy("policyURLAndScriptURL", {
createScriptURL: s => s
});
assert_throws_js(TypeError, _ => { p2.createHTML("foo"); });
assert_throws_js(TypeError, _ => { p2.createScript("foo"); });
}, "calling undefined callbacks throws");
test(t => {
const noopPolicy = {
createHTML: (s) => s,
createScriptURL: (s) => s,
createScript: (s) => s,
};
policy = trustedTypes.createPolicy(Math.random(), noopPolicy);
let el = document.createElement("div");
el.title = policy.createHTML(INPUTS.SCRIPTURL);
assert_equals(el.title, INPUTS.SCRIPTURL);
}, "Attributes without type constraints will work as before.");
test(t => {
const policy = trustedTypes.createPolicy("nullpolicy", null);
assert_throws_js(TypeError, _ => { policy.createScriptURL("foo"); });
assert_throws_js(TypeError, _ => { policy.createHTML("foo"); });
assert_throws_js(TypeError, _ => { policy.createScript("foo"); });
}, "trustedTypes.createPolicy(.., null) creates empty policy.");
// testCases contains a list of policy functions and expected results (when
// called with a given default argument). They also use various helper
// variables (declared just below) to test side effects or interactions of
// global vars/functions with policy callbacks.
let aGlobalVarForSideEffectTesting = "global";
var aGlobalObject = { "foo": "well," };
function aGlobalFunction(s) { return this.foo + " " + s; }
function anotherGlobalFunction(s) { return s + "#" + this.foo; }
var foo = "a global var named foo";
class WrappingClass {
callback_to_capture_this(s) {
return String(this);
}
}
const stringTestCases = [
[ s => s, "whatever" ],
[ s => null, "" ],
[ s => "well, " + s, "well, whatever" ],
[ s => { throw new Error() }, Error ],
[ new WrappingClass().callback_to_capture_this, "undefined"],
[ s => { aGlobalVarForSideEffectTesting = s; return s }, "whatever" ],
[ s => aGlobalVarForSideEffectTesting + s, "whateverwhatever" ],
[ aGlobalFunction.bind(aGlobalObject), "well, whatever" ],
[ s => aGlobalFunction(s), "a global var named foo whatever" ],
];
const urlTestCases = [
[ s => s, INPUTS.SCRIPTURL ],
[ s => null, "" ],
[ s => s + "#duck", INPUTS.SCRIPTURL + "#duck" ],
[ s => { throw new Error() }, Error ],
[ new WrappingClass().callback_to_capture_this, "undefined"],
[ s => s + "#" + aGlobalVarForSideEffectTesting,
INPUTS.SCRIPTURL + "#global" ],
[ anotherGlobalFunction.bind(aGlobalObject), INPUTS.SCRIPTURL + "#well," ],
[ s => anotherGlobalFunction(s),
INPUTS.SCRIPTURL + "#a global var named foo" ],
];
function policyBuilder(trustedMethodName, trustedType, defaultArg) {
return function(name, fn) {
let options = {};
options[trustedMethodName] = fn;
let policy = window.trustedTypes.createPolicy(name, options);
let result = policy[trustedMethodName](defaultArg);
assert_true(result instanceof trustedType);
return result;
}
}
const testCases = [
[TrustedHTML, "createHTML", "whatever", stringTestCases],
[TrustedScript, "createScript", "whatever", stringTestCases],
[TrustedScriptURL, "createScriptURL", INPUTS.SCRIPTURL, urlTestCases],
];
// Iterate over all trusted types, iterate over all test cases.
// For each, build the suitable policy and check the result.
for (let [trusted_class, trusted_method, default_arg, test_cases] of testCases) {
aGlobalVarForSideEffectTesting = "global";
let builder = policyBuilder(trusted_method, trusted_class, default_arg);
for (let [index, [policy_fn, value]] of test_cases.entries()) {
let subtest_name = "TestPolicy" + trusted_class.name + index;
test(t => {
if (typeof value == "object" || typeof value == "function") {
assert_throws_js(value, () => builder(subtest_name, policy_fn));
} else {
assert_equals("" + builder(subtest_name, policy_fn), value);
}
}, subtest_name + " (" + trusted_class.name + ": " +
policy_fn.toString() + ")");
}
}
</script>

View file

@ -0,0 +1,232 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<body>
<script>
//HTML tests
function createHTMLTest(policyName, policy, expectedHTML, t) {
let p = window.trustedTypes.createPolicy(policyName, policy);
let html = p.createHTML('whatever');
assert_true(html instanceof TrustedHTML);
assert_true(trustedTypes.isHTML(html));
assert_equals(html + "", expectedHTML);
}
test(t => {
createHTMLTest('TestPolicyHTML1', { createHTML: s => s }, 'whatever', t);
}, "html = identity function");
test(t => {
createHTMLTest('TestPolicyHTML2', { createHTML: s => null }, "", t);
}, "html = null");
var HTMLstr = 'well, ';
test(t => {
createHTMLTest('TestPolicyHTML3', { createHTML: s => HTMLstr + s }, HTMLstr + 'whatever', t);
}, "html = string + global string");
var HTMLx = 'global';
test(t => {
createHTMLTest('TestPolicyHTML4', { createHTML: s => { HTMLx = s; return s; } }, 'whatever', t);
assert_equals(HTMLx, 'whatever');
}, "html = identity function, global string changed");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyHTML5', { createHTML: s => { throw new Error(); }});
assert_throws_js(Error, _ => {
p.createHTML('whatever');
});
}, "html = callback that throws");
var obj = {
"foo": "well,"
}
function getHTML(s) {
return this.foo + " " + s;
}
test(t => {
createHTMLTest('TestPolicyHTML6', { createHTML: getHTML.bind(obj) }, 'well, whatever', t);
}, "html = this bound to an object");
var foo = "well,";
test(t => {
createHTMLTest('TestPolicyHTML7', { createHTML: s => getHTML(s) }, 'well, whatever', t);
}, "html = this without bind");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyHTML8', null);
assert_throws_js(TypeError, _ => {
p.createHTML('whatever');
});
}, "html - calling undefined callback throws");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyHTML9', { createHTML: createHTMLJS });
assert_throws_js(TypeError, _ => {
p.createScript(INPUTS.SCRIPT);
});
assert_throws_js(TypeError, _ => {
p.createScriptURL(INPUTS.SCRIPTURL);
});
}, "createHTML defined - calling undefined callbacks throws");
//Script tests
function createScriptTest(policyName, policy, expectedScript, t) {
let p = window.trustedTypes.createPolicy(policyName, policy);
let script = p.createScript('whatever');
assert_true(script instanceof TrustedScript);
assert_true(trustedTypes.isScript(script));
assert_equals(script + "", expectedScript);
}
test(t => {
createScriptTest('TestPolicyScript1', { createScript: s => s }, 'whatever', t);
}, "script = identity function");
test(t => {
createScriptTest('TestPolicyScript2', { createScript: s => null }, "", t);
}, "script = null");
var Scriptstr = 'well, ';
test(t => {
createScriptTest('TestPolicyScript3', { createScript: s => Scriptstr + s }, Scriptstr + 'whatever', t);
}, "script = string + global string");
var Scriptx = 'global';
test(t => {
createScriptTest('TestPolicyScript4', { createScript: s => { Scriptx = s; return s; } }, 'whatever', t);
assert_equals(Scriptx, 'whatever');
}, "script = identity function, global string changed");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyScript5', {
createScript: s => { throw new Error(); }
});
assert_throws_js(Error, _ => {
p.createScript('whatever');
});
}, "script = callback that throws");
var obj = {
"foo": "well,"
}
function getScript(s) {
return this.foo + " " + s;
}
test(t => {
createScriptTest('TestPolicyScript6', { createScript: getScript.bind(obj) }, 'well, whatever', t);
}, "script = this bound to an object");
var foo = "well,";
test(t => {
createScriptTest('TestPolicyScript7', { createScript: s => getScript(s) }, 'well, whatever', t);
}, "script = this without bind");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyScript8', null);
assert_throws_js(TypeError, _ => {
p.createScript('whatever');
});
}, "script - calling undefined callback throws");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyScript9', { createScript: createScriptJS });
assert_throws_js(TypeError, _ => {
p.createHTML(INPUTS.HTML);
});
assert_throws_js(TypeError, _ => {
p.createScriptURL(INPUTS.SCRIPTURL);
});
}, "createScript defined - calling undefined callbacks throws");
//ScriptURL tests
function createScriptURLTest(policyName, policy, expectedScriptURL, t) {
let p = window.trustedTypes.createPolicy(policyName, policy);
let scriptUrl = p.createScriptURL(INPUTS.SCRIPTURL);
assert_true(scriptUrl instanceof TrustedScriptURL);
assert_true(trustedTypes.isScriptURL(scriptUrl));
assert_equals(scriptUrl + "", expectedScriptURL);
}
test(t => {
createScriptURLTest('TestPolicyScriptURL1', { createScriptURL: s => s }, INPUTS.SCRIPTURL, t);
}, "script_url = identity function");
test(t => {
createScriptURLTest('TestPolicyScriptURL2', { createScriptURL: s => null }, "", t);
}, "script_url = null");
var scriptURLstr = '#duck';
test(t => {
createScriptURLTest('TestPolicyScriptURL3', { createScriptURL: s => s + scriptURLstr }, INPUTS.SCRIPTURL + scriptURLstr, t);
}, "script_url = string + global string");
var scriptURLx = 'global';
test(t => {
createScriptURLTest('TestPolicyScriptURL4', { createScriptURL: s => { ScriptURLx = s; return s; } }, INPUTS.SCRIPTURL, t);
assert_equals(ScriptURLx, INPUTS.SCRIPTURL);
}, "script_url = identity function, global string changed");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyScriptURL5', {
createScriptURL: s => { throw new Error(); }
});
assert_throws_js(Error, _ => {
p.createScriptURL(INPUTS.SCRIPTURL);
});
}, "script_url = callback that throws");
function getScriptURL(s) {
return s + this.baz;
}
var obj = {
"baz": "#duck"
}
test(t => {
createScriptURLTest('TestPolicyScriptURL6', { createScriptURL: getScriptURL.bind(obj) }, INPUTS.SCRIPTURL + "#duck", t);
}, "script_url = this bound to an object");
var baz = "#duck";
test(t => {
createScriptURLTest('TestPolicyScriptURL7', { createScriptURL: s => getScriptURL(s) }, INPUTS.SCRIPTURL + baz, t);
}, "script_url = this without bind");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyScriptURL8', null);
assert_throws_js(TypeError, _ => {
p.createScriptURL(INPUTS.SCRIPTURL);
});
}, "script_url - calling undefined callback throws");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyScriptURL9', { createScriptURL: createScriptURLJS });
assert_throws_js(TypeError, _ => {
p.createHTML(INPUTS.HTML);
});
assert_throws_js(TypeError, _ => {
p.createScript(INPUTS.SCRIPT);
});
}, "createScriptURL defined - calling undefined callbacks throws");
test(t => {
let p = window.trustedTypes.createPolicy('TestPolicyScriptURL10', {
createHTML: createHTMLJSWithThreeArguments,
createScript: createScriptJSWithThreeArguments,
createScriptURL: createScriptURLJSWithThreeArguments
});
assert_equals("abc", p.createHTML("a", "b", "c").toString());
assert_equals("abc", p.createScript("a", "b", "c").toString());
assert_equals("abc", p.createScriptURL("a", "b", "c").toString());
assert_equals("abundefined", p.createHTML("a", "b").toString());
assert_equals("a123null", p.createScript("a", 123, null).toString());
assert_equals("a[object Object]3.14", p.createScriptURL("a", {}, 3.14).toString());
}, "Arbitrary number of arguments");
</script>

View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<meta http-equiv="Content-Security-Policy" content="trusted-types">
<body>
<script>
//No name given test
test(t => {
assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } );
});
}, "No name list given - policy creation throws");
</script>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<meta http-equiv="Content-Security-Policy" content="trusted-types 'none' 'none' SomeName">
<body>
<script>
test(t => {
window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } );
}, "Can create policy with name 'SomeName'");
test(t => {
assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('default', { createHTML: s => s } );
});
}, "Cannot create policy with name 'default' - policy creation throws");
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<!-- This should behave the same as one `none` case and the case when no policy specified. -->
<meta http-equiv="Content-Security-Policy" content="trusted-types 'none' 'none'">
<body>
<script>
test(t => {
assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } );
});
}, "Cannot create policy with name 'SomeName' - policy creation throws");
test(t => {
assert_throws_js(TypeError, _ => {
window.trustedTypes.createPolicy('default', { createHTML: s => s } );
});
}, "Cannot create policy with name 'default' - policy creation throws");
</script>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<meta http-equiv="Content-Security-Policy" content="trusted-types * 'none' 'allow-duplicates'">
<body>
<script>
test(t => {
window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } );
}, "Can create policy with name 'SomeName'");
test(t => {
window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } );
}, "Can create a second policy with name 'SomeName'");
test(t => {
window.trustedTypes.createPolicy('default', { createHTML: s => s } );
}, "Can create policy with name 'default'");
</script>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<script src="support/helper.sub.js"></script>
<body>
<script>
//No name given test
test(t => {
let policy = window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } );
assert_equals(policy.name, 'SomeName');
}, "Wildcard given - policy creation works");
</script>

View file

@ -0,0 +1,66 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<meta charset="UTF-8">
<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-createpolicy">
<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/#tt-policy-name">
<link rel="help" href="https://github.com/w3c/trusted-types/issues/466">
<link rel="help" href="https://github.com/w3c/trusted-types/issues/504">
<body>
<script>
function is_tt_policy_name(aName) {
// tt-policy-name = 1*( ALPHA / DIGIT / "-" / "#" / "=" / "_" / "/" / "@" / "." / "%")
return /^([A-Za-z0-9\-\#\=\_\/\@$\.\%]+)$/.test(aName);
}
function tryCreatePolicy(aName) {
return window.trustedTypes.createPolicy(aName, { createHTML: s => s } );
}
test(() => {
assert_false(is_tt_policy_name(""));
assert_equals(tryCreatePolicy("").name, "");
}, "Creating an empty policy name works.");
test(() => {
for (let codePoint = 0; codePoint < 0x80; codePoint++) {
let policyName = String.fromCharCode(codePoint);
assert_equals(tryCreatePolicy(policyName).name, policyName);
}
}, "Creating policy names made of a single ASCII character works.");
test(() => {
for (let codePoint = 0; codePoint < 0x80; codePoint++) {
let policyName = `policy${String.fromCharCode(codePoint)}name`;
assert_equals(tryCreatePolicy(policyName).name, policyName);
}
}, "Creating policy names made of multiple ASCII characters works.");
test(() => {
// C1 Controls and Latin-1 Supplement
for (let codePoint = 0x80; codePoint <= 0xFF; codePoint++) {
let policyName = `policy${String.fromCharCode(codePoint)}name`;
assert_false(is_tt_policy_name(policyName));
assert_equals(tryCreatePolicy(policyName).name, policyName);
}
// Try U+1000, U+2000, U+3000, ..., U+F000.
for (let i = 0x1; i <= 0xF; i++) {
let codePoint = i * 0x1000;
let policyName = `policy${String.fromCharCode(codePoint)}name`;
assert_false(is_tt_policy_name(policyName));
assert_equals(tryCreatePolicy(policyName).name, policyName);
}
}, "Creating policy names containing a non-ASCII BMP character works.");
test(() => {
// Try U+1000, U+2000, U+3000, ..., U+1F000.
for (let i = 0x10; i <= 0x1F; i++) {
let codePoint = i * 0x1000;
let policyName = `policy${String.fromCharCode(codePoint)}name`;
assert_false(is_tt_policy_name(policyName));
assert_equals(tryCreatePolicy(policyName).name, policyName);
}
}, "Creating policy names containing a non-BMP character works.");
</script>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<script src="../resources/testharness.js" ></script>
<script src="../resources/testharnessreport.js"></script>
<body>
<script>
// This test tests error handling of duplicate policy creation when no
// Trusted Types enforcement is active.
test(t => {
trustedTypes.createPolicy('foo', {} );
trustedTypes.createPolicy('foo', {} );
}, "Duplicate policy names should be tolerated (unless in enforcing mode)");
</script>
</body>